python实现的聊天室(二)
1、前言
我在python实现的聊天室(一)一文中实现了一个没有界面的聊天室,所有操作都在CMD命令行下进行,用户体验比较差。所以在这一篇文章中,主要实现一个有界面的聊天室。
2、需求分析
我们要实现的分为两部分:
服务器:负责与用户建立 Socket 连接,并将某个用户发送的消息广播到所有在线的用户。显示用户进入/退出聊天室信息。客户端:可以输入聊天的内容并发送,同时可以显示其他用户的消息记录。
3、服务端实现
# -*- coding: gbk -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
from PyQt4.QtNetwork import *
import locale
#QTextCodec.setCodecForTr(QTextCodec.codecForName("utf8"))
class TcpClientSocket(QTcpSocket):
def __init__(self,parent=None):
super(TcpClientSocket,self).__init__(parent)
self.connect(self,SIGNAL("readyRead()"),self.dataReceive)
self.connect(self,SIGNAL("disconnected()"),self.slotDisconnected)
self.length = 0
self.msglist = QByteArray()
def dataReceive(self):
while self.bytesAvailable() > 0:
length = self.bytesAvailable()
msg = self.read(length)
self.emit(SIGNAL("updateClients(QString,int)"),msg,length)
def slotDisconnected(self):
pass
class Server(QTcpServer):
def __init__(self,parent=None,port=0):
super(Server,self).__init__(parent)
self.listen(QHostAddress.Any,port)
self.tcpClientSocketList = []
def incomingConnection(self,socketDescriptor):
tcpClientSocket = TcpClientSocket(self)
self.connect(tcpClientSocket,SIGNAL("updateClients(QString,int)"),self.updateClients)
self.connect(tcpClientSocket,SIGNAL("disconnetcted(int)"),self.slotDisconnected)
tcpClientSocket.setSocketDescriptor(socketDescriptor)
self.tcpClientSocketList.append(tcpClientSocket)
def updateClients(self,msg,length):
self.emit(SIGNAL("updateServer(QString,int)"),msg,length)
for i in xrange(len(self.tcpClientSocketList)):
item = self.tcpClientSocketList[i]
length_msg = item.writeData(msg)
#length_msg = item.writeData(msg.toUtf8())
#if length_msg != msg.toUtf8().length():
# continue
def slotDisconnected(self,descriptor):
for i in xrange(len(self.tcpClientSocketList)):
item = self.tcpClientSocketList[i]
if item.socketDescriptor() == descriptor:
self.tcpClientSocketList.remove[i]
return
return
class TcpServer(QDialog):
def __init__(self,parent=None,f=None):
super(TcpServer,self).__init__(parent)
self.setWindowTitle("服务端")
vbMain = QVBoxLayout(self)
self.ListWidgetContent = QListWidget(self)
vbMain.addWidget(self.ListWidgetContent)
hb = QHBoxLayout()
LabelPort = QLabel(self)
LabelPort.setText(self.tr("Port:"))
hb.addWidget(LabelPort)
LineEditPort = QLineEdit(self)
hb.addWidget(LineEditPort)
vbMain.addLayout(hb)
self.PushButtonCreate = QPushButton(self)
self.PushButtonCreate.setText(self.tr("创建聊天室"))
vbMain.addWidget(self.PushButtonCreate)
self.connect(self.PushButtonCreate,SIGNAL("clicked()"),self.slotCreateServer)
self.port = 8010
LineEditPort.setText(QString.number(self.port))
def slotCreateServer(self):
server = Server(self,self.port)
self.connect(server,SIGNAL("updateServer(QString,int)"),self.updateServer)
self.PushButtonCreate.setEnabled(False)
def updateServer(self,msg,length):
#self.ListWidgetContent.addItem(msg.fromUtf8(msg))
self.ListWidgetContent.addItem(msg)
if __name__ == '__main__':
app=QApplication(sys.argv)
mycode = locale.getpreferredencoding()
code = QTextCodec.codecForName(mycode)
QTextCodec.setCodecForLocale(code)
QTextCodec.setCodecForTr(code)
QTextCodec.setCodecForCStrings(code)
dialog=TcpServer()
dialog.show()
app.exec_()
4、客户端实现
# -*- coding: gbk -*-
from PyQt4.QtGui import *
from PyQt4.QtCore import *
import sys
from PyQt4.QtNetwork import *
import locale
#QTextCodec.setCodecForTr(QTextCodec.codecForName("utf8"))
class TcpClient(QDialog):
def __init__(self,parent=None):
super(TcpClient,self).__init__(parent)
self.setWindowTitle(self.tr("客户端"))
self.status = False
self.serverIP = QHostAddress()
self.port = 8010
self.msglist = QByteArray()
vbMain = QVBoxLayout(self)
self.ListWidgetContent = QListWidget(self)
vbMain.addWidget(self.ListWidgetContent)
hb = QHBoxLayout()
self.LineEditMessage = QLineEdit(self)
hb.addWidget(self.LineEditMessage)
self.PushButtonSend = QPushButton(self)
self.PushButtonSend.setText(self.tr("发送"))
self.PushButtonSend.setEnabled(False)
hb.addWidget(self.PushButtonSend)
self.connect(self.PushButtonSend,SIGNAL("clicked()"),self.slotSend)
hb1 = QHBoxLayout()
LabelName = QLabel(self)
LabelName.setText(self.tr("用户名:"))
self.LineEditUser = QLineEdit(self)
hb1.addWidget(LabelName)
hb1.addWidget(self.LineEditUser)
hb2 = QHBoxLayout()
LabelServerIP = QLabel(self)
LabelServerIP.setText(self.tr("服务器地址:"))
self.LineEditIP = QLineEdit(self)
hb2.addWidget(LabelServerIP)
hb2.addWidget(self.LineEditIP)
hb3 = QHBoxLayout()
LabelPort = QLabel(self)
LabelPort.setText(self.tr("端口:"))
self.LineEditPort = QLineEdit(self)
hb3.addWidget(LabelPort)
hb3.addWidget(self.LineEditPort)
vbMain.addLayout(hb)
vbMain.addLayout(hb1)
vbMain.addLayout(hb2)
vbMain.addLayout(hb3)
self.PushButtonLeave = QPushButton(self)
self.PushButtonLeave.setText(self.tr("进入聊天室"))
vbMain.addWidget(self.PushButtonLeave)
self.connect(self.PushButtonLeave,SIGNAL("clicked()"),self.slotEnter)
def slotSend(self):
msg = self.userName + ":" + self.LineEditMessage.text(
#length = self.tcpSocket.writeData(msg.toUtf8())
length = self.tcpSocket.writeData(msg)
self.LineEditMessage.clear()
#if length != msg.toUtf8().length():
# return
def slotEnter(self):
if not self.status:
ip = self.LineEditIP.text()
if not self.serverIP.setAddress(ip):
QMessageBox.information(self,self.tr("error"),self.tr("server ip address error!"))
return
if self.LineEditUser.text() == "":
QMessageBox.information(self,self.tr("error"),self.tr("User Name error!"))
return
self.userName = self.LineEditUser.text()
self.tcpSocket = QTcpSocket(self)
self.connect(self.tcpSocket,SIGNAL("connected()"),self.slotConnected)
self.connect(self.tcpSocket,SIGNAL("disconnected()"),self.slotDisconnected)
self.connect(self.tcpSocket,SIGNAL("readyRead()"),self.dataReceived)
self.tcpSocket.connectToHost(self.serverIP.toString(),8010)
self.status = True
else:
msg = self.userName + ":" + self.tr("离开聊天室")
length = self.tcpSocket.writeData(msg)
#length = self.tcpSocket.writeData(msg.toUtf8())
#if length != msg.toUtf8().length():
# return
self.tcpSocket.disconnectFromHost()
self.status = False
def slotConnected(self):
self.PushButtonSend.setEnabled(True)
self.PushButtonLeave.setText(self.tr("离开聊天室"))
msg = self.userName + ":" + self.tr("进入聊天室")
#length = self.tcpSocket.writeData(msg.toUtf8())
length = self.tcpSocket.writeData(msg)
#if length != msg.toUtf8().length():
# return
def slotDisconnected(self):
self.PushButtonSend.setEnabled(False)
self.PushButtonLeave.setText(self.tr("进入聊天室"))
def dataReceived(self):
while self.tcpSocket.bytesAvailable() > 0:
length = self.tcpSocket.bytesAvailable()
msg = QString(self.tcpSocket.read(length))
#msg = msg.fromUtf8(msg)
#self.ListWidgetContent.addItem(msg.fromUtf8(msg))
self.ListWidgetContent.addItem(msg)
if __name__ == '__main__':
app=QApplication(sys.argv)
mycode = locale.getpreferredencoding()
code = QTextCodec.codecForName(mycode)
QTextCodec.setCodecForLocale(code)
QTextCodec.setCodecForTr(code)
QTextCodec.setCodecForCStrings(code)
dialog=TcpClient()
dialog.show()
app.exec_()
5、运行效果
运行时先启动服务端,再运行客户端。服务器启动后创建聊天室。客户端运行后,输入昵称、服务器的IP及port信息,进入聊天室。此时服务端会提示用户进入聊天室。当客户端关闭,表明该用户退出聊天室,服务器会转发该用户退出消息到各个客户端。 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TLWfRAnb-1593526750899)(http://odsh9s4s2.bkt.clouddn.com/mypyqtchatserver.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XLahNRDt-1593526750901)(http://odsh9s4s2.bkt.clouddn.com/mypyqtchatclientA.png)] [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xB06qmLF-1593526750903)(http://odsh9s4s2.bkt.clouddn.com/mypyqtchatclientB.png)]
6、参考链接
因为代码中用到了汉字,所以涉及到编码问题。希望该链接能帮助到你。 http://blog.csdn.net/uselym/article/details/51867056 526750901)] [外链图片转存中…(img-xB06qmLF-1593526750903)]
6、参考链接
因为代码中用到了汉字,所以涉及到编码问题。希望该链接能帮助到你。 http://blog.csdn.net/uselym/article/details/51867056