我正在尝试构建一个非常简单的wxPython GUI来监视和显示外部数据。有一个按钮可以打开/关闭监控。打开监视时,GUI会使用实时数据更新几个wx StaticLabel。当监控关闭时,GUI会闲置。
我尝试构建它的方式是使用相当简单的Python Thread布局。单击“开始监视”按钮时,程序会生成一个线程,该线程使用实时信息更新标签。单击“停止监视”按钮时,将调用thread.join(),它应该停止。
启动功能正常工作,实时数据更新效果很好,但是当我点击“停止”时,整个程序会冻结。我在Windows 7 64位上运行它,所以我得到了通常的“此程序已停止响应”Windows对话框。
以下是相关代码:
class MonGUI(wx.Panel):
def __init__(self, parent):
wx.Panel.__init__(self, parent)
...
... other code for the GUI here ...
...
# Create the thread that will update the VFO information
self.monThread = Thread(None, target=self.monThreadWork)
self.monThread.daemon = True
self.runThread = False
def monThreadWork(self):
while self.runThread:
...
... Update the StaticLabels with info
... (This part working)
...
# Turn monitoring on/off when the button is pressed.
def OnClick(self, event):
if self.isMonitoring:
self.button.SetLabel("Start Monitoring")
self.isMonitoring = False
self.runThread = False
self.monThread.join()
else:
self.button.SetLabel("Stop Monitoring")
self.isMonitoring = True
# Start the monitor thread!
self.runThread = True
self.monThread.start()
我确信有更好的方法可以做到这一点,但我是GUI编程和Python线程的新手,这是我想出的第一件事。
那么,为什么单击按钮停止线程会使整个事情冻结?
答案 0 :(得分:2)
在wxPython中, GUI操作需要在主线程中进行。在代码中的某些位置,您从另一个线程调用GUI。
最简单的解决方案是使用wx.CallAfter()
。一行代码看起来像
wx.CallAfter(self.button.SetLabel, “Start Monitoring”)
然后在函数完成后从主线程调用self.button.SetLabel(“Start Monitoring”)。
还有其他方法可以解决这个问题,例如使用Python线程队列或wx.PostEvent,但是从CallAfter开始,因为它最简单。
其他问题也很重要,比如你无法重启同一个帖子,但使用CallAfter会停止崩溃。
答案 1 :(得分:1)
它可能挂在join([timeout])
上,它会阻塞调用线程,直到调用join()
方法的线程终止 - 正常或通过未处理的异常 - 或直到可选的超时发生。
你的线程中有一些内部循环,还是等待某些可能永远不会出现的数据源的阻塞调用?当我编写一个抓取COM端口数据的基本串行程序时,它有时会挂起,因为我的线程中的读取函数会阻塞,直到它有所作为。
我会在一些调试print
语句中查看最新情况。
修改强>
我还使用threading.Event()
而不是布尔标志,例如:
# in the init code...
self.runThread = threading.Event()
# when starting thread...
self.runThread.set()
self.monThread.start()
# in the thread...
while self.runThread.isSet():
pass # do stuff
# killing the thread...
self.runThread.clear()
self.monThread.join()
这不应该使它以不同的方式工作,但它是一种稍微更安全的方式。
答案 2 :(得分:1)
tom10有正确的想法,可以避免来自监视器线程的UI更新。
此外,在UI线程中使用阻止调用self.monThread.join()
可能不是一个好主意。如果您希望UI提供监视器线程实际结束的一些反馈,请让monThreadWorker在关闭之前发出wx.CallAfter()或wx.PostEvent()。
避免在您的UI线程中阻塞任何内容,并且您将避免死锁UI