总结一下之前写的一个python聊天程序

本文从WordPress迁移而来, 查看全部WordPress迁移文章

一直没空,今天翻出来,总结一下,虽然都是比较基础的东西,但还是记录一下吧….

一:程序的功能

1.服务器与客户端通信,客户端与客户端通信
2.服务器和客户端都可以显示在线的客户,客户上线,下线,可以实时刷新
3.基于第2点,在通信的时候,无论是服务器还是客户端,都可以选择和一人或多人通信

二:开发环境

1.环境:win7,python2.7,PyQt4
2.使用python的socket模块,threading模块
3.PyQt4开发图形界面(用designer布置控件,不是手写)

三:设计思路

  • 服务器

    1. 主线程负责图形界面等
    2. 创建一个线程负责监听客户的连接请求
    3. 没当有客户端成功连接,就又创建一个线程,专门负责和该用户通信
  • 客户端

    1. 主线程负责图形界面等
    2. 创建一个线程负责连接服务器,当连接失败或中断后,可以自动重复连接

四:使用的QT控件

QWidget
QGroupBox
QTextBrowser
QTextEdit
QListWidget
QPushButton

五:发送,接收信息自定义的协议

  • 服务器发送信息:
    1. 服务器发给客户端:FS>信息内容
    2. 服务器接收A客户端信息,发给B客户端:FA客户端地址>信息内容
  • 服务器接收信息:
    1. 客户端发给服务器的信息:TS>信息内容
    2. A客户端发给B客户端的信息(经服务器中转):TB客服端地址>信息内容
  • 客户端发送信息:
    1. 发送给服务器: TS>信息内容
    2. 发送给客户端: T对方地址>信息内容
  • 客户端接收信息:(其实都是服务器发过来的,一种是服务器直接发送,一种是服务器转发其他客户端的信息)
    1. 接收来自服务器的信息:FS>信息内容
    2. 接收客户端的信息:F对方地址>信息内容

六:问题

  1. 处理异常的方面做得不好
  2. 多线程方面考虑的东西太少
  3. find那个功能没有做,懒得做了。。。。以后把这个程序写得更丰富的时候再做了
  • 服务器程序MyServer.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
# -*- coding:utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from serverUI import Ui_mainWidget
from random import *
import sys
import socket
import threading
import time

class MyServer:

#构造函数
def __init__(self):
self.createSocket()
self.createGui()
self.createSignalSlot()

#创建socket
def createSocket(self):
self.bufsize = 1024
self.host = socket.gethostname()
self.ip = socket.gethostbyname(self.host)
self.port = 34567
self.mySocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.clientSocket = []

#布置图形界面
def createGui(self):
self.mainWidget = QWidget()
self.myUi = Ui_mainWidget()
self.myUi.setupUi(self.mainWidget)
self.mainWidget.setWindowTitle( self.PYSTOQTS('Server | '+self.ip+':'+str(self.port)) )
self.myUi.userLW.setSelectionMode(QAbstractItemView.ExtendedSelection) # ctrl + 单击 = 多选
self.myUi.userLW.setEditTriggers(QAbstractItemView.NoEditTriggers) # 双击的时候不可编辑(不过QListWiidget默认是只读)

#编辑空间的信号和槽
def createSignalSlot(self):
self.myUi.sendPB.clicked.connect(self.serverToClient)
self.myUi.quitPB.clicked.connect(self.quitApp)
self.myUi.inputTE.textChanged.connect(self.activeSendPB)
# selectAllPB,resetPB 的信号和槽已经在 Ui_mainWidget.setupUi 中定义

#启动一个线程去连接服务器
def start(self):
self.myListenThread = threading.Thread(target=self.listenFunc,args=(),name='myListenThread')
self.myListenThread.setDaemon(True)
self.myListenThread.start()

#show出图形界面
def show(self):
self.mainWidget.show()

#关闭程序
def quitApp(self):
self.mySocket.close() #关闭监听socket,不使用shutdown否则报错,因为监听socket其实并没有和其他socket连接
for i in range(len(self.clientSocket)):
self.clientSocket[i][1].shutdown(socket.SHUT_RDWR)
self.clientSocket[i][1].close()
self.mainWidget.close()

#转化一个地址格式,默认格式是(ip,port),转化为 ip:port
def changeClientaddress(self,clientaddress):
return str(clientaddress[0])+':'+str(clientaddress[1])

#显示当前时间,用time.ctime()的话,格式比较恶心,所以自己定义个格式
def myTime(self):
return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())

#将 python字符串 转化为 QT字符串
def PYSTOQTS(self,pystring):
return QString(unicode(pystring,'gb2312','ignore'))

#将 QT字符串 转化为 python字符串
def QTSTOPYS(self,qtstring):
return unicode(qtstring.toUtf8(),'utf8','ignore').encode('gb2312')

#线程函数,用于监听所有客户的连接
def listenFunc(self):
# bind listen accept 三部曲
self.mySocket.bind((self.ip,self.port))
self.mySocket.listen(10)
acceptERROR = False
while True:
self.myUi.statusTB.append( self.PYSTOQTS('waiting for connection ...... \n') )
try:
connection , clientaddress = self.mySocket.accept()
clientaddress = self.changeClientaddress(clientaddress) #将元组变为字符串,格式为 ip:port
except:
self.myUi.statusTB.append( self.PYSTOQTS('mySocket accept ERROR\n') )
acceptERROR = True
break
if acceptERROR == True:
break
newthread = threading.Thread(target=self.recvFromClient,args=(connection,clientaddress)) # 创建线程接收该用户发送的信息
newthread.setDaemon(True) #设置为后台线程
newthread.start()
tempThread = threading.Thread(target=self.addUser,args=(connection,clientaddress)) #添加一个新登录的user
tempThread.setDaemon(True) #设置为后台线程
tempThread.start()
self.mySocket.close()

#线程函数,在listenFunc中,有客户成功连接,则创建一个线程专门服务它
def recvFromClient(self,con,caddr):
while True:
try:
data = con.recv(self.bufsize)
if not data:
self.myUi.statusTB.append( self.PYSTOQTS(self.myTime()+'\n'+caddr+' logout'+'\n') )
break
if data[1] == 'S':
self.myUi.messageTB.append( self.PYSTOQTS(self.myTime()+'\n'+caddr+' to Server : '+data[3:]+'\n') )
else:
self.clientToClient(caddr,data[1:data.find('>')],data[data.find('>')+1:])
except:
self.myUi.statusTB.append( self.PYSTOQTS('lose the connection of '+caddr+'\n') )
break
con.shutdown(socket.SHUT_RDWR)
con.close()
self.delUser(caddr) #删除一个user


#发送信息函数,服务器发送给客户端
def serverToClient(self):
qtData = self.myUi.inputTE.toPlainText()
pyData = self.QTSTOPYS(qtData)
self.myUi.inputTE.clear()
itemlist = self.myUi.userLW.selectedItems()
for i in range(len(itemlist)):
caddr = self.QTSTOPYS(itemlist[i].text())
for k in range(len(self.clientSocket)):
if self.clientSocket[k][0] == caddr:
self.clientSocket[k][1].send('FS>'+pyData)
self.myUi.messageTB.append( self.PYSTOQTS('server to '+caddr+' : '+pyData+'\n') )
break

#发送信息函数,客户端发给客户端(实际上是服务器在中间起转发作用)
def clientToClient(self,caddrx,caddry,data):
con = None
for i in range(len(self.clientSocket)):
if self.clientSocket[i][0] == caddry:
con = self.clientSocket[i][1]
break
con.send('F'+caddrx+'>'+data)
self.myUi.messageTB.append( self.PYSTOQTS(self.myTime()+'\n'+caddrx+' to '+caddry+' : '+data+'\n') )


#当有一个用户上线的时候,添加一个新用户
def addUser(self,con,caddr):
self.myUi.userLW.addItem(caddr) #更新 QListWidget
self.notifyClientConnected(caddr) #通知其他user该user上线
for i in range(len(self.clientSocket)): #将所有已在线的user通知该user
con.send('ADD'+self.clientSocket[i][0])
self.clientSocket.append([caddr,con]) #更新clientSocket列表

#当有用户下线的时候,删除一个用户
def delUser(self,caddr):
for i in range(self.myUi.userLW.count()): #更新QListWidget
if self.myUi.userLW.item(i).text() == caddr:
self.myUi.userLW.takeItem(i)
break
for i in range(len(self.clientSocket)): #更新clientSocket
if caddr == self.clientSocket[i][0]:
self.clientSocket.pop(i)
break
self.notifyClientExited(caddr)

#通知其余所有用户,有某用户已经上线
def notifyClientConnected(self,caddr):
for i in range(len(self.clientSocket)):
self.clientSocket[i][1].send('ADD'+caddr)

#通知其余所有用户,有某用户已经下线
def notifyClientExited(self,caddr):
caddr = str(caddr)
for i in range(len(self.clientSocket)):
self.clientSocket[i][1].send('DEL'+caddr)

#一个小小的槽函数,用于激活send按钮
def activeSendPB(self):
if self.myUi.inputTE.toPlainText().isEmpty() == True:
self.myUi.sendPB.setEnabled(False)
else:
self.myUi.sendPB.setEnabled(True)


def main():
qtapp = QApplication(sys.argv)
myServer = MyServer()
myServer.start()
myServer.show()
sys.exit(qtapp.exec_())

if __name__ == '__main__':
main()
  • 客户端程序MyClient.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
# -*- coding:utf-8 -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
from clientUI import Ui_mainWidget
import socket
import threading
import sys
import time

class MyClient:

#构造函数
def __init__(self):
self.createSocket()
self.createGui()
self.createSignalSlot()

#创建socket
def createSocket(self):
self.bufsize = 1024
self.host = self.serverHost = socket.gethostname() # 本机的主机名
self.serverIP = socket.gethostbyname(self.serverHost) #因为在本地测试,所以服务器主机名也是本机主机名,否则应该是服务器的主机名
self.serverPort = 34567
self.ip = self.port = -1

#布置GUI
def createGui(self):
self.mainWidget = QWidget()
self.myUi = Ui_mainWidget()
self.myUi.setupUi(self.mainWidget)
self.myUi.userLW.setSelectionMode(QAbstractItemView.ExtendedSelection) # ctrl + 单击 = 多选
self.myUi.userLW.setEditTriggers(QAbstractItemView.NoEditTriggers) # 双击的时候不可编辑(不过QListWiidget默认是只读)

#编辑信号和槽
def createSignalSlot(self):
self.myUi.sendPB.clicked.connect(self.sendFunc)
self.myUi.quitPB.clicked.connect(self.quitApp)
self.myUi.inputTE.textChanged.connect(self.activeSendPB)
# selectAllPB,resetPB 的信号和槽已经在 Ui_mainWidget.setupUi 中定义

#启动连接服务器的线程
def start(self):
self.myConnectThread = threading.Thread(target=self.connectFunc,args=(),name='myConnectThread')
self.myConnectThread.setDaemon(True)
self.myConnectThread.start()

#show出图形界面
def show(self):
self.mainWidget.show()

#关闭程序
def quitApp(self):
try: # 如果socket还在成功connect之前,调用shutdown是错误的,所以要try
self.mySocket.shutdown(socket.SHUT_RDWR)
except:
pass
self.mySocket.close() #关闭监听socket
self.mainWidget.close()

# 将 python字符串 转化为 QT字符串
def PYSTOQTS(self,pystring):
return QString(unicode(pystring,'gb2312','ignore'))

# 将 QT字符串 转化为 python字符串
def QTSTOPYS(self,qtstring):
return unicode(qtstring.toUtf8(),'utf8','ignore').encode('gb2312')

#转化时间格式
def myTime(self):
return time.strftime("%Y-%m-%d %H:%M:%S",time.localtime())

#线程函数,用于连接服务器,成功连接后,开始接收服务器的信息
def connectFunc(self):
while True:
self.mySocket = socket.socket(socket.AF_INET,socket.SOCK_STREAM)
self.myUi.statusTB.append( self.PYSTOQTS('try to connect ... \n') )
while True: #连接失败时自动重连,中断后自动重连
try:
self.mySocket.connect((self.serverIP,self.serverPort))
(self.ip,self.port) = self.mySocket.getsockname() # 得到自己的ip,port,要在成功connect后才能调用 getsockname(),以元组形式返回
break
except:
pass

self.updataGui()

while True:
data = self.mySocket.recv(self.bufsize)
if not data:
self.myUi.statusTB.append( self.PYSTOQTS(self.myTime()+'\n'+'server close socket\n') )
break
if data[:3] == 'ADD': #新用户上线
self.addUser(data[3:])
elif data[:3] == 'DEL': #用户下线
self.delUser(data[3:])
elif data[1] == 'S': #服务器发给客户端的信息
self.recvFromServer(data[3:])
else: #客户端发给客户端的信息(当然,是经服务器转发过来的)
self.recvFromClient(data[1:data.find('>')] , data[data.find('>')+1:])


self.mySocket.shutdown(socket.SHUT_RDWR)
self.mySocket.close()

#接收服务器发来的信息
def recvFromServer(self,pyData):
self.myUi.messageTB.append( self.PYSTOQTS('From Server : '+pyData+'\n') )

#接收客户端发来的信息(经过服务器转发的)
def recvFromClient(self,caddr,pyData):
self.myUi.messageTB.append( self.PYSTOQTS('From '+caddr+' : '+pyData+'\n') )

#发送信息的函数,
def sendFunc(self):
qtData = self.myUi.inputTE.toPlainText()
pyData = self.QTSTOPYS(qtData)
self.myUi.inputTE.clear()
templist = self.myUi.userLW.selectedItems()
for i in range(len(templist)):
caddr = self.QTSTOPYS(templist[i].text())
if caddr == 'Server':
self.toServer(pyData)
else:
self.toClient(pyData,caddr)

#由sendFunc调用,细分为 toServer,toClient
def toServer(self,pyData):
self.mySocket.send('TS>'+pyData)
self.myUi.messageTB.append( self.PYSTOQTS('To Server : '+pyData+'\n') )

#由sendFunc调用,细分为 toServer,toClient
def toClient(self,pyData,caddr):
self.mySocket.send('T'+caddr+'>'+pyData)
self.myUi.messageTB.append( self.PYSTOQTS('To '+caddr+' : '+pyData+'\n') )


#新用户上线
def addUser(self,caddr):
self.myUi.userLW.addItem( self.PYSTOQTS(caddr) )
self.myUi.statusTB.append( self.PYSTOQTS(self.myTime()+'\n'+caddr+' login'+'\n') )

#用户下线
def delUser(self,caddr):
for i in range(self.myUi.userLW.count()):
if self.myUi.userLW.item(i).text() == self.PYSTOQTS(caddr):
self.myUi.userLW.takeItem(i)
break
self.myUi.statusTB.append( self.PYSTOQTS(self.myTime()+'\n'+caddr+' logout'+'\n') )

#成功连接服务器的时候,更新一下界面,在窗体标题显示ip,port等
def updataGui(self):
self.mainWidget.setWindowTitle( self.PYSTOQTS('Client | '+self.ip+':'+str(self.port)) )
self.myUi.statusTB.append( self.PYSTOQTS(self.myTime()+'\n'+'connected\n') ) #更新statusTB
self.myUi.userLW.addItem( self.PYSTOQTS('Server') ) #更新userLW

#槽函数,激活send按钮
def activeSendPB(self):
if self.myUi.inputTE.toPlainText().isEmpty() == True and self.ip != -1 and self.port != -1:
self.myUi.sendPB.setEnabled(False)
else:
self.myUi.sendPB.setEnabled(True)


def main():
qtApp = QApplication(sys.argv)
myClient = MyClient()
myClient.start()
myClient.show()
sys.exit(qtApp.exec_())

if __name__ == '__main__':
main()

由designer布置控件后,用PyQt4的命令自动生成的代码(其实使用的是同一个界面,只是做了稍微的修改….)

  • 服务器界面serverUI.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'UI.ui'
#
# Created: Mon Mar 31 16:12:37 2014
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s

try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)

class Ui_mainWidget(object):
def setupUi(self, mainWidget):
mainWidget.setObjectName(_fromUtf8("mainWidget"))
mainWidget.resize(575, 509)
mainWidget.setMinimumSize(QtCore.QSize(575, 509))
mainWidget.setMaximumSize(QtCore.QSize(575, 509))
mainWidget.setStyleSheet(_fromUtf8(""))
self.layoutWidget = QtGui.QWidget(mainWidget)
self.layoutWidget.setGeometry(QtCore.QRect(10, 10, 558, 489))
self.layoutWidget.setObjectName(_fromUtf8("layoutWidget"))
self.horizontalLayout_6 = QtGui.QHBoxLayout(self.layoutWidget)
self.horizontalLayout_6.setMargin(0)
self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6"))
self.verticalLayout_4 = QtGui.QVBoxLayout()
self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4"))
self.messageGB = QtGui.QGroupBox(self.layoutWidget)
self.messageGB.setMinimumSize(QtCore.QSize(372, 224))
self.messageGB.setObjectName(_fromUtf8("messageGB"))
self.horizontalLayout = QtGui.QHBoxLayout(self.messageGB)
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.messageTB = QtGui.QTextBrowser(self.messageGB)
self.messageTB.setObjectName(_fromUtf8("messageTB"))
self.horizontalLayout.addWidget(self.messageTB)
self.verticalLayout_4.addWidget(self.messageGB)
self.inputGB = QtGui.QGroupBox(self.layoutWidget)
self.inputGB.setObjectName(_fromUtf8("inputGB"))
self.verticalLayout = QtGui.QVBoxLayout(self.inputGB)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.inputTE = QtGui.QTextEdit(self.inputGB)
self.inputTE.setObjectName(_fromUtf8("inputTE"))
self.verticalLayout.addWidget(self.inputTE)
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
spacerItem = QtGui.QSpacerItem(188, 17, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.quitPB = QtGui.QPushButton(self.inputGB)
self.quitPB.setObjectName(_fromUtf8("quitPB"))
self.horizontalLayout_2.addWidget(self.quitPB)
self.sendPB = QtGui.QPushButton(self.inputGB)
self.sendPB.setEnabled(False)
self.sendPB.setObjectName(_fromUtf8("sendPB"))
self.horizontalLayout_2.addWidget(self.sendPB)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.verticalLayout_4.addWidget(self.inputGB)
self.horizontalLayout_6.addLayout(self.verticalLayout_4)
self.verticalLayout_3 = QtGui.QVBoxLayout()
self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
self.statusGB = QtGui.QGroupBox(self.layoutWidget)
self.statusGB.setMaximumSize(QtCore.QSize(171, 151))
self.statusGB.setObjectName(_fromUtf8("statusGB"))
self.horizontalLayout_5 = QtGui.QHBoxLayout(self.statusGB)
self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5"))
self.statusTB = QtGui.QTextBrowser(self.statusGB)
self.statusTB.setMaximumSize(QtCore.QSize(161, 119))
self.statusTB.setObjectName(_fromUtf8("statusTB"))
self.horizontalLayout_5.addWidget(self.statusTB)
self.verticalLayout_3.addWidget(self.statusGB)
self.userGB = QtGui.QGroupBox(self.layoutWidget)
self.userGB.setObjectName(_fromUtf8("userGB"))
self.verticalLayout_2 = QtGui.QVBoxLayout(self.userGB)
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
self.horizontalLayout_3 = QtGui.QHBoxLayout()
self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3"))
self.userLE = QtGui.QLineEdit(self.userGB)
self.userLE.setMaximumSize(QtCore.QSize(91, 20))
self.userLE.setObjectName(_fromUtf8("userLE"))
self.horizontalLayout_3.addWidget(self.userLE)
self.findPB = QtGui.QPushButton(self.userGB)
self.findPB.setEnabled(False)
self.findPB.setMaximumSize(QtCore.QSize(51, 23))
self.findPB.setObjectName(_fromUtf8("findPB"))
self.horizontalLayout_3.addWidget(self.findPB)
self.verticalLayout_2.addLayout(self.horizontalLayout_3)
self.userLW = QtGui.QListWidget(self.userGB)
self.userLW.setMinimumSize(QtCore.QSize(0, 161))
self.userLW.setMaximumSize(QtCore.QSize(149, 16777215))
self.userLW.setObjectName(_fromUtf8("userLW"))
self.verticalLayout_2.addWidget(self.userLW)
self.horizontalLayout_4 = QtGui.QHBoxLayout()
self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4"))
self.selectallPB = QtGui.QPushButton(self.userGB)
self.selectallPB.setMaximumSize(QtCore.QSize(75, 23))
self.selectallPB.setObjectName(_fromUtf8("selectallPB"))
self.horizontalLayout_4.addWidget(self.selectallPB)
self.resetPB = QtGui.QPushButton(self.userGB)
self.resetPB.setMaximumSize(QtCore.QSize(71, 23))
self.resetPB.setObjectName(_fromUtf8("resetPB"))
self.horizontalLayout_4.addWidget(self.resetPB)
self.verticalLayout_2.addLayout(self.horizontalLayout_4)
self.verticalLayout_3.addWidget(self.userGB)
self.horizontalLayout_6.addLayout(self.verticalLayout_3)

self.retranslateUi(mainWidget)
QtCore.QObject.connect(self.selectallPB, QtCore.SIGNAL(_fromUtf8("clicked()")), self.userLW.selectAll)
QtCore.QObject.connect(self.resetPB, QtCore.SIGNAL(_fromUtf8("clicked()")), self.userLW.reset)
QtCore.QMetaObject.connectSlotsByName(mainWidget)
mainWidget.setTabOrder(self.inputTE, self.sendPB)
mainWidget.setTabOrder(self.sendPB, self.messageTB)
mainWidget.setTabOrder(self.messageTB, self.userLE)
mainWidget.setTabOrder(self.userLE, self.findPB)
mainWidget.setTabOrder(self.findPB, self.userLW)
mainWidget.setTabOrder(self.userLW, self.selectallPB)
mainWidget.setTabOrder(self.selectallPB, self.resetPB)
mainWidget.setTabOrder(self.resetPB, self.statusTB)
mainWidget.setTabOrder(self.statusTB, self.quitPB)

def retranslateUi(self, mainWidget):
mainWidget.setWindowTitle(_translate("mainWidget", "Server", None))
self.messageGB.setTitle(_translate("mainWidget", "Message", None))
self.inputGB.setTitle(_translate("mainWidget", "Input", None))
self.quitPB.setText(_translate("mainWidget", "Quit", None))
self.sendPB.setText(_translate("mainWidget", "Send", None))
self.statusGB.setTitle(_translate("mainWidget", "Status", None))
self.statusTB.setHtml(_translate("mainWidget", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>", None))
self.userGB.setTitle(_translate("mainWidget", "User", None))
self.findPB.setText(_translate("mainWidget", "Find", None))
self.selectallPB.setText(_translate("mainWidget", "Select All", None))
self.resetPB.setText(_translate("mainWidget", "Reset", None))


if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
mainWidget = QtGui.QWidget()
ui = Ui_mainWidget()
ui.setupUi(mainWidget)
mainWidget.show()
sys.exit(app.exec_())
  • 客户端界面clientUI.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# -*- coding: utf-8 -*-

# Form implementation generated from reading ui file 'UI.ui'
#
# Created: Mon Mar 31 16:12:37 2014
# by: PyQt4 UI code generator 4.10.3
#
# WARNING! All changes made in this file will be lost!

from PyQt4 import QtCore, QtGui

try:
_fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
def _fromUtf8(s):
return s

try:
_encoding = QtGui.QApplication.UnicodeUTF8
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
def _translate(context, text, disambig):
return QtGui.QApplication.translate(context, text, disambig)

class Ui_mainWidget(object):
def setupUi(self, mainWidget):
mainWidget.setObjectName(_fromUtf8("mainWidget"))
mainWidget.resize(575, 509)
mainWidget.setMinimumSize(QtCore.QSize(575, 509))
mainWidget.setMaximumSize(QtCore.QSize(575, 509))
mainWidget.setStyleSheet(_fromUtf8(""))
self.layoutWidget = QtGui.QWidget(mainWidget)
self.layoutWidget.setGeometry(QtCore.QRect(10, 10, 558, 489))
self.layoutWidget.setObjectName(_fromUtf8("layoutWidget"))
self.horizontalLayout_6 = QtGui.QHBoxLayout(self.layoutWidget)
self.horizontalLayout_6.setMargin(0)
self.horizontalLayout_6.setObjectName(_fromUtf8("horizontalLayout_6"))
self.verticalLayout_4 = QtGui.QVBoxLayout()
self.verticalLayout_4.setObjectName(_fromUtf8("verticalLayout_4"))
self.messageGB = QtGui.QGroupBox(self.layoutWidget)
self.messageGB.setMinimumSize(QtCore.QSize(372, 224))
self.messageGB.setObjectName(_fromUtf8("messageGB"))
self.horizontalLayout = QtGui.QHBoxLayout(self.messageGB)
self.horizontalLayout.setObjectName(_fromUtf8("horizontalLayout"))
self.messageTB = QtGui.QTextBrowser(self.messageGB)
self.messageTB.setObjectName(_fromUtf8("messageTB"))
self.horizontalLayout.addWidget(self.messageTB)
self.verticalLayout_4.addWidget(self.messageGB)
self.inputGB = QtGui.QGroupBox(self.layoutWidget)
self.inputGB.setObjectName(_fromUtf8("inputGB"))
self.verticalLayout = QtGui.QVBoxLayout(self.inputGB)
self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))
self.inputTE = QtGui.QTextEdit(self.inputGB)
self.inputTE.setObjectName(_fromUtf8("inputTE"))
self.verticalLayout.addWidget(self.inputTE)
self.horizontalLayout_2 = QtGui.QHBoxLayout()
self.horizontalLayout_2.setObjectName(_fromUtf8("horizontalLayout_2"))
spacerItem = QtGui.QSpacerItem(188, 17, QtGui.QSizePolicy.Expanding, QtGui.QSizePolicy.Minimum)
self.horizontalLayout_2.addItem(spacerItem)
self.quitPB = QtGui.QPushButton(self.inputGB)
self.quitPB.setObjectName(_fromUtf8("quitPB"))
self.horizontalLayout_2.addWidget(self.quitPB)
self.sendPB = QtGui.QPushButton(self.inputGB)
self.sendPB.setEnabled(False)
self.sendPB.setObjectName(_fromUtf8("sendPB"))
self.horizontalLayout_2.addWidget(self.sendPB)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.verticalLayout_4.addWidget(self.inputGB)
self.horizontalLayout_6.addLayout(self.verticalLayout_4)
self.verticalLayout_3 = QtGui.QVBoxLayout()
self.verticalLayout_3.setObjectName(_fromUtf8("verticalLayout_3"))
self.statusGB = QtGui.QGroupBox(self.layoutWidget)
self.statusGB.setMaximumSize(QtCore.QSize(171, 151))
self.statusGB.setObjectName(_fromUtf8("statusGB"))
self.horizontalLayout_5 = QtGui.QHBoxLayout(self.statusGB)
self.horizontalLayout_5.setObjectName(_fromUtf8("horizontalLayout_5"))
self.statusTB = QtGui.QTextBrowser(self.statusGB)
self.statusTB.setMaximumSize(QtCore.QSize(161, 119))
self.statusTB.setObjectName(_fromUtf8("statusTB"))
self.horizontalLayout_5.addWidget(self.statusTB)
self.verticalLayout_3.addWidget(self.statusGB)
self.userGB = QtGui.QGroupBox(self.layoutWidget)
self.userGB.setObjectName(_fromUtf8("userGB"))
self.verticalLayout_2 = QtGui.QVBoxLayout(self.userGB)
self.verticalLayout_2.setObjectName(_fromUtf8("verticalLayout_2"))
self.horizontalLayout_3 = QtGui.QHBoxLayout()
self.horizontalLayout_3.setObjectName(_fromUtf8("horizontalLayout_3"))
self.userLE = QtGui.QLineEdit(self.userGB)
self.userLE.setMaximumSize(QtCore.QSize(91, 20))
self.userLE.setObjectName(_fromUtf8("userLE"))
self.horizontalLayout_3.addWidget(self.userLE)
self.findPB = QtGui.QPushButton(self.userGB)
self.findPB.setEnabled(False)
self.findPB.setMaximumSize(QtCore.QSize(51, 23))
self.findPB.setObjectName(_fromUtf8("findPB"))
self.horizontalLayout_3.addWidget(self.findPB)
self.verticalLayout_2.addLayout(self.horizontalLayout_3)
self.userLW = QtGui.QListWidget(self.userGB)
self.userLW.setMinimumSize(QtCore.QSize(0, 161))
self.userLW.setMaximumSize(QtCore.QSize(149, 16777215))
self.userLW.setObjectName(_fromUtf8("userLW"))
self.verticalLayout_2.addWidget(self.userLW)
self.horizontalLayout_4 = QtGui.QHBoxLayout()
self.horizontalLayout_4.setObjectName(_fromUtf8("horizontalLayout_4"))
self.selectallPB = QtGui.QPushButton(self.userGB)
self.selectallPB.setMaximumSize(QtCore.QSize(75, 23))
self.selectallPB.setObjectName(_fromUtf8("selectallPB"))
self.horizontalLayout_4.addWidget(self.selectallPB)
self.resetPB = QtGui.QPushButton(self.userGB)
self.resetPB.setMaximumSize(QtCore.QSize(71, 23))
self.resetPB.setObjectName(_fromUtf8("resetPB"))
self.horizontalLayout_4.addWidget(self.resetPB)
self.verticalLayout_2.addLayout(self.horizontalLayout_4)
self.verticalLayout_3.addWidget(self.userGB)
self.horizontalLayout_6.addLayout(self.verticalLayout_3)

self.retranslateUi(mainWidget)
QtCore.QObject.connect(self.selectallPB, QtCore.SIGNAL(_fromUtf8("clicked()")), self.userLW.selectAll)
QtCore.QObject.connect(self.resetPB, QtCore.SIGNAL(_fromUtf8("clicked()")), self.userLW.reset)
QtCore.QMetaObject.connectSlotsByName(mainWidget)
mainWidget.setTabOrder(self.inputTE, self.sendPB)
mainWidget.setTabOrder(self.sendPB, self.messageTB)
mainWidget.setTabOrder(self.messageTB, self.userLE)
mainWidget.setTabOrder(self.userLE, self.findPB)
mainWidget.setTabOrder(self.findPB, self.userLW)
mainWidget.setTabOrder(self.userLW, self.selectallPB)
mainWidget.setTabOrder(self.selectallPB, self.resetPB)
mainWidget.setTabOrder(self.resetPB, self.statusTB)
mainWidget.setTabOrder(self.statusTB, self.quitPB)

def retranslateUi(self, mainWidget):
mainWidget.setWindowTitle(_translate("mainWidget", "Client", None))
self.messageGB.setTitle(_translate("mainWidget", "Message", None))
self.inputGB.setTitle(_translate("mainWidget", "Input", None))
self.quitPB.setText(_translate("mainWidget", "Quit", None))
self.sendPB.setText(_translate("mainWidget", "Send", None))
self.statusGB.setTitle(_translate("mainWidget", "Status", None))
self.statusTB.setHtml(_translate("mainWidget", "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0//EN\" \"http://www.w3.org/TR/REC-html40/strict.dtd\">\n"
"<html><head><meta name=\"qrichtext\" content=\"1\" /><style type=\"text/css\">\n"
"p, li { white-space: pre-wrap; }\n"
"</style></head><body style=\" font-family:\'SimSun\'; font-size:9pt; font-weight:400; font-style:normal;\">\n"
"<p style=\"-qt-paragraph-type:empty; margin-top:0px; margin-bottom:0px; margin-left:0px; margin-right:0px; -qt-block-indent:0; text-indent:0px;\"><br /></p></body></html>", None))
self.userGB.setTitle(_translate("mainWidget", "User", None))
self.findPB.setText(_translate("mainWidget", "Find", None))
self.selectallPB.setText(_translate("mainWidget", "Select All", None))
self.resetPB.setText(_translate("mainWidget", "Reset", None))


if __name__ == "__main__":
import sys
app = QtGui.QApplication(sys.argv)
mainWidget = QtGui.QWidget()
ui = Ui_mainWidget()
ui.setupUi(mainWidget)
mainWidget.show()
sys.exit(app.exec_())

运行截图

  • server

  • client1

  • client2