PyQt简单示例聊天程序

Title
PyQt简单示例聊天程序
Date
May 4, 2023

效果展示:

notion image

代码:

from PyQt5.Qt import * from talk import Ui_Form import socket import sys import time class Window(QWidget,Ui_Form): def __init__(self, parent=None, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.setupUi(self) ipaddress = Socket_Client.get_ip() #调用类方法进行获取ip self.lineEdit_3.setText(ipaddress) self.radioButton.setChecked(True) self.lineEdit_4.setText("8888") self.pushButton.clicked.connect(self.socket_connect) self.pushButton_2.clicked.connect(self.send_message) self.radioButton.toggled.connect(self.switch_client_server) def switch_client_server(self): print("switch") self.pushButton.clicked.connect(self.socket_server) self.pushButton_2.clicked.connect(self.server_send_data) def socket_connect(self): #连接按钮绑定函数,建立socket连接 QCoreApplication.processEvents(); try: self.socket_client = Socket_Client(self.lineEdit_3.text(),int(self.lineEdit_4.text())) self.socket_client.client_socket() self.setWindowTitle("聊天程序1.0 连接成功") self.thread_1 = Thread_recv(self.socket_client,self.textEdit_2) self.thread_1.start() except Exception as e: print(e) print("error") self.setWindowTitle("聊天程序1.0 连接失败") def socket_server(self): #服务端函数 QCoreApplication.processEvents(); try: self.server_socket = Socket_Client(self.lineEdit_3.text(),int(self.lineEdit_4.text())) self.server_socket.server_socket() self.setWindowTitle("聊天程序1.0 服务开启成功") self.thread_2 = Thread_sev(self.server_socket,self.textEdit_2) self.thread_2.start() except Exception as e: print(e) self.setWindowTitle("聊天程序1.0 服务开启失败") def send_message(self):#发送消息 try: self.socket_client.send_data(self.textEdit.toPlainText()) localtime = time.strftime("%H:%M:%S") self.textEdit_2.append(f"[{localtime}]:"+"发送成功!") self.textEdit_2.append(f"[{localtime}]:" + "我说:" + self.textEdit.toPlainText() ) self.textEdit.clear() self.textEdit.moveCursor(QTextCursor.End) except Exception as e: print(e) def server_send_data(self): try: self.server_socket.server_send_data(self.textEdit.toPlainText()) localtime = time.strftime("%H:%M:%S") self.textEdit_2.append(f"[{localtime}]:" + "发送成功!") self.textEdit_2.append(f"[{localtime}]:" + "我说:" + self.textEdit.toPlainText()) self.textEdit.clear() self.textEdit.moveCursor(QTextCursor.End) except Exception as e: print(e) class Thread_recv(QThread): def __init__(self, recv,text_edit): super().__init__() self.recv = recv self.text_edit = text_edit def run(self): while True: self.recv_data = self.recv.recv_data() print(self.recv_data) localtime = time.strftime("%H:%M:%S") self.text_edit.append( f"[{localtime}]:" +"对方说:"+ self.recv_data) class Thread_sev(QThread): def __init__(self,server_socket,text_edit): super().__init__() self.server = server_socket self.text = text_edit def run(self): self.server.server_accept() print(self.server.client_ip_port) localtime = time.strftime("%H:%M:%S") self.text.append(f"[{localtime}]:" + f"客户端连接成功:{self.server.client_ip_port[0]}") while True: self.data = str(self.server.client.recv(1024),"gbk") print(self.data) localtime = time.strftime("%H:%M:%S") self.text.append(f"[{localtime}]:" + "对方说:" + self.data) class Socket_Client(object): def __init__(self,ipadress,port): self.ipaddress = ipadress self.port = port @classmethod def get_ip(cls): socket_ip = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: socket_ip.connect(('10.255.255.255', 1)) return socket_ip.getsockname()[0] except Exception: return '127.0.0.1' finally: socket_ip.close() def client_socket(self): self.client_connect = socket.socket(family=-1,type=-1) self.client_connect.connect((self.ipaddress,self.port)) def close_socket(self): self.client_connect.close() def server_socket(self): self.server_connect = socket.socket(family=-1,type=-1) self.server_connect.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) self.server_connect.bind((self.ipaddress,self.port)) self.server_connect.listen(128) def server_accept(self): self.client,self.client_ip_port = self.server_connect.accept() def server_send_data(self,data): data = data.encode('gbk') print(data) self.client.send(data) def send_data(self,data): data = data.encode('gbk') #进行gbk编码 print(data) self.client_connect.send(data) def recv_data(self): recv = str(self.client_connect.recv(1024),'gbk') #以gbk方式转码 return recv if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.setWindowTitle("聊天程序1.0") window.show() sys.exit(app.exec())

界面示意图:

notion image
界面代码:
# -*- coding: utf-8 -*- # Form implementation generated from reading ui file 'C:\Users\Administrator\Desktop\qt\talk\talk.ui' # # Created by: PyQt5 UI code generator 5.15.2 # # WARNING: Any manual changes made to this file will be lost when pyuic5 is # run again. Do not edit this file unless you know what you are doing. from PyQt5 import QtCore, QtGui, QtWidgets class Ui_Form(object): def setupUi(self, Form): Form.setObjectName("Form") Form.resize(400, 300) self.pushButton = QtWidgets.QPushButton(Form) self.pushButton.setGeometry(QtCore.QRect(290, 40, 93, 28)) self.pushButton.setObjectName("pushButton") self.pushButton_2 = QtWidgets.QPushButton(Form) self.pushButton_2.setGeometry(QtCore.QRect(290, 90, 93, 28)) self.pushButton_2.setObjectName("pushButton_2") self.lineEdit = QtWidgets.QLineEdit(Form) self.lineEdit.setGeometry(QtCore.QRect(20, 10, 31, 20)) self.lineEdit.setObjectName("lineEdit") self.lineEdit_2 = QtWidgets.QLineEdit(Form) self.lineEdit_2.setGeometry(QtCore.QRect(20, 150, 31, 20)) self.lineEdit_2.setObjectName("lineEdit_2") self.radioButton = QtWidgets.QRadioButton(Form) self.radioButton.setGeometry(QtCore.QRect(300, 130, 93, 31)) self.radioButton.setObjectName("radioButton") self.radioButton_2 = QtWidgets.QRadioButton(Form) self.radioButton_2.setGeometry(QtCore.QRect(300, 160, 93, 31)) self.radioButton_2.setObjectName("radioButton_2") self.lineEdit_3 = QtWidgets.QLineEdit(Form) self.lineEdit_3.setGeometry(QtCore.QRect(280, 220, 111, 31)) self.lineEdit_3.setText("") self.lineEdit_3.setObjectName("lineEdit_3") self.label = QtWidgets.QLabel(Form) self.label.setGeometry(QtCore.QRect(310, 200, 54, 12)) self.label.setObjectName("label") self.label_2 = QtWidgets.QLabel(Form) self.label_2.setGeometry(QtCore.QRect(290, 260, 41, 21)) font = QtGui.QFont() font.setFamily("Arial") self.label_2.setFont(font) self.label_2.setLayoutDirection(QtCore.Qt.RightToLeft) self.label_2.setAutoFillBackground(False) self.label_2.setObjectName("label_2") self.lineEdit_4 = QtWidgets.QLineEdit(Form) self.lineEdit_4.setGeometry(QtCore.QRect(320, 260, 61, 21)) self.lineEdit_4.setObjectName("lineEdit_4") self.textEdit = QtWidgets.QTextEdit(Form) self.textEdit.setGeometry(QtCore.QRect(20, 40, 241, 101)) self.textEdit.setObjectName("textEdit") self.textEdit_2 = QtWidgets.QTextEdit(Form) self.textEdit_2.setGeometry(QtCore.QRect(20, 180, 241, 101)) self.textEdit_2.setObjectName("textEdit_2") self.retranslateUi(Form) QtCore.QMetaObject.connectSlotsByName(Form) def retranslateUi(self, Form): _translate = QtCore.QCoreApplication.translate Form.setWindowTitle(_translate("Form", "Form")) self.pushButton.setText(_translate("Form", "连接/开启")) self.pushButton_2.setText(_translate("Form", "发送")) self.lineEdit.setText(_translate("Form", "发送")) self.lineEdit_2.setText(_translate("Form", "接收")) self.radioButton.setText(_translate("Form", "客户端")) self.radioButton_2.setText(_translate("Form", "服务端")) self.label.setText(_translate("Form", "ip地址")) self.label_2.setToolTip(_translate("Form", "<html><head/><body><p align=\"center\"><br/></p></body></html>")) self.label_2.setText(_translate("Form", "端口")) if __name__ == "__main__": import sys app = QtWidgets.QApplication(sys.argv) Form = QtWidgets.QWidget() ui = Ui_Form() ui.setupUi(Form) Form.show() sys.exit(app.exec_())

思路:

1.使用PyQT和Socket库
先初始化ui界面:
class Window(QWidget,Ui_Form): def __init__(self, parent=None, *args, **kwargs): super().__init__(parent, *args, **kwargs) self.setupUi(self) ipaddress = Socket_Client.get_ip() #获取ip self.lineEdit_3.setText(ipaddress) self.radioButton.setChecked(True) self.lineEdit_4.setText("8888") self.pushButton.clicked.connect(self.socket_connect) self.pushButton_2.clicked.connect(self.send_message) self.radioButton.toggled.connect(self.switch_client_server)
然后绑定两个按钮的点击信号的槽函数:
self.pushButton.clicked.connect(self.socket_connect) self.pushButton_2.clicked.connect(self.send_message)
创建socket类当运行槽函数时创建服务端或客户端:
class Socket_Client(object): def __init__(self,ipadress,port): #当类初始化时填入ip地址和端口号 self.ipaddress = ipadress self.port = port @classmethod def get_ip(cls): #获取初始化的ip地址,获取不到填入127.0.0.1 socket_ip = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: socket_ip.connect(('10.255.255.255', 1)) return socket_ip.getsockname()[0] except Exception: return '127.0.0.1' finally: socket_ip.close() def client_socket(self): #创建客户端的socket通信 self.client_connect = socket.socket(family=-1,type=-1) self.client_connect.connect((self.ipaddress,self.port)) def close_socket(self): #关闭socket连接 self.client_connect.close() def server_socket(self): #创建服务端的sockett通信 self.server_connect = socket.socket(family=-1,type=-1) self.server_connect.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, True) self.server_connect.bind((self.ipaddress,self.port)) self.server_connect.listen(128) def server_accept(self):#服务端的监听函数 self.client,self.client_ip_port = self.server_connect.accept() def server_send_data(self,data): #服务端发送函数 data = data.encode('gbk') print(data) self.client.send(data) def send_data(self,data):#客户端发送数据函数 data = data.encode('gbk') #进行gbk编码 print(data) self.client_connect.send(data) def recv_data(self):#客户端监听函数 recv = str(self.client_connect.recv(1024),'gbk') #以gbk方式转码 return recv
分别创建客户端和服务端的发送按钮和开启按钮的槽函数:
def socket_connect(self): #连接按钮绑定函数,建立socket连接 QCoreApplication.processEvents(); try: self.socket_client = Socket_Client(self.lineEdit_3.text(),int(self.lineEdit_4.text())) self.socket_client.client_socket() self.setWindowTitle("聊天程序1.0 连接成功") self.thread_1 = Thread_recv(self.socket_client,self.textEdit_2 #创建子进程 self.thread_1.start()#开启子进程 except Exception as e: print(e) print("error") self.setWindowTitle("聊天程序1.0 连接失败") def socket_server(self): #服务端函数 QCoreApplication.processEvents(); try: self.server_socket = Socket_Client(self.lineEdit_3.text(),int(self.lineEdit_4.text())) self.server_socket.server_socket() self.setWindowTitle("聊天程序1.0 服务开启成功") self.thread_2 = Thread_sev(self.server_socket,self.textEdit_2) #创建子进程 self.thread_2.start() #开启子进程 except Exception as e: print(e) self.setWindowTitle("聊天程序1.0 服务开启失败") def send_message(self):#发送消息 try: self.socket_client.send_data(self.textEdit.toPlainText()) localtime = time.strftime("%H:%M:%S") self.textEdit_2.append(f"[{localtime}]:"+"发送成功!") self.textEdit_2.append(f"[{localtime}]:" + "我说:" + self.textEdit.toPlainText() ) self.textEdit.clear() self.textEdit.moveCursor(QTextCursor.End) except Exception as e: print(e) def server_send_data(self): try: self.server_socket.server_send_data(self.textEdit.toPlainText()) localtime = time.strftime("%H:%M:%S") self.textEdit_2.append(f"[{localtime}]:" + "发送成功!") self.textEdit_2.append(f"[{localtime}]:" + "我说:" + self.textEdit.toPlainText()) self.textEdit.clear() self.textEdit.moveCursor(QTextCursor.End) except Exception as e: print(e)
然后创建俩个QT线程,因为PYQT主线程用来绘制GUI界面,如果有创造一些耗时的函数,在主线程中执行,那么界面很容易就卡死,解决的方法一般有两个,一个是创造线程,进程,一个就是调用QCoreApplication.processEvents(),来让GUI界面防止卡死,但是遇到阻塞函数,还是一样会卡死。在socket中的接收消息的过程中,就需要一直等待接收,只能放到子线程中去执行。
class Thread_recv(QThread): #客户端等待接收消息的线程 def __init__(self, recv,text_edit): super().__init__() self.recv = recv self.text_edit = text_edit def run(self): while True: self.recv_data = self.recv.recv_data() print(self.recv_data) localtime = time.strftime("%H:%M:%S") self.text_edit.append( f"[{localtime}]:" +"对方说:"+ self.recv_data) class Thread_sev(QThread): #服务端等待连接的线程 def __init__(self,server_socket,text_edit): super().__init__() self.server = server_socket self.text = text_edit def run(self): self.server.server_accept() print(self.server.client_ip_port) localtime = time.strftime("%H:%M:%S") self.text.append(f"[{localtime}]:" + f"客户端连接成功:{self.server.client_ip_port[0]}") while True: self.data = str(self.server.client.recv(1024),"gbk") print(self.data) localtime = time.strftime("%H:%M:%S") self.text.append(f"[{localtime}]:" + "对方说:" + self.data)
最后加一个选择按钮的切换状态槽函数和主函数:
def switch_client_server(self): print("switch") self.pushButton.clicked.connect(self.socket_server) self.pushButton_2.clicked.connect(self.server_send_data)
if __name__ == '__main__': app = QApplication(sys.argv) window = Window() window.setWindowTitle("聊天程序1.0") window.show() sys.exit(app.exec())
总结:
pyqt的界面非常容易卡死,所以在写程序的时候,多线程是必不可少的。
Built with Potion.so