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"
|