I am new to WX, so I decided to make a program that will periodically write out a line of text to the screen based on an outside input. The basis of the program contains a basic window with the multiline text control covering the entire window. The only other method I have in the frame is to print out what ever the method listen_event
gets as a new line for the multiline TextCtrl. Thats it, as proof the code is below:
class Frame(wx.Frame):
def __init__(self):
wx.Frame.__init__(self, None,
title = 'Program',
size = (640, 480),
style = wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX | wx.MINIMIZE_BOX)
panel = wx.Panel(self)
self.textArea = wx.TextCtrl(parent = panel,
id = -1,
pos = (0, 0),
size = (-1, -1),
style = wx.TE_MULTILINE | wx.TE_READONLY | wx.TE_AUTO_URL)
def listen_event(self, data):
self.textArea.AppendText(data)
The listen event is is called periodically from another thread. All seems to be fine, the program works, however, every so often (more often than I would like) I get a massive dump of what reminds me of ObjectiveC error messages:
2013-06-21 20:11:47.820 Python[85638:420b] An uncaught exception was raised
2013-06-21 20:11:47.821 Python[85638:420b] NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds
2013-06-21 20:11:47.824 Python[85638:420b] (
0 CoreFoundation 0x00007fff92e2bf56 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff8fc0bd5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff92e2bd8a +[NSException raise:format:arguments:] + 106
3 CoreFoundation 0x00007fff92e2bd14 +[NSException raise:format:] + 116
4 Foundation 0x00007fff8fe93b20 -[NSMutableRLEArray replaceObjectsInRange:withObject:length:] + 132
5 AppKit 0x00007fff8d3e33f8 -[NSLayoutManager addTemporaryAttribute:value:forCharacterRange:] + 500
6 AppKit 0x00007fff8d7f9716 -[NSTextView _markTextEditedForRange:] + 1025
7 AppKit 0x00007fff8d7f8392 -[NSTextView insertText:replacementRange:] + 2400
8 AppKit 0x00007fff8d7f7a25 -[NSTextView insertText:] + 320
9 libwx_osx_cocoau-2.9.4.0.0.dylib 0x0000000101953b71 _ZN19wxNSTextViewControl9WriteTextERK8wxString + 257
10 libwx_osx_cocoau-2.9.4.0.0.dylib 0x00000001018bfb53 _ZN11wxTextEntry9WriteTextERK8wxString + 67
11 _core_.so 0x00000001014dec57 _wrap_TextEntryBase_AppendText + 199
12 Python 0x00000001000c1112 PyEval_EvalFrameEx + 22626
13 Python 0x00000001000c2d29 PyEval_EvalCodeEx + 2137
14 Python 0x00000001000c0b6a PyEval_EvalFrameEx + 21178
15 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
16 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
17 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
18 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
19 Python 0x00000001000c2d29 PyEval_EvalCodeEx + 2137
20 Python 0x00000001000c0b6a PyEval_EvalFrameEx + 21178
21 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
22 Python 0x00000001000c2d29 PyEval_EvalCodeEx + 2137
23 Python 0x000000010003da80 function_call + 176
24 Python 0x000000010000c5e2 PyObject_Call + 98
25 Python 0x000000010001ebcb instancemethod_call + 363
26 Python 0x000000010000c5e2 PyObject_Call + 98
27 Python 0x00000001000ba5f7 PyEval_CallObjectWithKeywords + 87
28 Python 0x0000000100100a63 t_bootstrap + 67
29 libsystem_c.dylib 0x00007fff933008bf _pthread_start + 335
30 libsystem_c.dylib 0x00007fff93303b75 thread_start + 13
)
2013-06-21 20:11:47.825 Python[85638:420b] *** Terminating app due to uncaught exception 'NSRangeException', reason: 'NSMutableRLEArray replaceObjectsInRange:withObject:length:: Out of bounds'
*** First throw call stack:
(
0 CoreFoundation 0x00007fff92e2bf56 __exceptionPreprocess + 198
1 libobjc.A.dylib 0x00007fff8fc0bd5e objc_exception_throw + 43
2 CoreFoundation 0x00007fff92e2bd8a +[NSException raise:format:arguments:] + 106
3 CoreFoundation 0x00007fff92e2bd14 +[NSException raise:format:] + 116
4 Foundation 0x00007fff8fe93b20 -[NSMutableRLEArray replaceObjectsInRange:withObject:length:] + 132
5 AppKit 0x00007fff8d3e33f8 -[NSLayoutManager addTemporaryAttribute:value:forCharacterRange:] + 500
6 AppKit 0x00007fff8d7f9716 -[NSTextView _markTextEditedForRange:] + 1025
7 AppKit 0x00007fff8d7f8392 -[NSTextView insertText:replacementRange:] + 2400
8 AppKit 0x00007fff8d7f7a25 -[NSTextView insertText:] + 320
9 libwx_osx_cocoau-2.9.4.0.0.dylib 0x0000000101953b71 _ZN19wxNSTextViewControl9WriteTextERK8wxString + 257
10 libwx_osx_cocoau-2.9.4.0.0.dylib 0x00000001018bfb53 _ZN11wxTextEntry9WriteTextERK8wxString + 67
11 _core_.so 0x00000001014dec57 _wrap_TextEntryBase_AppendText + 199
12 Python 0x00000001000c1112 PyEval_EvalFrameEx + 22626
13 Python 0x00000001000c2d29 PyEval_EvalCodeEx + 2137
14 Python 0x00000001000c0b6a PyEval_EvalFrameEx + 21178
15 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
16 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
17 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
18 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
19 Python 0x00000001000c2d29 PyEval_EvalCodeEx + 2137
20 Python 0x00000001000c0b6a PyEval_EvalFrameEx + 21178
21 Python 0x00000001000c1ebe PyEval_EvalFrameEx + 26126
22 Python 0x00000001000c2d29 PyEval_EvalCodeEx + 2137
23 Python 0x000000010003da80 function_call + 176
24 Python 0x000000010000c5e2 PyObject_Call + 98
25 Python 0x000000010001ebcb instancemethod_call + 363
26 Python 0x000000010000c5e2 PyObject_Call + 98
27 Python 0x00000001000ba5f7 PyEval_CallObjectWithKeywords + 87
28 Python 0x0000000100100a63 t_bootstrap + 67
29 libsystem_c.dylib 0x00007fff933008bf _pthread_start + 335
30 libsystem_c.dylib 0x00007fff93303b75 thread_start + 13
)
terminate called throwing an exception
What gives? I cannot find a rhyme or reason for these errors. On occasion it works for a couple in a row, then crashes. And sometimes it crashes on the first go. Am I implementing the text control box incorrectly?
This may or may not be the reason for your crash, but if definitely will cause crashes, and they're likely to be exactly the kind of crash you're seeing (sometimes it works, sometimes it fails, sometimes it works for a while and then suddenly fails even though nothing visible has changed…).
You cannot call methods that operate on UI objects from other threads. Every time you call self.textArea.AppendText
from another thread, there is a chance of crashing—or, more fun, corrupting memory or other resources leading to a crash later.
There are a few different ways around this:
PostEvent
to queue an event for the main UI thread (with the actual UI-changing code in the event handler).CallAfter
or CallLater
to schedule a function to be called on the main UI thread (with the actual UI-changing code inside that function).pubsub
framework.threading
module, or a pipe, or whatever) to signal the main UI thread.(Under the covers, CallAfter
is a wrapper around PostEvent
, and CallLater
is a wrapper around a timer plus CallAfter
, and the timer and threading
are both wrappers around the same native thread APIs, and so on, so this isn't as many different possibilities as it seems…)
Anyway, the most trivial change to your code is:
def listen_event(self, data):
wx.CallAfter(self.textArea.AppendText, data)
On top of that, you're violating the first rule of multithreaded programming: shared mutable objects must only be accessed under locks.
I don't think this is causing your problem here. Since self
will never change attributes after creation, and (assuming you're using CPython, or some other Python implementation with a GIL) there's nothing else that can happen non-atomically that can affect you, you'll get away with it here. But if you don't understand why your code (other than the wx
calls) is thread-safe, you shouldn't count on it.