Search code examples
multithreadingpython-2.7user-interfacewxpython

wxPython, Runtime and Assertion Error when closing mainframe


I have a GUI running some threads, up to now there are only four. 2 of them are running read routing, from the instrument to finishing uploading to SQL. The other two of them are monitoring the data being uploaded to the SQL Data base. When closing the mainframe i get an Runtimeerror, Both of them update the GUI.

Exception in thread Thread-3:
Traceback (most recent call last):
  File "C:\Python27\lib\threading.py", line 801, in __bootstrap_inner
    self.run()
  File "C:\Users\ACAS\PycharmProjects\ACAS\Main\GUI\panels\hygrometer_panel.py", line 42, in run
    wx.PostEvent(self.wxObject, ResultEvent(self.worker.read()))
RuntimeError: wrapped C/C++ object of type hygrometer has been deleted

I've tried in using wx.CallAfter lines. But the error only changed to an assertion error.

Exception in thread Thread-1:
Traceback (most recent call last):
  File "C:\Python27\lib\threading.py", line 801, in __bootstrap_inner
    self.run()
  File "C:\Users\ACAS\PycharmProjects\ACAS\Main\GUI\panels\acquisition_panel.py", line 20, in run
    self.method()
  File "C:\Users\ACAS\PycharmProjects\ACAS\Main\GUI\panels\acquisition_panel.py", line 73, in ccn_status_method
    wx.CallAfter(self.ccn_status.SetLabel, 'RUNNING')
  File "C:\Python27\lib\site-packages\wx\core.py", line 3254, in CallAfter
    assert app is not None, 'No wx.App created yet'
AssertionError: No wx.App created yet

Here some thread approaches. First

class TestThread(Thread):
    def __init__(self, wxObject):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.wxObject = wxObject
        self.worker = workers.hygrometer_worker()
        self.start()  # start the thread

    def run(self):
        """Run Worker Thread."""
        # This is the code executing in the new thread.
        while True:
            wx.PostEvent(self.wxObject, ResultEvent(self.worker.read()))
            time.sleep(1)

Second

class TestThread(Thread):
    def __init__(self, method):
        """Init Worker Thread Class."""
        Thread.__init__(self)
        self.method = method
        self.start()  # start the thread

    def run(self):
        """Run Worker Thread."""
        # This is the code executing in the new thread.
        while True:
            self.method()
            time.sleep(1)

This sending the method doing all the work,

    def update_display(self):
        data = self.worker.read()
        index = data.index.values[0]
        wx.CallAfter(self.current_ss_value.SetLabel, str(data.at[index, 'Current SS']))
        wx.CallAfter(self.stage_value.SetLabel, str(data.at[index, '1st Stage Mon']))
        wx.CallAfter(self.concentration_value.SetLabel, str(data.at[index, 'CCN Number Conc']))
        wx.CallAfter(self.delta_value.SetLabel, str(data.at[index, 'Delta T']))
        wx.CallAfter(self.cmd_value.SetLabel, str(data.at[index, 'CMD']))
        wx.CallAfter(self.gmd_values.SetLabel, str(data.at[index, 'GMD']))

I dont really seem the error. I've tried different things.


Solution

  • You can ensure that self.wxObject still exists before trying to use it by testing it with an if. The widget classes have an __int__ or __bool__ that return False if the C++ object has already been destroyed. So, something like this:

        if self.wxObject:
            wx.PostEvent(self.wxObject, ...)
    

    For the wx.App error you can test it too with wx.GetApp() and ensuring it is not None before using wx.CallAfter. Or you can just delay the starting of your worker threads until after the application object has been created.

    One more comment about your code: I recommend not issuing several wx.CallAfters in a row with each one doing an individual UI update. It's much better to move those statements to a new method, and then use wx.CallAfter to invoke just that method. It's much more efficient that way, but more importantly it avoids problems stemming from how the event system is implemented and the possibility of some of those CallAfters being executed before an earlier one returns.