方案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)