问题描述
我在 GUI 中使用 wxPython,并且我有一个与某些 I/O 交互的不同线程。当线程从 I/O 设备接收事件时,有没有办法更新 GUI 以显示 I/O 状态? 更清楚一点:IOthread 每 3 秒向停车栏询问它是向上还是向下,我希望 GUI 在向下时显示红色 LED 或向上时显示绿色 LED。所以基本上我需要将 IOthread 接收到的信息传达给运行在主线程中的 GUI
解决方法
您可以看一看 pubsub
,但是当您将 wxPython 与线程一起使用时,使用用户定义的 event
会更容易。
这个改编的代码,只是在 True 和 False 之间交替,显然你会改变它,并且只有在它改变时才发布事件。
from threading import Thread
import wx
import time
import wx.lib.newevent
progress_event,EVT_PROGRESS_EVENT = wx.lib.newevent.NewEvent()
class WorkThread(Thread):
def __init__(self,parent_target):
"""Init Worker Thread Class."""
Thread.__init__(self)
self.target = parent_target
self.stop_work_thread = 0
self.start() # start the thread
def run(self):
curr_loop = True
while True:
if self.stop_work_thread == 1: # Exit
break
time.sleep(1)
if self.stop_work_thread == 2: # Paused
continue
curr_loop = not curr_loop
evt = progress_event(active=curr_loop,name=self.name)
#Send back current bar position up True,Down False
try:
wx.PostEvent(self.target,evt)
except: # The parent frame has probably been destroyed
self.stop()
return
def stop(self):
self.stop_work_thread = 1
def pause(self):
if self.stop_work_thread == 2:
self.stop_work_thread = 0
self.target.pause_btn.SetLabel('Pause')
else:
self.stop_work_thread = 2
self.target.pause_btn.SetLabel('Paused')
class Progress(wx.Frame):
def __init__(self,parent,title):
super(Progress,self).__init__(parent,title = title,size = (500,300))
left_sizer = wx.BoxSizer(wx.VERTICAL)
middle_sizer = wx.BoxSizer(wx.HORIZONTAL)
self.panel = wx.Panel(self)
self.start_btn = wx.Button(self.panel,label="Start")
self.stop_btn =wx.Button(self.panel,label="Stop")
self.pause_btn =wx.Button(self.panel,label="Pause")
self.quit_btn =wx.Button(self.panel,label="Quit")
self.logger = wx.TextCtrl(self.panel,size=(200,200),style=wx.TE_MULTILINE | wx.TE_READONLY)
self.stop_btn.Disable()
self.pause_btn.Disable()
left_sizer.Add(self.start_btn,wx.EXPAND)
left_sizer.Add(self.stop_btn,wx.EXPAND)
left_sizer.Add(self.pause_btn,wx.EXPAND)
left_sizer.Add(self.quit_btn,wx.EXPAND)
middle_sizer.Add(self.logger,wx.EXPAND)
self.mainsizer = wx.BoxSizer(wx.HORIZONTAL)
self.mainsizer.Add(left_sizer)
self.mainsizer.Add(middle_sizer)
self.panel.SetSizer(self.mainsizer)
self.Layout()
self.start_btn.Bind(wx.EVT_BUTTON,self.onStart)
self.stop_btn.Bind(wx.EVT_BUTTON,self.onCancel)
self.pause_btn.Bind(wx.EVT_BUTTON,self.onPause)
self.quit_btn.Bind(wx.EVT_BUTTON,self.onExit)
#Bind to the progress event issued by the thread
self.Bind(EVT_PROGRESS_EVENT,self.OnActive)
self.Bind(wx.EVT_CLOSE,self.onExit)
def OnActive(self,event):
if not self.work.is_alive():
return
active = event.active
ident = event.name
if active:
self.logger.Clear()
self.logger.SetBackgroundColour("green")
self.logger.write('\n\n\n\n\n\t\tBar is Up')
else:
self.logger.Clear()
self.logger.SetBackgroundColour("red")
self.logger.write('\n\n\n\n\n\t\tBar is Down')
def onStart(self,event):
self.start_btn.Disable()
self.work = WorkThread(parent_target=self)
self.pause_btn.Enable()
self.stop_btn.Enable()
def onPause(self,event):
if self.work.is_alive():
self.work.pause() # Pause the thread
def onCancel(self,event):
"""Cancel thread process"""
try:
self.work.stop()
self.work.join()
except:
pass
self.onFinish()
def onFinish(self):
"""thread process finished - clean up"""
self.start_btn.Enable()
self.stop_btn.Disable()
self.pause_btn.Disable()
self.pause_btn.SetLabel("Pause")
self.logger.Clear()
self.logger.SetBackgroundColour("white")
def onExit(self,event):
self.onCancel(None)
self.onFinish()
self.Destroy()
app = wx.App()
frame = Progress(None,'Bar Up/Down Display')
frame.Show()
app.MainLoop()