上一章python套接字(二):实现一个服务器和多客户端连接,大概实现了多人聊天室功能,但是比较简陋,本篇内容将结合pyside2做一个有界面的多人聊天室。
直接命令安装就好了
pip install pyside2
使用pyside2自带的designer.exe来设计界面,界面如下,界面设计具体可以参考qtdesigner页面布局,界面如下(不想设计界面也可以直接复制下面的py代码):
保存之后会得到ui文件,使用命令可以将ui文件转换成py文件:
pyside2-uic chat_win.ui > chat_win.py
能得到如下的py文件
chat_win.py
# -*- coding: utf-8 -*-
################################################################################
## Form generated from reading UI file 'chat_win.ui'
##
## Created by: Qt User Interface Compiler version 5.15.2
##
## WARNING! All changes made in this file will be lost when recompiling UI file!
################################################################################
from PySide2.QtCore import *
from PySide2.QtGui import *
from PySide2.QtWidgets import *
class Ui_ChatWin(object):
def setupUi(self, ChatWin):
if not ChatWin.objectName():
ChatWin.setObjectName(u"ChatWin")
ChatWin.resize(480, 600)
self.verticalLayout = QVBoxLayout(ChatWin)
self.verticalLayout.setObjectName(u"verticalLayout")
self.horizontalLayout = QHBoxLayout()
self.horizontalLayout.setObjectName(u"horizontalLayout")
self.ip_label = QLabel(ChatWin)
self.ip_label.setObjectName(u"ip_label")
self.horizontalLayout.addWidget(self.ip_label)
self.ip_edt = QLineEdit(ChatWin)
self.ip_edt.setObjectName(u"ip_edt")
self.horizontalLayout.addWidget(self.ip_edt)
self.port_label = QLabel(ChatWin)
self.port_label.setObjectName(u"port_label")
self.horizontalLayout.addWidget(self.port_label)
self.port_edt = QLineEdit(ChatWin)
self.port_edt.setObjectName(u"port_edt")
self.horizontalLayout.addWidget(self.port_edt)
self.verticalLayout.addLayout(self.horizontalLayout)
self.horizontalLayout_3 = QHBoxLayout()
self.horizontalLayout_3.setObjectName(u"horizontalLayout_3")
self.name_label = QLabel(ChatWin)
self.name_label.setObjectName(u"name_label")
self.horizontalLayout_3.addWidget(self.name_label)
self.name_edt = QLineEdit(ChatWin)
self.name_edt.setObjectName(u"name_edt")
self.horizontalLayout_3.addWidget(self.name_edt)
self.join_btn = QPushButton(ChatWin)
self.join_btn.setObjectName(u"join_btn")
self.horizontalLayout_3.addWidget(self.join_btn)
self.leave_btn = QPushButton(ChatWin)
self.leave_btn.setObjectName(u"leave_btn")
self.horizontalLayout_3.addWidget(self.leave_btn)
self.verticalLayout.addLayout(self.horizontalLayout_3)
self.chat_edt = QTextEdit(ChatWin)
self.chat_edt.setObjectName(u"chat_edt")
self.chat_edt.setReadOnly(True)
self.verticalLayout.addWidget(self.chat_edt)
self.horizontalLayout_2 = QHBoxLayout()
self.horizontalLayout_2.setObjectName(u"horizontalLayout_2")
self.send_edt = QTextEdit(ChatWin)
self.send_edt.setObjectName(u"send_edt")
self.horizontalLayout_2.addWidget(self.send_edt)
self.send_btn = QPushButton(ChatWin)
self.send_btn.setObjectName(u"send_btn")
sizePolicy = QSizePolicy(QSizePolicy.Minimum, QSizePolicy.Minimum)
sizePolicy.setHorizontalStretch(0)
sizePolicy.setVerticalStretch(0)
sizePolicy.setHeightForWidth(self.send_btn.sizePolicy().hasHeightForWidth())
self.send_btn.setSizePolicy(sizePolicy)
self.horizontalLayout_2.addWidget(self.send_btn)
self.verticalLayout.addLayout(self.horizontalLayout_2)
self.verticalLayout.setStretch(0, 1)
self.verticalLayout.setStretch(1, 1)
self.verticalLayout.setStretch(2, 15)
self.verticalLayout.setStretch(3, 3)
self.retranslateUi(ChatWin)
QMetaObject.connectSlotsByName(ChatWin)
# setupUi
def retranslateUi(self, ChatWin):
ChatWin.setWindowTitle(QCoreApplication.translate("ChatWin", u"\u674e\u5927\u5e1d\u7684\u804a\u5929\u5ba4", None))
self.ip_label.setText(QCoreApplication.translate("ChatWin", u"\u804a\u5929\u5ba4\u5730\u5740\uff1a", None))
self.port_label.setText(QCoreApplication.translate("ChatWin", u"\u7aef\u53e3\uff1a", None))
self.name_label.setText(QCoreApplication.translate("ChatWin", u"\u7528\u6237\u540d\uff1a", None))
self.join_btn.setText(QCoreApplication.translate("ChatWin", u"\u52a0\u5165\u804a\u5929\u5ba4", None))
self.leave_btn.setText(QCoreApplication.translate("ChatWin", u"\u79bb\u5f00\u804a\u5929\u5ba4", None))
self.send_btn.setText(QCoreApplication.translate("ChatWin", u"\u53d1\u9001", None))
# retranslateUi
因为图形化界面是给客户用的,所以要修改的地方是客户端,服务器端不用改。
首先客户端需要引入图形界面的代码。发送数据因为是点击发送按钮,才发送数据,所以不用使用子线程处理,改为点击按钮触发。接收数据仍要需要一直等待服务器发送数据,所以还是需要创建一个子线程处理接收数据方法,将接收到的数据在文本框中展示。代码如下:
main.py
from PySide2.QtWidgets import QApplication, QWidget, QMessageBox
import sys
import socket
from threading import Thread
from chat_win import Ui_ChatWin
class ChatMainWin(QWidget, Ui_ChatWin):
def __init__(self):
self.server_addr = None # 存放服务器地址
self.tcp_cli_socket = None # 创建socket对象
super().__init__() # 执行父类构造器
self.setupUi(self) # 执行Ui_ChatWin中的setupUi()方法,用于窗口展示
self.btn_controller() # 执行按钮绑定事件
# 存放按钮绑定事件
def btn_controller(self):
self.join_btn.clicked.connect(self.join_chat) # 加入聊天室
self.send_btn.clicked.connect(self.msg_send) # 发送消息
self.leave_btn.clicked.connect(self.leave_chat) # 离开聊天室
def join_chat(self):
"""
加入聊天室:
没有和服务器连接的才能加入聊天室
"""
if self.tcp_cli_socket is None:
self.tcp_cli_socket = socket.socket() # 创建socket对象
# 获取数据
ip = self.ip_edt.text().strip() # 服务器ip地址
port = self.port_edt.text().strip() # 端口地址
username = self.name_edt.text().strip() # 用户名
# 判断必要信息是否填写全
if ip == "":
QMessageBox.warning(self, '警告', "请输入服务器地址!")
return
if port == "":
QMessageBox.warning(self, '警告', "请输入服务器端口号!")
return
if username == "":
QMessageBox.warning(self, '警告', "请输入用户名!")
return
self.server_addr = (ip, int(port))
# 链接服务器
try:
self.tcp_cli_socket.connect(self.server_addr)
except Exception as e:
QMessageBox.critical(self, '错误', "连接失败,请重试!")
self.tcp_cli_socket.close()
print(e)
return
self.tcp_cli_socket.send((username + ' -n').encode('utf-8'))
data = self.tcp_cli_socket.recv(128).decode('utf-8')
if data == "OK":
QMessageBox.information(self, '成功', "你已成功进入聊天室!")
# print(data)
else:
QMessageBox.critical(self, '失败', "进入聊天室失败,请重新尝试!")
print(data)
return
t = Thread(target=self.msg_recv, daemon=True)
t.start()
else:
QMessageBox.warning(self, '警告', "您已经在聊天室中!")
def leave_chat(self):
"""
离开聊天室:
先判断有没有和服务器连接,有连接才能离开
"""
choice = QMessageBox.question(self, '提示', '确定离开聊天室吗')
# print(choice)
if choice == QMessageBox.Yes:
if self.tcp_cli_socket:
self.tcp_cli_socket.send('exit'.encode("utf-8")) # 发送exit指令到服务器
else:
QMessageBox.warning(self, '警告', '您还没加入任何聊天室!')
def msg_recv(self):
"""
接收数据:
创建死循环来监听来自服务器的消息
"""
while True:
data = self.tcp_cli_socket.recv(1024)
if data.decode("utf-8") == "exit":
self.chat_edt.insertPlainText('您已退出聊天室\n') # 往聊天栏里面添加数据
break
self.chat_edt.insertPlainText(data.decode("utf-8") + '\n') # 往聊天栏里面添加数据
self.tcp_cli_socket.close()
self.tcp_cli_socket = None # 重置值
def msg_send(self):
"""
发送数据
"""
data_info = self.send_edt.toPlainText().strip()
self.tcp_cli_socket.send((data_info + ' -ta').encode("utf-8"))
self.send_edt.setPlainText("") # 清空输入框内容
if __name__ == '__main__':
app = QApplication(sys.argv)
chat_win = ChatMainWin()
chat_win.show()
sys.exit(app.exec_())
先运行服务器
然后运行三个服务端(main.py运行了三次),这里我讲服务器ip改成了本机的局域网ip,这样只要在一个局域网内,都可以进行通信。
大功告成,确实是可以互相通信。
最后,附上代码链接:
链接:https://pan.baidu.com/s/1IO7juzqytyhuECacRqlmMA?pwd=73j8
提取码:73j8