• GUI 应用:socket 网络聊天室


    在这个周末刚刚写出来的python桌面应用--网络聊天室,主要通过pyqt5作为桌面应用框架,socket作为网络编程的框架,从而实现包括客户端和服务端的网络聊天室的GUI应用,希望可以一起学习、一起进步!

    应用包括服务端server_ui.py、客户端client_ui.py两个python模块实现,并且在pyqt5的使用过程中都使用QThread多线程应用以及基本的UI页面布局。开始之前通过一个动态图来观察一下socket服务端、socket客户端通信的实现效果。

    【阅读全文】

    file

    1. socket_ui.py 服务端

    1-1. 依赖引用

    在socket服务端的实现过程中,除了pyqt5相关的UI界面的引用外,还包括time、threading、sys、socket等辅助模块来一起实现socket服务端的桌面应用程序。

    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    import sys
    
    from QCandyUi import CandyWindow
    
    # 导入 socket 通讯模块
    import socket
    # 导入时间管理模块
    import time
    # 导入多线程模块
    import threading
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    1-2. 实现过程

    在服务端的业务实现上面,我们依然是按照之前的GUI实现方式,采用主线程用来实现页面布局,子线程QThread来实现业务逻辑的方式来进行实现的,socket的服务端通信业务都是在子线程ServerThread中编写的。下面是socket服务端桌面应用实现的全部代码块,copy到自己的ide中即可直接启动使用。

    class ServerUI(QWidget):
        def __init__(self):
            super(ServerUI, self).__init__()
            self.init_ui()
    
        def init_ui(self):
            self.setWindowTitle('socket 服务端  公众号:[Python 集中营]')
            self.setWindowIcon(QIcon('hi.ico'))
            self.setFixedSize(500, 300)
    
            hbox = QHBoxLayout()
    
            hbox_v1 = QVBoxLayout()
            self.brower = QTextBrowser()
            self.brower.setFont(QFont('宋体', 8))
            self.brower.setReadOnly(True)
            self.brower.setPlaceholderText('消息展示区域...')
            self.brower.ensureCursorVisible()
            hbox_v1.addWidget(self.brower)
    
            hbox_v2 = QVBoxLayout()
    
            hbox_v2_f1 = QFormLayout()
            self.ip_label = QLabel()
            self.ip_label.setText('ip地址 ')
            self.ip_txt = QLineEdit()
            self.ip_txt.setPlaceholderText('0.0.0.0')
    
            self.port_label = QLabel()
            self.port_label.setText('端口 ')
            self.port_txt = QLineEdit()
            self.port_txt.setPlaceholderText('4444')
    
            self.lis_num_label = QLabel()
            self.lis_num_label.setText('最大监听个数 ')
            self.lis_num_txt = QLineEdit()
            self.lis_num_txt.setPlaceholderText('10')
    
            self.close_cli_label = QLabel()
            self.close_cli_label.setText('客户端关闭指令 ')
            self.close_cli_txt = QLineEdit()
            self.close_cli_txt.setPlaceholderText('exit,客户端发送相应指令则关闭')
    
            hbox_v2_f1.addRow(self.ip_label, self.ip_txt)
            hbox_v2_f1.addRow(self.port_label, self.port_txt)
            hbox_v2_f1.addRow(self.lis_num_label, self.lis_num_txt)
            hbox_v2_f1.addRow(self.close_cli_label, self.close_cli_txt)
    
            self.start_btn = QPushButton()
            self.start_btn.setText('开启服务端')
            self.start_btn.clicked.connect(self.start_btn_clk)
    
            hbox_v2.addLayout(hbox_v2_f1)
            hbox_v2.addWidget(self.start_btn)
    
            hbox.addLayout(hbox_v1)
            hbox.addLayout(hbox_v2)
    
            self.thread_ = ServerThread(self)
            self.thread_.message.connect(self.show_message)
    
            self.setLayout(hbox)
    
        def show_message(self, text):
            '''
            槽函数:向文本浏览器中写入内容
            :param text:
            :return:
            '''
            cursor = self.brower.textCursor()
            cursor.movePosition(QTextCursor.End)
            self.brower.append(text)
            self.brower.setTextCursor(cursor)
            self.brower.ensureCursorVisible()
    
        def start_btn_clk(self):
            self.thread_.start()
            self.start_btn.setEnabled(False)
    
    
    class ServerThread(QThread):
        message = pyqtSignal(str)
    
        def __init__(self, parent=None):
            super(ServerThread, self).__init__(parent)
            self.parent = parent
            self.working = True
    
        def __del__(self):
            self.working = False
            self.wait()
    
        def run(self):
            self.message.emit('准备启动socket服务端...')
            # 创建服务端 socket
            socket_server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
            # 绑定服务地址、端口
            address = (self.parent.ip_txt.text().strip(), int(self.parent.port_txt.text().strip()))
            socket_server.bind(address)
            # 设置监听最大等待数
            socket_server.listen(int(self.parent.lis_num_txt.text().strip()))
            self.message.emit("服务已经启动,正在等待客户端连接...")
            while True:
                # 设置睡眠时间
                time.sleep(0.1)
                # 允许客户端连接
                client, info = socket_server.accept()
                self.client, self.info = client, info
                # 启用新线程调用消息处理
                thread = threading.Thread(target=self.catch_message)
                # 设置为守护线程
                thread.setDaemon(True)
                # 开启线程执行
                thread.start()
    
        def catch_message(self):
            self.client.send("欢迎来到网络聊天室".encode('utf-8'))
            self.message.emit("客户端信息:" + str(self.info))
            close_cli = self.parent.close_cli_txt.text().strip()
            while True:
                try:
                    #  接收客户端消息、接收最大长度为 1024,并进行 utf-8 解码
                    message = self.client.recv(1024).decode('utf-8')
                    # 校验是否关闭客户端
                    if not message and close_cli == message:
                        self.client.close()
                        self.message.emit("当前客户端已关闭!")
                        break
                    self.message.emit("接收到消息:" + message)
                    # 将消息进行 utf-8 编码后发给客户端
                    rcv = "服务端成功接收消息:" + message
                    self.client.send(rcv.encode('utf-8'))
                except Exception as e:
                    self.client.send("服务端处理消息异常!".encode('utf-8'))
                    break
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = CandyWindow.createWindow(ServerUI(), theme='blueGreen', title='socket 服务端  公众号:[Python 集中营]',
                                     ico_path='hi.ico')
        w.show()
        sys.exit(app.exec_())
    • 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

    1-3. 实现效果

    file

    1. client_ui.py 客户端

    2-1. 依赖引用

    在socket客户端的实现过程中,除了pyqt5相关的UI界面的引用外,还包括sys、socket等辅助模块来一起实现socket服务端的桌面应用程序,相比服务端来说,客户端并没有使用多线程threading模块。

    from PyQt5.QtWidgets import *
    from PyQt5.QtCore import *
    from PyQt5.QtGui import *
    import sys
    
    from QCandyUi import CandyWindow
    
    # 导入socket 通信模块
    import socket
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    2-2. 实现过程

    客户端的实现过程和服务端server_ui.py实现是基本相似的,同样也使用到了pyqt5的QThread的子线程应用,唯一不同的是socket客户端通信方式跟服务端不大相同,同样将下面的代码块copy到自己的ide中直接使用即可。

    class ClientUI(QWidget):
        def __init__(self):
            super(ClientUI, self).__init__()
            self.init_ui()
    
        def init_ui(self):
            self.setWindowTitle('socket 客户端  公众号:[Python 集中营]')
            self.setWindowIcon(QIcon('hi.ico'))
            self.setFixedSize(500, 300)
    
            hbox = QHBoxLayout()
    
            hbox_v1 = QVBoxLayout()
            self.brower = QTextBrowser()
            self.brower.setFont(QFont('宋体', 8))
            self.brower.setReadOnly(True)
            self.brower.setPlaceholderText('消息展示区域...')
            self.brower.ensureCursorVisible()
            hbox_v1.addWidget(self.brower)
    
            hbox_v2 = QVBoxLayout()
    
            hbox_v2_g1 = QGridLayout()
            self.ip_label = QLabel()
            self.ip_label.setText('ip地址 ')
            self.ip_txt = QLineEdit()
            self.ip_txt.setPlaceholderText('0.0.0.0')
    
            self.port_label = QLabel()
            self.port_label.setText('端口 ')
            self.port_txt = QLineEdit()
            self.port_txt.setPlaceholderText('4444')
    
            self.message = QTextEdit()
            self.message.setPlaceholderText('发送消息内容...')
    
            hbox_v2_g1.addWidget(self.ip_label, 0, 0, 1, 1)
            hbox_v2_g1.addWidget(self.ip_txt, 0, 1, 1, 1)
    
            hbox_v2_g1.addWidget(self.port_label, 1, 0, 1, 1)
            hbox_v2_g1.addWidget(self.port_txt, 1, 1, 1, 1)
    
            hbox_v2_g1.addWidget(self.message, 2, 0, 1, 2)
    
            self.start_btn = QPushButton()
            self.start_btn.setText('发送消息')
            self.start_btn.clicked.connect(self.start_btn_clk)
    
            hbox_v2.addLayout(hbox_v2_g1)
            hbox_v2.addWidget(self.start_btn)
    
            hbox.addLayout(hbox_v1)
            hbox.addLayout(hbox_v2)
    
            self.thread_ = ClientThread(self)
            self.thread_.message.connect(self.show_message)
    
            self.setLayout(hbox)
    
        def show_message(self, text):
            '''
            槽函数:向文本浏览器中写入内容
            :param text:
            :return:
            '''
            cursor = self.brower.textCursor()
            cursor.movePosition(QTextCursor.End)
            self.brower.append(text)
            self.brower.setTextCursor(cursor)
            self.brower.ensureCursorVisible()
    
        def start_btn_clk(self):
            self.thread_.start()
    
    
    class ClientThread(QThread):
        message = pyqtSignal(str)
    
        def __init__(self, parent=None):
            super(ClientThread, self).__init__(parent)
            self.parent = parent
            self.working = True
            self.is_connect = False
    
        def __del__(self):
            self.working = False
            self.wait()
    
        def run(self):
            try:
                if self.is_connect is False:
                    self.connect_serv()
                # 将控制台输入消息进行 utf-8 编码后发送
                self.socket_client.send(self.parent.message.toPlainText().strip().encode('utf-8'))
                self.message.emit(self.socket_client.recv(1024).decode('utf-8'))
            except Exception as e:
                self.message.emit("发送消息异常:" + str(e))
    
        def connect_serv(self):
            try:
                self.message.emit("正在创建客户端socket...")
                # 创建客户端 socket
                self.socket_client = socket.socket()
                # 连接服务端
                address = (self.parent.ip_txt.text().strip(), int(self.parent.port_txt.text().strip()))
                self.socket_client.connect(address)
                self.message.emit("服务端连接成功...")
                # 接收服务端消息并进行 utf-8 解码
                self.message.emit(self.socket_client.recv(1024).decode())
                self.is_connect = True
            except:
                self.is_connect = False
    
    
    if __name__ == '__main__':
        app = QApplication(sys.argv)
        w = CandyWindow.createWindow(ClientUI(), theme='blueGreen', title='socket 客户端  公众号:[Python 集中营]',
                                     ico_path='hi.ico')
        w.show()
        sys.exit(app.exec_())
    • 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

    2-3. 实现效果

    file

    【往期精彩】

    零配置python日志,安装即用!

    英语没学好到底能不能做coder,别再纠结了先学起来...

    数据清洗工具flashtext,效率直接提升了几十倍数!

    一个help函数解决了python的所有文档信息查看...

    python 自定义异常/raise关键字抛出异常

  • 相关阅读:
    uniapp使用request下载图片,并且显示出来
    《零基础作曲编曲完全教程》(合辑)崛起的卧室音乐人 编曲教程 作曲教程精进篇
    [资料] GM8284DD:LVDS转TTL/RGB视频转换IC
    I/O多路复用三种实现
    刷题记录:牛客NC13947Contest
    Profinet IO从站数据 转EtherCAT项目案例
    如何在 Windows 10/8.1/8/7 上无密码删除 Deep Freeze
    Java学习笔记---多态综合练习
    字节跳动流式数据集成基于Flink Checkpoint两阶段提交的实践和优化
    电源硬件设计----电源基础知识(2)
  • 原文地址:https://blog.csdn.net/chengxuyuan_110/article/details/125591222