Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

wxPython Pango error when using a while True loop in a thread

In this program I get an error when I use a while True loop in the thread. Without the loop I get no error. Of course in the real program I don't update a label continuously. Any idea what I'm doing wrong?

This is the program:

import wx
import thread

class Example(wx.Frame):

    def __init__(self, parent): 
        wx.Frame.__init__(self,parent)
        self.InitUI()

    def InitUI(self):
        self.SetSize((250, 200))
        self.Show(True)

        self.text = wx.StaticText(self, label='',pos=(20,30))

        thread.start_new_thread(self.watch,(self,None))

    def watch(self,dummy,e):
        while True:
            self.text.SetLabel('Closed')


def main():
    ex = wx.App()
    Example(None)
    ex.MainLoop()    

if __name__ == '__main__':
    main() 

And this is the error:

Pango:ERROR:/build/pango1.0-LVHqeM/pango1.0-1.30.0/./pango/pango-            layout.c:3801:pango_layout_check_lines: assertion failed: (!layout->log_attrs) Aborted

Any suggestions as to what I'm doing wrong? I'm (obviously) new to threading.

like image 312
Maria Avatar asked Jan 15 '23 12:01

Maria


2 Answers

I am not exactly sure if that is what causes you problem, but... You should not interact with the GUI from another thread. You should use wx.CallAfter(). I would consider adding sleep inside the loop also.

wx.CallAfter() documentation says:

Call the specified function after the current and pending event handlers have been completed. This is also good for making GUI method calls from non-GUI threads. Any extra positional or keyword args are passed on to the callable when it is called.

Updated code would than be:

import wx
import thread
import time

class Example(wx.Frame):
    def __init__(self, parent): 
        wx.Frame.__init__(self,parent)
        self.InitUI()

    def InitUI(self):
        self.SetSize((250, 200))
        self.Show(True)

        self.text = wx.StaticText(self, label='',pos=(20,30))

        thread.start_new_thread(self.watch,(self,None))

    def watch(self,dummy,e):
        while True:
            time.sleep(0.1)
            wx.CallAfter(self.text.SetLabel, 'Closed')

def main():
    ex = wx.App()
    Example(None)
    ex.MainLoop()    

if __name__ == '__main__':
    main() 

Maybe you can also consider using wx.Timer.

BTW: Your code runs OK on my PC with Windows 7 and wxPython 2.8.

like image 174
Fenikso Avatar answered Jan 21 '23 17:01

Fenikso


In addition to the no updates from background threads rule, I've found that in similar situations (high frequency update of UI objects) that it really helps to only update the value if it has changed from what is already displayed. That can greatly reduce the load on the application because if the value does not change then there will be no need for sending and processing paint events, moving pixels to the screen, etc. So in this example I would add a new method that is called via CallAfter that compares the current value in the widget with the requested value, and only calls SetLabel if they are different.

like image 25
RobinDunn Avatar answered Jan 21 '23 16:01

RobinDunn