nanotek
nanotek

Reputation: 3087

WxPython creating NewEvent in loop leads to GUI slow down

I have a thread running which monitors the state of things, and posts an event to refresh the Gui with a max rate of 0.1s. However, I noticed after several hours that the Gui became sluggish and would eventually freeze.

class Hello(wx.Frame):

    def __init__(self):
        self.refreshEvent, EVT_REFRESH_GUI = wx.lib.newevent.NewEvent()
        self.Bind(EVT_REFRESH_GUI, self.__RefreshGui)

    def MyThread(self):

        while(True):
            # Do stuff
            self.var = self.GetVar()

            # Post refresh event.
            refreshEvent = self.refreshEvent()
            wx.PostEvent(self, refreshEvent)
            time.sleep(0.1)

    def __RefreshGui(self, event):
        pass

I just realized that if I move refreshEvent = self.refreshEvent() outside the while loop everything seems much more responsive.

I haven't seen anything about creating new events causing lag. Is this a known and expected behavior? Is this specifically because of wxPython, or is this from wxWidgets?

class Hello(wx.Frame):

    def __init__(self):
        self.refreshEvent, EVT_REFRESH_GUI = wx.lib.newevent.NewEvent()
        self.Bind(EVT_REFRESH_GUI, self.__RefreshGui)

    def MyThread(self):
        refreshEvent = self.refreshEvent()
        while(True):
            # Do stuff
            self.var = self.GetVar()

            # Post refresh event.
            wx.PostEvent(self, refreshEvent)
            time.sleep(0.1)

    def __RefreshGui(self, event):
        pass

EDIT:

To address some of the current comments and answers, this is a simplified view of the real code to try and focus on what was causing my issue. Yes, there is a real thread in the code. We can try to move towards a wxTimer approach for the future, and I appreciate the feedback because I was wondering how to best go about this. The reason we are using PostEvent is because we needed it to be thread safe. Previously we were running into segfault issues from different threads calling the same things. There is a significant amount of legacy code, and this seemed like a good approach at the time.

The question was really wondering if there was an inherent slowdown with the number of custom events created and posted. Is the approach of creating events and posting events many, many times expected to lead to a slowdown?

Upvotes: 0

Views: 304

Answers (2)

Rolf of Saxony
Rolf of Saxony

Reputation: 22443

Without repeating what others have said, although I agree, here's a simple wx.timer example:

import wx
class MyFrame(wx.Frame):
    def __init__(self, parent, title):
        wx.Frame.__init__(self, parent, -1, title,size=(350, 225))
        panel = wx.Panel(self)
        self.Text = wx.StaticText(panel,-1,"Tick")
        self.timer = wx.Timer(self)
        self.Bind(wx.EVT_TIMER, self.onTimer, self.timer)
        sizer = wx.BoxSizer(wx.VERTICAL)
        sizer.Add(self.Text, 0, wx.ALL, 10)
        panel.SetSizer(sizer)
        self.timer.Start(2000) # fire every 2 seconds
        self.Show()

    def onTimer(self, evt):
        if self.Text.GetLabel() == "Tick":
            self.Text.SetLabel("Tock")
        else:
            self.Text.SetLabel("Tick")

if __name__ == '__main__':
    app = wx.App()
    MyFrame(None,"Tick Tock timer")
    app.MainLoop()

Upvotes: 0

nepix32
nepix32

Reputation: 3177

Just because you call a class method MyThread does not automatically make it a Thread. But in some way you are already on the right track.

You will want to check out the topic LongRunningTasks on the wxPython wiki, especially the section Easiest Implementation Ever :) at the bottom of the page.

Your GUI freezes because you are eating up all resources by sleeping in an endless while loop. This guarantees that wxPython will not be able to react to any Events any more (also the one you created yourself).

My advice: First, get rid of your custom event. You do not need that here.

You essentially have two options:

  1. Moving the sleep into a separate thread and communicate with the main GUI thread in thread-safe ways (e. g. wx.CallBack). This is described very well in the wiki link.

  2. If you just need some code which is run every 100 ms, use wx.Timer (see the wxPython demo for an example). However, you have to make sure, that your work can be done in well than below 100 ms, otherwise events will pile up. If this is an concern, use Method 1 instead.

Upvotes: 1

Related Questions