pyqt5-4.界面與通信的異步實現(信號與槽)

方案1:信號與線程
程序啓動,創建一個線程(存活週期:直到軟件關閉),當點擊事件發生,發送信號,該信號連接兩個槽,A負責界面變化切換,B進行後臺通訊,B通訊結束,再通過信號將結果返回到界面切換,通過這種機制實現界面與通信的分離。
流程圖如下:

結論:
經過代碼測試,發現這個方案並非是異步的,而是同步的,原因是同時連接兩個槽,這個槽機制應該是一個列表,串行執行的,必然兩個槽的執行會存在先後問題,當一個阻塞,另一個也就阻塞了。

方案2:線程
程序啓動,當點擊事件發生,發送信號,該信號連接一個槽,槽負責界面變化切換,同時創建一個線程(存活週期:報文發送接收完成既關閉),線程進行後臺通訊,通訊結束,再通過信號將結果返回到界面切換,通過這種機制實現界面與通信的分離。線程中發送的數據要通過線程創建時傳入。
流程圖和方案一的流程圖是一樣的,這時的線程與界面就變成了異步了。
結論:
該方法雖然實現了界面切換與通訊的異步處理,但是每點擊一次按鈕,都需要一次線程的創建,而且對於一直保持通訊的心跳機制,還需要單獨起一個線程,可謂是花費巨大,感覺不是很好的方法

方案3:線程與隊列
爲了解決方案2中頻繁創建線程的問題,現在做如下改進,程序啓動,創建一個線程(存活週期:直到軟件關閉),在線程中創建多個隊列,線程監控隊列,隊列分別有信號隊列,信息發送隊列,當前界面位置隊列,當界面事件發生,去修改隊列,線程則監控隊列,取出隊列進行處理,處理之後將結果返回
結論:
這樣的處理機制避免了線程的頻繁創建,同時能存儲一些全局的重要信息,也實現了異步的效果。

線程通訊類

'''
@Author: chenjianwen
@Date: 2020-06-03 15:15:27
@LastEditTime: 2020-06-10 17:23:42
@LastEditors: Please set LastEditors
@Description: In User Settings Edit
@FilePath: \DsafeshareClient\communication_module.py
'''
# -*- coding: UTF-8 -*-
from Login_Pane import LoginPane
from Register_Pane import RegisterPane
from Main_Pane import MainMangerPane
from PyQt5.Qt import *

import threading,queue
import time
import sys,signal

import communication.uuser as uuser
import communication.ufile as ufile

from communication.sutils import socket as sutils
import communication.globalv as globalv

'''
@description: 
@param {type} 
@return: 
'''
class communicat_device (QObject,threading.Thread):
    '''
    需要將發送者的對象先進行註冊,然後使用通訊器進行數據發送
    通訊器應該是全局可見的,所有設備對象都有調用的權利
    '''
    send_obj = {}
    # recv_obj = {}

    '''
    @description: post 列表, 
    @param {type} 
    @return: 
    '''
    mess_list = []

    '''
    @description:將執行結果返回給界面進行處理 
    @param {type}第一個參數爲對應的那個指令的返回數據:例如login_recv
        第二個參數爲返回狀態值,參數取值如下:
        "dok":返回該字段,代表有數據返回,
        "disconnect": 網絡錯誤,第二,三參數爲空
        "ok": 返回該字段則表示數據正確,該報文沒有返回的數據,在報文裏,只有一個正確標識,
            第三個參數爲空
        "err":返回該字段則出現錯誤,第三個字段中則是錯誤信息
        第三個參數返回數據 
    @return: 
    '''
    sig_recv = pyqtSignal(str,str,str)
    '''
    @description: 
    @param {type} 功能欄的信號,id爲按下功能鍵的編號
    @return: 
    '''
    sig_recv_id = pyqtSignal(str,str,dict,int)

    def __init__(self, threadID, name):
        super(communicat_device,self).__init__()
        self.threadID = threadID
        self.name = name
        self.daemon = True
        self.exitFlag = False
        

    def __del__(self):
        self.wait()

    def exit(self):
        self.exitFlag = True

    def run(self):
        while not self.exitFlag:
            if len(self.mess_list) > 0:
                data = self.mess_list.pop(-1)
                meth = self.check_methon(data)
                if "login" == meth:
                    response = sutils.send_cmd(data)
                else:
                    response = sutils.send_cmd_sig(data)
                recv_mess = self.check_reply(meth,response)
                
            time.sleep(0.1)
    
    def post(self,data):
        self.mess_list.append(data)

    '''
    @description: 對要發送的數據進行處理,主要是查看mod字段,
        是哪一個報文,然後將報文記下來,在回覆數據中check_reply,通過該
        函數的返回值,進行分別對應處理 
    @param {type} 
    @return: 
    '''
    def check_methon(self,meth):
        if "login" == meth["mod"]:
            return "login"
        elif "webdirlist" == meth["mod"]:
            return "webdirlist"
        pass
    
    def check_reply(self,meth,recv_mess):
        if "login" == meth:
            if uuser.login(recv_mess) == 1:
                self.sig_recv.emit("login_recv","ok","")
            else:
                self.sig_recv.emit("login_recv","err","")
        elif "webdirlist" == meth:
            # if ufile.webdirlist(recv_mess) == 1:
            # print("xxxxxxxxxxxxx22222:%s",recv_mess)
            self.sig_recv_id.emit("webdirlist_recv","ok",recv_mess,0)
            # else:
            #     self.sig_recv_id.emit("webdirlist_recv","err","",0)
        pass

實例化調用,當有事件需要發送信息時,在事件clicked中,將數據報文加入到線程的信息列表中,然後線程進行發送,當發送完畢,通過線程中信號發送回來,在調用出用槽接受數據即可

globalv.gl_com_dev = tx_m.communicat_device(0,"xxxx")
globalv.gl_com_dev.start()

    def on_check_login_res(recv_mod,flag,mess):
        #無論是否正確,都應該關閉loading
        if "login_recv" == recv_mod and "ok" == flag :
            mian_pane.show()
            mian_pane.button_switch_clicked(0)
            login_pane.check_close_func()
            globalv.gl_menban.setVisible(True)
        else:
            login_pane.show_error_animation()
            globalv.gl_menban.setVisible(False)

    def check_login(account, pwd):
        globalv.gl_menban.setVisible(True)
        globalv.gl_menban.move(login_pane.pos())
        globalv.gl_com_dev.post(uuser.login_data(account,pwd,2))

    globalv.gl_com_dev.sig_recv.connect(on_check_login_res)
    login_pane.check_login_signal.connect(check_login)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章