2015-06-20 72 views
6

這只是發生在Linux(可能OS X也不能ATM測試),工作正常的Windows。wx.ProgressDialog造成賽格故障和/或GTK_IS_WINDOW故障時被銷燬

我有一個催生與主線程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() 

(我們選擇2.8,但理想的任何修補程序應該在這兩個2.8和3.0工作,我其實一直沒能在Linux 3.0的測試由於壞的3.0版本)

這做得很好,在表示問題:在Windows中工作正常,但是當它試圖摧毀進度對話框賽格故障。但是,我不能得到的例子來說明GTK_IS_WINDOW

我用盡尋找解決方案。我讀過這可能是由於工作線程完成得太快的事實,從而使GUI在隊列中留下了一些消息。我不確定我是否完全理解了這一點(從來沒有得到良率和消息的等待),但是我認爲這意味着當員工達到100%時,ProgressDialog(速度較慢)可能只會在75%,仍然有額外的25%的消息用於「更新」GUI,但相反會被破壞。

我想澄清一下,如果我是否正確理解。

而且,我相信.Hide()作品作爲變通,但我想摧毀它,而不是因爲這是做正確的事。

無論如何,任何幫助將不勝感激。 =)

回答

1

我試過你的代碼,也嘗試過很多修改來解決這個問題,但是失敗了。無論如何,我創建了以下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() 

我使用wx.Gauge做什麼wx.ProgressDialog呢,還有一個額外的wx.Timer顯示經過的時間。 MakeModal()方法用於模仿ShowModal效果這是默認的風格,Dialog節目,不要忘記MakeModal(False)釋放的模態或框架將被凍結。您可以在ProgressBarFrame課程中添加更多內容。

我在想事件調用可能會引起段錯誤錯誤,特別是當涉及multithreading問題時,可能仔細檢查wx.ProgressDialog類會顯示一些線索。

Screenshot of progressbar demo