bli*_*ann 6 python multithreading wxpython
这只发生在Linux上(也可能是OS X,无法测试atm),在Windows上运行正常.
我有一个wx.ProgressDialog与主线程产生.我将工作发送到另一个线程,它会定期回调主线程中的回调函数,该函数将更新ProgressDialog,或者在工作结束时将其销毁.但是,当发生这种情况时,我在Linux上收到一条有趣的消息:
(python:12728): Gtk-CRITICAL **: IA__gtk_window_set_modal: assertion 'GTK_IS_WINDOW (window)' failed
该对话框确实关闭,但如果我再次尝试生成它,它看起来已经差不多完成了.有时,seg错误也会跟随此消息.
我试图在这里使用精简版本来模拟它:
import wxversion
wxversion.select("2.8")
import wx
import sys
import threading
MAX_COUNT = 100
## This class is in a different area of the codebase and
class WorkerThread(threading.Thread):
def __init__(self, callback):
threading.Thread.__init__(self)
self.callback = callback
def run(self):
# simulate work done. IRL, this calls another function in another
# area of the codebase. This function would generate an XML document,
# which loops through a list of items and creates a set of elements for
# each item, calling back after each item. Here, we simply set up a for
# loop and simulate work with wx.MilliSleep
for i in xrange(MAX_COUNT):
print i
wx.MilliSleep(30)
wx.CallAfter(self.callback, i)
# Send done signal to GUI
wx.CallAfter(self.callback, -1)
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(350,200))
panel = wx.Panel(self)
box = wx.BoxSizer(wx.VERTICAL)
m_btn = wx.Button(panel, wx.ID_ANY, "Run Stuff")
m_btn.Bind(wx.EVT_BUTTON, self.OnRunButton)
box.Add(m_btn, 0, wx.ALL, 10)
panel.SetSizer(box)
panel.Layout()
def OnRunButton(self, event):
self.progressDialog = wx.ProgressDialog("Doing work",
"Doing Work",
maximum=MAX_COUNT, parent=self,
style=wx.PD_APP_MODAL | wx.PD_ELAPSED_TIME)
self.worker(self.threadCallback)
self.progressDialog.ShowModal()
def worker(self, callback):
# This bit is in another part of the codebase originally. In the test,
# I could have added it to OnRunButton, but I wanted function calls to
# be similar between test and actual code
thread = WorkerThread(callback)
thread.start()
def threadCallback(self, info):
# We update based on position, or destroy if we get a -1
if info == -1:
self.progressDialog.Destroy()
else:
self.progressDialog.Update(info)
app = wx.App(redirect=False)
top = Frame("ProgressDialog Test")
top.Show()
app.MainLoop()
Run Code Online (Sandbox Code Playgroud)
(我们选择2.8,但理想情况下,任何修复都应该在2.8和3.0中都有效.由于3.0版本的错误,我实际上无法在linux中测试3.0)
这在表示问题方面做得很好:在Windows中工作正常,但在尝试销毁进度对话框时出现seg错误.但是,我无法通过示例来展示GTK_IS_WINDOW
我试过寻找解决方案.我已经读过,这可能是因为工作线程完成得太快,因此在GUI中留下了一些消息.我不确定我是否完全理解这一点(从未得到过收益和消息等等),但我认为这意味着当工人处于100%时,ProgressDialog(变慢)可能只是在75%,并且还有额外25%的消息用于"更新"GUI,而是被销毁.
如果我理解正确与否,我想澄清一下.
另外,我认为.Hide()可以解决这个问题,但是我想要去除它,因为这是正确的做法.
无论如何,任何帮助将不胜感激.=)
我已经尝试过你的代码,也尝试了许多修改来解决这个问题,但失败了。无论如何,我创建了以下 wxPython 脚本来实现您的目的,请参见下文:
import wxversion
wxversion.select("2.8") # version 3.0 works, too.
import wx
import sys
import threading
import time
MAX_COUNT = 200
class WorkerThread(threading.Thread):
def __init__(self, target, countNum):
threading.Thread.__init__(self, target = target)
self.setDaemon(True)
self.cnt = countNum
self.target = target
self.pb = self.target.pb
def run(self):
for i in xrange(self.cnt):
print i+1
wx.MilliSleep(50)
wx.CallAfter(self.pb.SetValue, i+1)
wx.CallAfter(self.target.MakeModal, False)
wx.CallAfter(self.target.Close)
class ProgressBarFrame(wx.Frame):
def __init__(self, parent, title, range = 100) :
wx.Frame.__init__(self, parent = parent, title = title)
self.range = range
self.createProgressbar()
self.SetMinSize((400, 10))
self.Centre()
self.Show()
self.t0 = time.time()
self.elapsed_time_timer.Start(1000)
def createProgressbar(self):
self.pb = wx.Gauge(self)
self.pb.SetRange(range = self.range)
self.elapsed_time_st = wx.StaticText(self, label = 'Elapsed Time:')
self.elapsed_time_val = wx.StaticText(self, label = '00:00:00')
vbox_main = wx.BoxSizer(wx.VERTICAL)
hbox_time = wx.BoxSizer(wx.HORIZONTAL)
hbox_time.Add(self.elapsed_time_st, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5)
hbox_time.Add(self.elapsed_time_val, 0, wx.ALIGN_LEFT | wx.EXPAND | wx.ALL, 5)
vbox_main.Add(self.pb, 0, wx.EXPAND | wx.ALL, 5)
vbox_main.Add(hbox_time, 0, wx.EXPAND | wx.ALL, 5)
self.SetSizerAndFit(vbox_main)
self.elapsed_time_timer = wx.Timer(self)
self.Bind(wx.EVT_TIMER, self.onTickTimer, self.elapsed_time_timer)
def onTickTimer(self, event):
fmt='%H:%M:%S'
self.elapsed_time_val.SetLabel(time.strftime(fmt, time.gmtime(time.time()-self.t0)))
class Frame(wx.Frame):
def __init__(self, title):
wx.Frame.__init__(self, None, title=title, pos=(150,150), size=(350,200))
panel = wx.Panel(self)
box = wx.BoxSizer(wx.VERTICAL)
m_btn = wx.Button(panel, wx.ID_ANY, "Run Stuff")
self.Bind(wx.EVT_BUTTON, self.OnRunButton, m_btn)
box.Add(m_btn, 0, wx.ALL, 10)
panel.SetSizer(box)
def OnRunButton(self, event):
self.progressbar = ProgressBarFrame(self, 'Working Processing', MAX_COUNT)
self.progressbar.MakeModal(True)
worker = WorkerThread(self.progressbar, MAX_COUNT)
worker.start()
app = wx.App(redirect=False)
top = Frame("ProgressDialog Test")
top.Show()
app.MainLoop()
Run Code Online (Sandbox Code Playgroud)
我用来wx.Gauge做的wx.ProgressDialog事情,以及额外的wx.Timer显示经过的时间。MakeModal()方法用于模仿ShowModal默认样式Dialog显示的效果,不要忘记释放模态状态,MakeModal(False)否则框架将被冻结。您可以在课堂上添加更多内容ProgressBarFrame。
我认为段错误可能是由事件调用引起的,特别是当multithreading涉及问题时,也许仔细检查类wx.ProgressDialog会显示一些线索。
