Search code examples
wxpython

WxPython - GUI frozen


my python program consist of a server socket and a GUI. If a new client connects to the server, a new slider should be added to the GUI. The received data of the client should change the position of the slider.

My problem is, that the call of the method "addSlider" makes the GUI freeze. I noticed that it freezes as long as the server thread, which called the method, stops.

To make it simpler I made a Test thread instead of the server thread

Do you have any suggestion how I can add the slider properly?

Main Thread:

g = GUI()
t = Test(g.addSlider)
g.start()
t.start()

Test Class:

class Test(threading.Thread):
    def __init__(self, addFunction):
            threading.Thread.__init__(self)
            self.add = True
            self.addFunction = addFunction

    def run(self):
            while True:
                    if self.add:
                            self.addFunction(10)
                            self.add = False

GUI Class:

class GUI(threading.Thread):
    def __init__(self, title='New Window'):
        threading.Thread.__init__(self)
        self.ex = wx.App(False)
        self.title = title
        self.window = Window(None, self.title)

    def loop(self):
        self.ex.MainLoop()

    def addSlider(self, newId):
        self.window.addSlider(newId)

class Window(wx.Frame):

class Window(wx.Frame):
    def __init__(self, parent, title):
        super(Window, self).__init__(parent, title = title, style= wx.SYSTEM_MENU | wx.CAPTION | wx.CLOSE_BOX)
        self.Bind(wx.EVT_CLOSE, self.OnClose)
        self.SetSize(wx.Size(300, 500))
        self.SetMinSize(wx.Size(300, 500))
        self.SetBackgroundColour('RED')

        self.rootPanel = wx.Panel(self)
        self.rootPanel.Fit()
        self.rootPanel.SetBackgroundColour('YELLOW')

        self.panelList = []
        self.panelIdList = []

        self.vbox = wx.BoxSizer(wx.HORIZONTAL)
        self.vbox.Fit(self)

        self.rootPanel.SetSizer(self.vbox)

        self.Centre()
        self.Show(True)

    def addSlider(self, sldId):
        minSize = 130 * (len(self.panelList)+1)

        if minSize < 300:
            minSize = 300

        self.SetSize(wx.Size(minSize, 500))
        self.SetMinSize(wx.Size(minSize, 500))

        cpnl = ControlPanel(self.rootPanel, 500, sldId)

        self.panelIdList.append(sldId)
        self.panelList.append(cpnl)

        self.vbox.Add(self.panelList[len(self.panelList)-1], 1, wx.ALL|wx.ALIGN_CENTER)
        self.vbox.Layout()
        self.vbox.Fit(self)

        self.rootPanel.Layout()
        self.Layout()

ControlPanel Class:

class ControlPanel(wx.Panel):
    def __init__(self, parent, newHeight, newId):
        wx.Panel.__init__(self, parent, -1, size=wx.Size(100, newHeight), style=wx.ALIGN_CENTER)
        self.id = newId

        self.lbl = wx.StaticText(self, label = ("ID = " + str(newId)), name = ("ID = " + str(newId)), style = wx.ALIGN_CENTRE_HORIZONTAL)

        self.sld = wx.Slider(self, value = 10, size = wx.Size(100, newHeight-100), minValue = 0, maxValue = 255, style = wx.SL_VERTICAL|wx.SL_MIN_MAX_LABELS|wx.SL_INVERSE)

        self.sizer = wx.BoxSizer(wx.VERTICAL)
        self.sizer.Add(self.lbl, 0, wx.ALIGN_CENTER_HORIZONTAL | wx.BOTTOM | wx.TOP, 20)
        self.sizer.Add(self.sld, 1, wx.ALIGN_CENTER | wx.LEFT, 30)

        self.sizer.Fit(parent)
        self.SetSizer(self.sizer)

        self.sizer.Layout()
        self.Layout()

Solution

  • The problem is almost certainly that you are starting the wx.App from within a Python thread. This is backwards. The App object should be the main thread and spawn off all other threads as needed. Create a sub-class of wx.App instead and then create a different thread if you need one.

    Note that when using threads, you must use wxPython's thread-safe methods to communicate from the thread back to wxPython. The following methods are thread-safe:

    • wx.CallAfter
    • wx.CallLater
    • wx.PostEvent

    Here are a few links with more information: