GUI 應用:socket 網路聊天室

来源:https://www.cnblogs.com/lwsbc/archive/2022/07/03/16441267.html
-Advertisement-
Play Games

背景介紹 在程式中,主線程啟動一個子線程進行非同步計算,主線程是不阻塞繼續執行的,這點看起來是非常自然的,都已經選擇啟動子線程去非同步執行了,主線程如果是阻塞的話,那還不如主線程自己去執行不就好了。那會不會有一種場景,非同步線程執行的結果主線程是需要使用的,或者說主線程先做一些工作,然後需要確認子線程執行 ...


在這個周末剛剛寫出來的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關鍵字拋出異常

歡迎關註作者公眾號【Python 集中營】,專註於後端編程,每天更新技術乾貨,不定時分享各類資料!
您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 記錄我第一次使用Android Studio時遇到的問題以及一些簡單的筆記。 我所使用的是Android Studio 2.2版本 遇到的問題 創建一個Hello World!項目無疑是相當簡單的,我很快就完成了項目的創建過程。 然後……就報錯了。 Error:A problem occurred ...
  • 記錄使用typescript配合webpack打包一個javascript library的配置過程. 目標是構建一個可以同時支持`CommonJs`, `esm`, `amd`這個幾個js模塊系統的javascript庫, 然後還有一個單獨打包出一個css的樣式文件的需求. ...
  • jQuery 什麼是jQuery? jQuery是一個實用的JavaScript函數庫,jQuery極大地簡化了JS對DOM的操作,封裝並實現的一系列常用的方法。 jQuery庫封裝了JavaScript常用的功能代碼,提供一種簡便的JavaScript設計模式,優化HTML文檔操作、事件處理、動畫 ...
  • 微服務可以對你的企業產生積極的影響。因此,值得瞭解如何處理微服務架構(MSA)和一些微服務的設計模式,以及,微服務架構的一般目標或原則。以下是微服務架構方法中需要考慮的四個目標。 降低成本。MSA將降低設計、實施和維護IT服務的總體成本。 提高發佈速度:MSA將提高從想法到部署服務的速度。 提高複原 ...
  • springboot項目上傳存儲圖片到七牛雲伺服器 問題描述: 當圖片存在本地時會出現卡頓的現象。比如一篇圖文混排的文章,如果圖片沒有載入完,可能整個文章都顯示不出來,因為它們都是用的同一個伺服器。 但是如果把圖片單獨拿出來放在雲伺服器上進行載入,這樣圖片的載入和文字的載入互不幹擾,就可以優化這個問 ...
  • 促使我寫這篇文章主要是在寫一個關於虛擬貨幣賬戶監控的項目時使用 Ticker 的問題。 Ticker 的問題 如果用過 Ticker 的朋友會知道,創建 Ticker 後並不會馬上執行,而是會等待一個時間 d,這就是創建時的間隔時間。如果間隔時間很短這基本上不會有太大問題,但是如果對首次執行時間有要 ...
  • ​ 隨著互聯網的快速發展,各種組件層出不窮,需要框架集成的組件越來越多。每一種組件與Spring容器整合需要實現相關代碼。SpringMVC框架配置由於太過於繁瑣和依賴XML文件;為了方便快速集成第三方組件和減少對配置文件的依賴,SpringBoot應運而生,其中採用了約定大於配置的理論讓開發者不需 ...
  • 開發經常遇到分頁查詢的需求,但是當翻頁過多的時候,就會產生深分頁,導致查詢效率急劇下降。 有沒有什麼辦法,能解決深分頁的問題呢? 本文總結了三種優化方案,查詢效率直接提升10倍,一起學習一下。 ...
一周排行
    -Advertisement-
    Play Games
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...