Search code examples
pythonwhile-loopwxpythonpanelwxwidgets

How to execute a while loop when wx.panel is shown?


I'm new using wxPython and I want to execute a while loop just when certain panel (Panel1) is shown and stop it when the panel gets hidden.

I'm interested in the loop because I want to change a label's value which is in the Panel1.

I tried to literally put the code while True: ..., but didn't work,

Any suggestions?


Solution

  • As the others have mentioned, you can use a thread or a wx.Timer to accomplish what you're looking for. I grabbed some code from my old panel switching tutorial and modified it to show how to use the timer approach. All you have to do is run the following code and switch the panels via the menu:

    import wx
    import wx.grid as gridlib
    
    ########################################################################
    class PanelOne(wx.Panel):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self, parent):
            """Constructor"""
            wx.Panel.__init__(self, parent=parent)
    
            grid = gridlib.Grid(self)
            grid.CreateGrid(25,12)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(grid, 0, wx.EXPAND)
            self.SetSizer(sizer)
    
    ########################################################################
    class PanelTwo(wx.Panel):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self, parent):
            """Constructor"""
            wx.Panel.__init__(self, parent=parent)
            self.count = 0
            self.lbl = wx.StaticText(self,
                                     label='Counter: {}'.format(self.count))
    
            self.timer = wx.Timer(self)
            self.Bind(wx.EVT_TIMER, self.update, self.timer)
    
        #----------------------------------------------------------------------
        def start_timer(self):
            self.timer.Start(1000)
    
        #----------------------------------------------------------------------
        def stop_timer(self):
            self.timer.Stop()
    
        #----------------------------------------------------------------------
        def update(self, event):
            self.count += 1
            self.lbl.SetLabel('Counter: {}'.format(self.count))
    
    ########################################################################
    class MyForm(wx.Frame):
    
        #----------------------------------------------------------------------
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY,
                              "Panel Switcher Tutorial")
    
            self.panel_one = PanelOne(self)
            self.panel_two = PanelTwo(self)
            self.panel_two.Hide()
    
            self.sizer = wx.BoxSizer(wx.VERTICAL)
            self.sizer.Add(self.panel_one, 1, wx.EXPAND)
            self.sizer.Add(self.panel_two, 1, wx.EXPAND)
            self.SetSizer(self.sizer)
    
    
            menubar = wx.MenuBar()
            fileMenu = wx.Menu()
            switch_panels_menu_item = fileMenu.Append(wx.ID_ANY,
                                                      "Switch Panels",
                                                      "Some text")
            self.Bind(wx.EVT_MENU, self.onSwitchPanels,
                      switch_panels_menu_item)
            menubar.Append(fileMenu, '&File')
            self.SetMenuBar(menubar)
    
        #----------------------------------------------------------------------
        def onSwitchPanels(self, event):
            """"""
            if self.panel_one.IsShown():
                self.SetTitle("Panel Two Showing")
                self.panel_one.Hide()
                self.panel_two.Show()
                self.panel_two.start_timer()
            else:
                self.SetTitle("Panel One Showing")
                self.panel_one.Show()
                self.panel_two.stop_timer()
                self.panel_two.Hide()
            self.Layout()
    
    
    # Run the program
    if __name__ == "__main__":
        app = wx.App(False)
        frame = MyForm()
        frame.Show()
        app.MainLoop()
    

    Personally, I think using a thread for this is overkill. However it was kind of fun to modify the code above to use a thread instead. So here's that version too:

    import time
    import wx
    import wx.grid as gridlib
    
    from threading import Thread
    
    
    ########################################################################
    class TestThread(Thread):
        """Test Worker Thread Class."""
    
        #----------------------------------------------------------------------
        def __init__(self, panel):
            """Init Worker Thread Class."""
            Thread.__init__(self)
            self.panel = panel
            self.sentinel = True
            self.start()    # start the thread
    
        #----------------------------------------------------------------------
        def run(self):
            """Run Worker Thread."""
            # This is the code executing in the new thread.
            while self.sentinel:
                time.sleep(1)
                wx.CallAfter(self.panel.update)
            print 'Thread finished!'
    
    ########################################################################
    class PanelOne(wx.Panel):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self, parent):
            """Constructor"""
            wx.Panel.__init__(self, parent=parent)
    
            grid = gridlib.Grid(self)
            grid.CreateGrid(25,12)
    
            sizer = wx.BoxSizer(wx.VERTICAL)
            sizer.Add(grid, 0, wx.EXPAND)
            self.SetSizer(sizer)
    
    ########################################################################
    class PanelTwo(wx.Panel):
        """"""
    
        #----------------------------------------------------------------------
        def __init__(self, parent):
            """Constructor"""
            wx.Panel.__init__(self, parent=parent)
            self.count = 0
            self.lbl = wx.StaticText(self,
                                     label='Counter: {}'.format(self.count))
            self.thread = None
    
        #----------------------------------------------------------------------
        def start_timer(self):
            self.thread = TestThread(self)
    
        #----------------------------------------------------------------------
        def stop_timer(self):
            self.thread.sentinel = False
    
        #----------------------------------------------------------------------
        def update(self):
            self.count += 1
            self.lbl.SetLabel('Counter: {}'.format(self.count))
    
    ########################################################################
    class MyForm(wx.Frame):
    
        #----------------------------------------------------------------------
        def __init__(self):
            wx.Frame.__init__(self, None, wx.ID_ANY,
                              "Panel Switcher Tutorial")
    
            self.panel_one = PanelOne(self)
            self.panel_two = PanelTwo(self)
            self.panel_two.Hide()
    
            self.sizer = wx.BoxSizer(wx.VERTICAL)
            self.sizer.Add(self.panel_one, 1, wx.EXPAND)
            self.sizer.Add(self.panel_two, 1, wx.EXPAND)
            self.SetSizer(self.sizer)
    
            menubar = wx.MenuBar()
            fileMenu = wx.Menu()
            switch_panels_menu_item = fileMenu.Append(wx.ID_ANY,
                                                      "Switch Panels",
                                                      "Some text")
    
            self.Bind(wx.EVT_MENU, self.onSwitchPanels,
                      switch_panels_menu_item)
            self.Bind(wx.EVT_CLOSE, self.on_close)
    
            menubar.Append(fileMenu, '&File')
            self.SetMenuBar(menubar)
    
        #----------------------------------------------------------------------
        def onSwitchPanels(self, event):
            """"""
            if self.panel_one.IsShown():
                self.SetTitle("Panel Two Showing")
                self.panel_one.Hide()
                self.panel_two.Show()
                self.panel_two.start_timer()
            else:
                self.SetTitle("Panel One Showing")
                self.panel_one.Show()
                self.panel_two.stop_timer()
                self.panel_two.Hide()
            self.Layout()
    
        #----------------------------------------------------------------------
        def on_close(self, event):
            self.panel_two.stop_timer()
            self.panel_two.thread.join()
            self.Destroy()
    
    
    # Run the program
    if __name__ == "__main__":
        app = wx.App(False)
        frame = MyForm()
        frame.Show()
        app.MainLoop()
    

    Note that you will want to catch the close event via EVT_CLOSE to make sure the thread is stopped when exiting your application or you may have your application hang or misbehave.