在wxPython/python中,wx.CallAfter()导致应用崩溃的无限循环
我正在使用Python 2.7和wxPython 3.0,在Windows 7操作系统上工作。在下面的代码片段中,我实现了一个发布-订阅机制,用来更新我的图形用户界面(GUI)。我使用了wx.CallAfter()
来把更新发送到我的GUI主循环。
问题:为什么我提供的代码片段会导致我的应用程序崩溃?这是为什么呢?我该如何避免这种情况?
代码:代码中使用的图片可以从这里下载 blue.bmp 和 g.bmp。
import wx
import time
from wx.lib.pubsub import setupkwargs
from wx.lib.pubsub import pub
from threading import Thread
import threading
import random
class gui(wx.Frame):
def __init__(self, parent, id, title):
wx.Frame.__init__(self, None, id, title, size=(200,200), style=wx.DEFAULT_FRAME_STYLE ^ wx.RESIZE_BORDER)
self.mainPanel = mainPanel = wx.Panel(self, -1)
mySizer = wx.BoxSizer(wx.VERTICAL)
self.myButton1 = wx.Button(mainPanel, -1, size=(30,30))
mySizer.Add(self.myButton1)
mainPanel.SetSizer(mySizer)
Bimage_file = 'blue.bmp'
self.Blue = wx.Image(Bimage_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
Gimage_file = 'g.bmp'
self.Green = wx.Image(Gimage_file, wx.BITMAP_TYPE_ANY).ConvertToBitmap()
pub.subscribe(self.updateImages, 'Update')
def updateImages(self, value):
self.myButton1.DestroyChildren()
if value <= 5:
wx.StaticBitmap(self.myButton1, -1, self.Blue, (0, 0))
self.mainPanel.Layout()
else:
wx.StaticBitmap(self.myButton1, -1, self.Green, (0, 0))
self.mainPanel.Layout()
class myThread(Thread):
def __init__(self):
Thread.__init__(self)
self.start()
def run(self):
while True:
result = random.randrange(0,10)
wx.CallAfter(pub.sendMessage, 'Update', value=result)
#Uncomment the following line to avoid the crash!!!
#time.sleep(1)
if __name__=='__main__':
app = wx.App()
frame = gui(parent=None, id=-1, title="Demo")
frame.Show()
myThread()
app.MainLoop()
不过,如果我在wx.CallAfter()
之后添加以下这一行,程序就能正常工作:
time.sleep(1)
感谢你的时间!
2 个回答
0
也许你的线程应该有一个返回的回调函数,或者用一个变量(信号量)来知道 CallAfter 是否已经执行完毕?
把你的 pub.sendMessage 调用放在另一个函数里,这个函数可以在发送消息后清除一个信号量标志。你的线程可以在发出 CallAfter 之前设置信号量,然后在发送另一个消息之前等待信号量被清除。
如果你把它做成一个计数器的话,可以在设置时加一,在清除时减一,并且只允许计数器到达一定的上限,这样就能限制排队的 CallAfter 的数量。
3
这个问题可能是因为Windows的消息队列溢出了。关于这个bug,在wxWidgets的bug跟踪系统里有好几个相关的记录,其中一些问题已经修复了,但可能还不是全部。
无论如何,即使我们能总是避免溢出,像这样不断向主线程发送事件也是个坏主意(因为每次调用wx.CallAfter()
实际上就相当于发送了一个事件)。在实际情况下,后台线程会在这些事件之间做一些工作,所以这种情况不太可能发生,但如果你真的需要在这样的简单示例中让它工作,使用sleep()
可能是个不错的选择。