scrolling a StyledTextCtrl works on Windows but crashes on Linux (wxpython 2.9)

I have some code that adds lines to a STC and scrolls the window if necessary to ensure that the last line is visible. This works fine on Windows, but causes a core dump on our linux boxes. Both are running wxpython 2.9 (2.9.4.0 on windows and 2.9.4.1 on linux).

The STC is declared as follows:

self.outputCtrl = stc.StyledTextCtrl(panel)

numLogLines = 0

The code that adds the line and scrolls the window looks like this:

def __onUpdateDisplay(self, arg1):

self.outputCtrl.AddText(arg1)

self.numLogLines += 1

self.outputCtrl.ScrollToLine(self.numLogLines)

Removing the ScrollToLine bit permits the code to run fine under linux ( except you have to scroll the window manually).

When it crashes it prints this error in the console:

foo.py: Fatal IO error 11 (Resource temporarily unavailable) on X server localhost:10.0.

Hi,

···

On Fri, Feb 15, 2013 at 3:18 PM, patrick korsnick korsnick@gmail.com wrote:

I have some code that adds lines to a STC and scrolls the window if necessary to ensure that the last line is visible. This works fine on Windows, but causes a core dump on our linux boxes. Both are running wxpython 2.9 (2.9.4.0 on windows and 2.9.4.1 on linux).

The STC is declared as follows:

self.outputCtrl = stc.StyledTextCtrl(panel)

numLogLines = 0

The code that adds the line and scrolls the window looks like this:

def __onUpdateDisplay(self, arg1):

self.outputCtrl.AddText(arg1)

self.numLogLines += 1

self.outputCtrl.ScrollToLine(self.numLogLines)

Removing the ScrollToLine bit permits the code to run fine under linux ( except you have to scroll the window manually).

When it crashes it prints this error in the console:

foo.py: Fatal IO error 11 (Resource temporarily unavailable) on X server localhost:10.0.

Is the _onUpdateDisplay being called from another thread?

If so that is likely the problem as the UI is not threadsafe. Can work around this in many ways but easiest is likely to use CallAfter to delegate the call the main thread.

wx.CallAfter(__onUpdateDisplay, arg1)

Cody

HI Cody,

No, _onUpdateDisplay is registered with a pubsub receiver to receive the messages from the other thread, so we should be OK :slight_smile:

create a pubsub receiver for UpdateDisplay ‘topics’

pub.subscribe(self.__onUpdateDisplay, ‘UpdateDisplay’)

···

On Friday, February 15, 2013 2:18:58 PM UTC-7, patrick korsnick wrote:

I have some code that adds lines to a STC and scrolls the window if necessary to ensure that the last line is visible. This works fine on Windows, but causes a core dump on our linux boxes. Both are running wxpython 2.9 (2.9.4.0 on windows and 2.9.4.1 on linux).

The STC is declared as follows:

self.outputCtrl = stc.StyledTextCtrl(panel)

numLogLines = 0

The code that adds the line and scrolls the window looks like this:

def __onUpdateDisplay(self, arg1):

self.outputCtrl.AddText(arg1)

self.numLogLines += 1

self.outputCtrl.ScrollToLine(self.numLogLines)

Removing the ScrollToLine bit permits the code to run fine under linux ( except you have to scroll the window manually).

When it crashes it prints this error in the console:

foo.py: Fatal IO error 11 (Resource temporarily unavailable) on X server localhost:10.0.

Hi,

HI Cody,

No, _onUpdateDisplay is registered with a pubsub receiver to receive the messages from the other thread, so we should be OK :slight_smile:

create a pubsub receiver for UpdateDisplay ‘topics’

pub.subscribe(self.__onUpdateDisplay, ‘UpdateDisplay’)

Actually no, pubsub does not do any thread handling all send calls are made on the same thread as send is called on.

In very very simplistic terms what pubsub does can be expressed like the following (psudo code).

class Publisher:

_subscibers = { messageId : [ list of subscriber methods] }

def subscribe(callback, msgtype):

_subscribers[msgtype].append(callback)

def sendMessage(msgtype, msgdata):

for subscriber in _subscibers.get(msgtype):

subscriber(msgdata)

(of course it does allot more than this but in essence this is what happens)

Cody

···

On Fri, Feb 15, 2013 at 3:27 PM, patrick korsnick korsnick@gmail.com wrote:

Hmm, I must have misread something then- from the posts I read online it seemed that using pubsub was an approved way to share data between two threads.

So you’re saying the following is not a thread safe way to update a GUI:

thread 1:

pub.subscribe(self.__onUpdateDisplay, ‘UpdateDisplay’)

def __onUpdateDisplay(self, arg1):

thread 2:

pub.sendMessage(“UpdateDisplay”, arg1=“foo bar baz”)

···

On Friday, February 15, 2013 2:40:01 PM UTC-7, Cody Precord wrote:

Hi,

On Fri, Feb 15, 2013 at 3:27 PM, patrick korsnick kors...@gmail.com wrote:

HI Cody,

No, _onUpdateDisplay is registered with a pubsub receiver to receive the messages from the other thread, so we should be OK :slight_smile:

create a pubsub receiver for UpdateDisplay ‘topics’

pub.subscribe(self.__onUpdateDisplay, ‘UpdateDisplay’)

Actually no, pubsub does not do any thread handling all send calls are made on the same thread as send is called on.

In very very simplistic terms what pubsub does can be expressed like the following (psudo code).

class Publisher:

_subscibers = { messageId : [ list of subscriber methods] }

def subscribe(callback, msgtype):

_subscribers[msgtype].append(callback)

def sendMessage(msgtype, msgdata):

for subscriber in _subscibers.get(msgtype):

subscriber(msgdata)

(of course it does allot more than this but in essence this is what happens)

Cody

Hi,

···

On Fri, Feb 15, 2013 at 3:55 PM, patrick korsnick korsnick@gmail.com wrote:

Hmm, I must have misread something then- from the posts I read online it seemed that using pubsub was an approved way to share data between two threads.

So you’re saying the following is not a thread safe way to update a GUI:

thread 1:

pub.subscribe(self.__onUpdateDisplay, ‘UpdateDisplay’)

def __onUpdateDisplay(self, arg1):

thread 2:

pub.sendMessage(“UpdateDisplay”, arg1=“foo bar baz”)

Correct, the sendMessage calls are synchronous so they will be executed in the context of the calling thread.

You can go ahead and put a “print wx.Thread_IsMain()” in the method to see for yourself.

The usual quick workaround is as mentioned earlier to use CallAfter. In this case you could do something like this

wx.CallAfter(pub.sendMessage, “UpdateDisplay”, “foo bar baz”)

This will use an event to delegate the execution of the call to the main thread.

Cody

Wow thanks for clearing that up! So essentially pubsub doesn’t buy us any thread safety (the only reason we switched to using it). Thus we can get rid of the pubsub stuff and go back to the way we were doing it before: (passing the TextCtrl to the second thread and calling write()) but this time we’ll wrap it with a CallAfter-- wx.CallAfter(theTextCtrl.write(“foo bar baz”)) and if I understand correctly it should be thread safe.

···

On Friday, February 15, 2013 3:01:18 PM UTC-7, Cody Precord wrote:

Hi,

On Fri, Feb 15, 2013 at 3:55 PM, patrick korsnick kors...@gmail.com wrote:

Hmm, I must have misread something then- from the posts I read online it seemed that using pubsub was an approved way to share data between two threads.

So you’re saying the following is not a thread safe way to update a GUI:

thread 1:

pub.subscribe(self.__onUpdateDisplay, ‘UpdateDisplay’)

def __onUpdateDisplay(self, arg1):

thread 2:

pub.sendMessage(“UpdateDisplay”, arg1=“foo bar baz”)

Correct, the sendMessage calls are synchronous so they will be executed in the context of the calling thread.

You can go ahead and put a “print wx.Thread_IsMain()” in the method to see for yourself.

The usual quick workaround is as mentioned earlier to use CallAfter. In this case you could do something like this

wx.CallAfter(pub.sendMessage, “UpdateDisplay”, “foo bar baz”)

This will use an event to delegate the execution of the call to the main thread.

Cody

Personally I would suggest having interfaces to your GUI object - so
that the rest of your code doesn’t need to know anything else about
what the GUI is made up of and having the CallAfter in those methods
e.g. in your GUI class:
def UpdateStatus(self, NewStatusText):
“”" Update the user display of the status “”"
wx.CallAfter(_theTextCtrl.write(NewStatusText))
and in the non-GUI parts of your code:
ourGUI.UpdateStatus(“foo bar baz”)
Then most of your code will not need to have any awareness of wx or
of what components your GUI is made up of just that it has a
UpdateStatus method, etc., then if you need to use some other method
of displaying your status at some point down the line the changes
are localised to the GUI, e.g. if the user requests that all error
statuses be displayed in red and the rest in black you could extend
UpdateStatus to take an error code as an additional parameter,
defaulting to zero, if it is non-zero set the text colour to red,
otherwise set it to black, (both in a call after), change the text
control to a rich text control and in those places where you are
calling UpdateStatus where it is an error add a non-zero error code
to that call, rather than searching for all occurrences of
theTextCtrl.write and adding code all over the place to change the
colour appropriately. Then the user might request an audible signal
on a new error…
Hopefully you begin to see the advantages of separating GUI from
functional code. :slight_smile:
Steve

···

On 15/02/13 22:23, patrick korsnick
wrote:

  Wow thanks for clearing that up! So essentially pubsub

doesn’t buy us any thread safety (the only reason we switched to
using it). Thus we can get rid of the pubsub stuff and go back to
the way we were doing it before: (passing the TextCtrl to the
second thread and calling write()) but this time we’ll wrap it
with a CallAfter-- wx.CallAfter(theTextCtrl.write(“foo bar baz”))
and if I understand correctly it should be thread safe.


Steve Gadget Barnes