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. 实现过程

在服务端的业务实现上面,我们依然是按照之前的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-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

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_())

2-3. 实现效果

file

【往期精彩】

file

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

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

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

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

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

本站文章如无特殊说明,均为本站原创,如若转载,请注明出处:GUI 应用:socket 网络聊天室 - Python技术站

(0)
上一篇 2023年4月2日
下一篇 2023年4月2日

相关文章

  • python做了个自动关机工具,再也不会耽误我下班啦

    上班族经常会遇到这样情况,着急下班结果将关机误点成重启,或者临近下班又通知开会,开完会已经迟了还要去给电脑关机。 【阅读全文】 今天使用PyQt5做了个自动关机的小工具,设置好关机时间然后直接提交即可,下班就可以直接走人了。 有直接需要.exe可执行应用的话,直接到文末处获取下载链接! 自动关机小工具也支持了清除已经设置好的关机时间,防止已经设置好了关机时间…

    2023年4月2日
    00
  • 用python为心爱的人制作520照片墙,已成功做出效果图

    马上又要进入一年一度的520了,作为一个地地道道的程序猿心里慌得一批。除了吃饭买礼物看电影好像就没有更多的想法了,于是想想将女友从以前到现在的所有照片整理了一下准备制作一个前所未有的照片墙。 【阅读全文】 当然,这里我使用的照片是从百度上面拿下来的,私人照片不能公开,哈哈~ 没有女朋友的也可以试试,毕竟万一哪天就有了呢! 话不多说,进入正题吧,先将需要的非标…

    2023年4月2日
    00
  • 记住这些windows网络操作命令,轻松搞定自己的电脑网络!

    windows操作系统中的网络操作主要是网络的配置、网络连接的监听、网络的路由跟踪、DNS服务器解析等等。 【阅读全文】 针对上面的这几项操作,整理了关于windows网络操作相关的使用方法。 1、网络配置 网络配置主要有ip地址、子网掩码、默认网关、DNS服务器地址,一般情况下,在公网的状态下这些地址都是自动获取的不需要我们进行特别的配置。 但是,在我们的…

    2023年4月2日
    00
  • 英语没学好到底能不能做coder,别再纠结了先学起来

    其实,编程中用到的英文词汇并不多,经常用到的写着写着就记住了。大多数时候只有给变量或者对象起名的时候才会去Google上查。于是,我将经常在coding中用到的词汇总结了一下。 【阅读全文】 A字母开头的英文词汇 Appearance外表assert/assertion异常add添加append附加args/argument参数attribute属性 B字母…

    2023年4月2日
    00
  • tabulate结合loguru打印出美观又方便查找的日志记录!

    在开发过程中经常碰到在本地环境无法完成联调测试的情况,必须到统一的联机环境对接其他系统测试。往往是出现了BUG难以查找数据记录及时定位到错误出现的位置。 【阅读全文】 面对这种情况可能情况可能是一个简单的BUG导致的,但是定位问题往往就需要很长的时间。在python编程中推荐非标准库tabulate,它可以将程序运行过程中产生的数据记录格式化的打印出来很方便…

    Python开发 2023年4月2日
    00
  • 懒人python操作,代码中永远只需要导入一个库

    Pyforest是一个开源的Python库,可以自动导入代码中使用到的Python库。 【阅读全文】 实话说,作为一个程序员还是得不停的学习呀。昨天晚上睡觉之前就在论坛上面溜达了一会儿,发现了有个叫pyforest的python非标准库可以自动导入代码中使用到的Python库,我竟然还不知道。 于是,迫不及待的测试了一下还真行,真是拯救了我们这些懒人。事情的…

    2023年4月2日
    00
  • 如何实现根据照片获取地理位置及如何防御照片泄漏地理位置

    【阅读全文】 首先,说明一下python确实可以根据照片获取地理位置,但是也是有一定的限制条件的。 获取照片地理位置的实现思路是这样的:通过提取照片中的经纬度信息。然后通过经纬度信息找到具体的地理位置信息。 安装可以读取经纬度信息的python非标准库exifread pip install exifread 将该模块导入到当前代码块中。 import ex…

    2023年4月2日
    00
  • python打包技巧:彻底解决pyinstaller打包exe文件太大的问题

    之前也写过很多的小工具,粉丝朋友们应该都知道在本公众号内回复任意关键字即可获取以往的工具源码或是exe可执行应用。 【阅读全文】 因为以往发过的小工具基本都是几十MB大小的exe应用,也就没有在意exe太大给大家带来的困扰,今天就准备来彻底的解决一下这个问题。 其实每次打包的exe的应用过大无非就是两个方面的语言因导致的,一是我们在开发的代码的过程中为了简便…

    2023年4月2日
    00
合作推广
合作推广
分享本页
返回顶部