Wx.ProgressDialog causes seg crashes and / or GTK_IS_WINDOW crashes when destroyed

This only happens on Linux (maybe OS X also cannot test atm), it works fine on Windows.

I have a wx.ProgressDialog that is created using the main thread. I send the work to another thread, and it periodically calls back the callback function in the main thread, which will update the ProgressDialog or, at the end of the work, destroy it. However, when this happens, I get an interesting Linux post:

(python:12728): Gtk-CRITICAL **: IA__gtk_window_set_modal: assertion 'GTK_IS_WINDOW (window)' failed

The dialog closes, but if I try to create it again, it seems to be almost done. Sometimes a seg error also follows with this message.

I tried to simulate it with a stripped down version here:

 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() 

(we choose 2.8, but ideally, any fix should work in both 2.8 and 3.0. I really could not test it in 3.0 on Linux due to poor build 3.0)

This is a good job at presenting the problem: works fine on Windows, but seg fault when it tries to destroy the progress dialog. However, I cannot give an example to show GTK_IS_WINDOW

Ive tried to find a solution. I read that this may be due to the fact that the workflow ends too quickly and thus leaves the GUI with some messages in the queue. I’m not sure I fully understand this (I never got access to income and messages, etc.), but I believe that this means that when an employee is 100%, ProgressDialog (slower) can be only 75%, and still there is an additional 25% of the messages that need to be used to β€œupdate” the GUI, but are destroyed instead.

I would like clarification as to whether I understand this correctly.

In addition, I believe .Hide() works like a job, but I would like to destroy it because it needs to be done.

Regardless, any help would be greatly appreciated. =)

+6
source share
1 answer

I tried your code, and also many modifications tried to solve this problem, but failed. Anyway, I created the following wxPython script to complete your task, see below:

 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() 

I use wx.Gauge to do what wx.ProgressDialog , as well as an additional wx.Timer , to show elapsed time. The MakeModal() method is used to simulate the MakeModal() effect, which is the default style displayed by Dialog , do not forget to release the MakeModal(False) modality MakeModal(False) or the frame will be frozen. You can add more things to the ProgressBarFrame class.

I think that a segment failure error can occur as a result of event wx.ProgressDialog , especially when the multithreading problem is related, perhaps a thorough inspection in the wx.ProgressDialog class wx.ProgressDialog show some key.

Screenshot of progressbar demo

+1
source

Source: https://habr.com/ru/post/989454/


All Articles