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.
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.