消费机 水控机 售饭机 电梯门禁 门禁机
快速寻找产品(请输入产品型号或名称的关键词):
首页> 技术文档

技术文档

解决Python子线程更新UI窗口显示经常卡顿、卡死、奔溃等问题

发布者:广州荣士电子有限公司         发布时间: 2022-10-14 

   Python使用PyQt5做UI界面,开启子线程侦听UDP端口,端口接收到网络读卡器的读卡数据后刷新UI界面显示接收数据,解析数据包信息并向读卡器发送显示文字、驱动读卡器播报语音、蜂鸣响声提示、开启继电器开关等操作。

   在接收数据的子线程内如果直接更改UI窗口控件来显示信息,会产生显示信息刷新不及时、造成显示卡顿、卡死或软件直接奔溃等问题,产生原因是PyQt5中,数据接收处理子线程内是不能刷新UI显示线程的,必须使用创建信号,触发时将显示信号传送给槽函数来刷新UI的方式,代码如下:

class SockListenThread(QThread):      #Socket端口侦听线程
    Sock_data = pyqtSignal(int,str)   # 创建一个信号,触发时传递显示信息给槽函数

    def run(self):
        while listen==1:
            try:
                data, addr = s.recvfrom(1024)  #UDP端口接收到数据
                RemortIPort = '%s:%s' % addr
                self.Sock_data.emit(1,RemortIPort)

                GetData = 'FromIP:%s:%s' % addr + '    Data:'
                for num in range(0, len(data)):
                    GetData = GetData + '%02X ' % (data[num])
                self.Sock_data.emit(2,GetData)
            except:
                self.Sock_data.emit(2, 'The socket is being reopened')

在UI界面初始化内绑定槽函数:

    self.subSockListenThread=SockListenThread() 
    self.subSockListenThread.Sock_data.connect(self.SockGetData) 
    self.subSockListenThread.start()

槽函数更新UI
    def SockGetData(self,dispcode,Getdata):
        if(dispcode==1):
            self.textEdit_7.setText(Getdata)
        elif(dispcode==2):
            self.listWidget.addItem(Getdata)
            self.ListBottom() 


   其实这不仅仅Python、Pyqt5开发UI界面时要这样处理,Visual Studio C#、vb.net等多线程开发工具,在子线程更新UI窗口显示信息时,都要用类似的方式,虽然有个 CheckForIllegalCrossThreadCalls = false 设置来强制刷新UI,但是微软并不推荐这样使用(测试发现有时会出现刷新不了的情况

C#线程内更新UI界面使用委拖的方式:

delegate void Update1(string text1,string text2);   //线程内更新UI委拖

public  void ThrListener()    //UDP端口侦听线程
{
    while (ready)
     {
        try
        {
            EndPoint RemotePoint = new IPEndPoint(System.Net.IPAddress.Any, 0);
            byte[] bytes = new byte[1024];
            int NumGet = ListenerSock.ReceiveFrom(bytes, ref RemotePoint);

            string Msg = Encoding.GetEncoding(936).GetString(bytes, 0, NumGet);

            string dispstr = DateTime.Now.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss") + "Data:";
            RemoteIPoint =(IPEndPoint)RemotePoint;           //获取数据包来源IP及端口,原路回应
            this.BeginInvoke(new Update1(EditUi), dispstr, Msg);  //显示接收到的数据包,并根据情况回应设备
        }
        catch(Exception ex )
        {
            this.BeginInvoke(new Update1(EditUi), DateTime.Now.ToLocalTime().ToString("yyyy-MM-dd HH:mm:ss")+" ",ex.Message  );
        }
    }
 }

private void EditUi(string text1, string Msg)     //刷新显示过程,参数类型必须委托定义一致
 {
    if (ListBox1.Items.Count > 50) { ListBox1.Items.Clear();}
    ListBox1.Items.Add(text1 + Msg);
    ListBox1.SelectedIndex = ListBox1.Items.Count - 1;
}


VB.Net线程内更新UI界面使用委拖的方式:

Delegate Sub EditUi(ByVal data0 As String, ByVal data1 As String)  '线程内更新UI传送两个参数

Private Sub ThrListener()     '侦听线程      
    While ready
        Try
            Dim bytes(1024) As Byte
            Dim dataArray() As String
            Dim RemotePoint As System.Net.EndPoint = New System.Net.IPEndPoint(System.Net.IPAddress.Any, 0)
            Dim NumGet As Integer
            Dim Msg As String

            NumGet = ListenerSock.ReceiveFrom(bytes, RemotePoint)
            Msg = Encoding.GetEncoding(936).GetString(bytes, 0, NumGet)

            Me.Invoke(New EditUi(AddressOf EditUiNow), Now() & (" FromIP:" & Convert.ToString(RemotePoint) + "          ").Substring(0, 30) & "Data:", Msg) '用Invoke跨线程更新UI      

            Me.Invoke(New EditTC(AddressOf EditTCNow), 3, Convert.ToString(RemotePoint)) '用Invoke跨线程更新UI

        Catch ex As Exception
            Me.Invoke(New EditTC(AddressOf EditTCNow), 2, "ERROR:" & vbCrLf & ex.GetHashCode & ex.Message & vbCrLf)
        End Try
    End While
End Sub

Private Sub EditTCNow(ByVal con As Integer, ByVal dispinf As String) '这里要和委托定义时的参数保持一致
        Select Case con
            Case 1
                TextBox3.Text = dispinf
            Case 2
                ListBox1.Items.Add(dispinf)
                ListBox1.SelectedIndex = ListBox1.Items.Count - 1
            Case 3
                TextBox1.Text = dispinf
        End Select
End Sub

    怀念VB6单线程编程,直接刷新窗口显示的时代!  Text1.text="hello world"

 
上一篇:NTAG213卡、NTAG215卡和NTAG216卡的区别 下一篇:NFC简介
     
Guangzhou Rong Shi Electronics Co., Ltd., China 广州荣士电子有限公司 备案/许可证编号:粤ICP备11063836号
TEL  020-22307058    020-82301718
消费机
隐私政策

消费机 水控机 售饭机 电梯门禁 门禁机

网站地图 xml