When I first tried to write a time wait process in wxpython
I think you will follow the path. (Isn't it?) I made a note of this time waiting process because I use it quite often.
Honestly, this content is all over the place, so I hope you can see it in one of the reference articles.
Roughly speaking, the cause is that the Mainloop running behind the GUI stops at sleep.
Mac OS wxpython 4.1.0
install wxpython
python
pip install wxpython
threading can be used without installation. I don't use it when using wx.Timer, but use time instead.
python
import wx
import threading
import time
This is effective when processing is performed at regular intervals. Is it the easiest to use to update the current time?

python
import wx
import threading  #Not used this time
import time  #Not used this time
#Used for time display
import datetime as dt
class MainFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(400, 200))
        #TimerPanel instance creation
        self.panel = TimerPanel(self, -1)
        #Display with the screen centered
        self.Center()
        self.Show()
class TimerPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        #Creating a string of the current time, self.clock_txt creation,Display time
        now = dt.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
        self.clock_txt = wx.StaticText(self, -1, label=now)
        #Font settings,After changing the size to 20 self.clock_Reflected in txt
        font = wx.Font(20, wx.FONTFAMILY_DEFAULT, wx.FONTSTYLE_NORMAL, wx.FONTWEIGHT_NORMAL)
        self.clock_txt.SetFont(font)
        #Sizar creation, self.clock_Add txt to sizar,Apply sizer to panel
        sizer = wx.GridSizer(1, 1, gap=(0, 0))
        sizer.Add(self.clock_txt, flag=wx.ALIGN_CENTRE)  # ALIGN_CENTRE: Installed in the center of the sizar
        self.SetSizer(sizer)
        self.timer = wx.Timer(self)  #Create a timer in the panel
        self.Bind(wx.EVT_TIMER, self.clock)  #Self at specified intervals.Run clock
        self.timer.Start(1000)  #Timer 1000ms(=1s)Set to start
        # self.timer.Stop()  #If you want to stop the timer, this
    def clock(self, event):
        #Get the current time and self.clock_Set to txt
        now = dt.datetime.now().strftime('%Y/%m/%d %H:%M:%S')
        self.clock_txt.SetLabel(now)
        self.Refresh()
if __name__ == '__main__':
    app = wx.App()
    MainFrame(None, -1, 'TimeEvent')
    app.MainLoop()
Create a timer with the frame or panel as the parent, set the interval and start. Multiple timers can be run at the same time.
The gif above has two panels, each displaying the time with a timer.
The top panel is 1 second and the bottom is 2 seconds.
wx.Timer uses threading.Thread for processing that is difficult to implement. This function can create another thread and execute multiple processes at the same time. You should not use time.sleep () in the thread where the GUI is running, but there is no problem in another thread.
The following is the process that I find difficult with wx.Timer --Periodic execution (ex: traffic light) where the time waiting interval is not the same --Processing that changes state with the passage of time-> It is troublesome to make a conditional branch every time during regular execution (ex: traffic light)
In other words, use threadind.Thred when making a traffic light (isn't it too limited?)
Blue, yellow, and red are displayed for 4, 1, and 5 seconds each.
python
import wx
import time
import threading
class TrafficLight(threading.Thread):
    def __init__(self, panel):
        super().__init__()
        self.panel = panel
        #Daemonize the subthread, if this is not set to True, the subthread will continue to run even if the screen is closed.
        self.setDaemon(True)
    def run(self):
        while True:
            #Yellow panel->black,Red panel->Red
            self.panel.yellow_panel.SetBackgroundColour('#000000')
            self.panel.red_panel.SetBackgroundColour('#ff0000')
            self.panel.Refresh()
            time.sleep(5)
            #Red panel->black,Blue panel->Blue
            self.panel.red_panel.SetBackgroundColour('#000000')
            self.panel.blue_panel.SetBackgroundColour('#00ff00')
            self.panel.Refresh()
            time.sleep(4)
            #Blue panel->black,Yellow panel->yellow
            self.panel.blue_panel.SetBackgroundColour('#000000')
            self.panel.yellow_panel.SetBackgroundColour('#ffff00')
            self.panel.Refresh()
            time.sleep(1)
class MainFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(600, 200))
        self.main_panel = MainPanel(self, -1)
        
        #Display the screen in the center
        self.Center()
        self.Show()
class MainPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        
        #Blue panel, color specified as black
        self.blue_panel = wx.Panel(self, -1)
        self.blue_panel.SetBackgroundColour('#000000')
        
        #Yellow panel, color specified as black
        self.yellow_panel = wx.Panel(self, -1)
        self.yellow_panel.SetBackgroundColour('#000000')
        #Red panel, color specified as black
        self.red_panel = wx.Panel(self, -1)
        self.red_panel.SetBackgroundColour('#000000')
        
        #Start button
        self.button = wx.Button(self, -1, 'start')
        
        #Layout related
        sizer1 = wx.FlexGridSizer(2, 1, gap=(0, 0))
        sizer2 = wx.GridSizer(1, 3, gap=(0, 0))
        sizer1.Add(sizer2, flag=wx.EXPAND)
        sizer1.Add(self.button, flag=wx.ALIGN_RIGHT)
        sizer1.AddGrowableCol(0)
        sizer1.AddGrowableRow(0)
        sizer2.Add(self.blue_panel, flag=wx.EXPAND)
        sizer2.Add(self.yellow_panel, flag=wx.EXPAND)
        sizer2.Add(self.red_panel, flag=wx.EXPAND)
        self.SetSizer(sizer1)
        
        #Subthread instance creation, self as an argument
        self.traffic_light = TrafficLight(self)
        
        #Button event
        self.button.Bind(wx.EVT_BUTTON, self.start)
    def start(self, event):
        #Subthread instance creation, self as an argument
        traffic_light = TrafficLight(self)
        #Subthread operation started
        traffic_light.start()
        self.start_button.Disable()  #Button disable,Prevents multiple threads from being created
if __name__ == "__main__":
    app = wx.App()
    MainFrame(None, -1, "traffic light")
    app.MainLoop()
Overriding the run method of threading.Thread. Only self can be passed to the run method. The required attributes are passed when the instance is created. Also, since it can only be executed once per thread, it is instantiated when the button is pressed.
The above traffic light cannot be stopped because it is operating while True. But I want to be able to stop it. Therefore, rewrite the TrafficLight class and add global variables and functions.
The GUI has added a stop button. When the stop button is pressed, thread_stop_flag becomes True and the thread operation ends.
python
import wx
import time
import threading
thread_stop_flag = False
def wait_time(seconds):
    """
Stop during the waiting time passed_Wait while looking at the flag
    stop_Exit the while statement when flag is True
    :param seconds: int
Wait time
    :return: None
    """
    wait_start = time.time()
    while time.time() - wait_start <= seconds:
        if not thread_stop_flag:
            time.sleep(1)
        else:
            break
class TrafficLight(threading.Thread):
    def __init__(self, panel):
        super().__init__()
        self.panel = panel
        #Daemonize the subthread, if this is not set to True, the subthread will continue to run even if the screen is closed.
        self.setDaemon(True)
    def run(self):
        while not thread_stop_flag:  #Change 2
            #Yellow panel->black,Red panel->Red
            self.panel.yellow_panel.SetBackgroundColour('#000000')
            self.panel.red_panel.SetBackgroundColour('#ff0000')
            self.panel.Refresh()
            wait_time(5)
            #Red panel->black,Blue panel->Blue
            self.panel.red_panel.SetBackgroundColour('#000000')
            self.panel.blue_panel.SetBackgroundColour('#00ff00')
            self.panel.Refresh()
            wait_time(4)
            #Blue panel->black,Yellow panel->yellow
            self.panel.blue_panel.SetBackgroundColour('#000000')
            self.panel.yellow_panel.SetBackgroundColour('#ffff00')
            self.panel.Refresh()
            wait_time(1)
        #Change 3,Return all to black
        self.panel.blue_panel.SetBackgroundColour('#000000')
        self.panel.yellow_panel.SetBackgroundColour('#000000')
        self.panel.red_panel.SetBackgroundColour('#000000')
        self.panel.Refresh()
class MainFrame(wx.Frame):
    def __init__(self, parent, id, title):
        wx.Frame.__init__(self, parent, id, title, size=(600, 200))
        self.main_panel = MainPanel(self, -1)
        #Display the screen in the center
        self.Center()
        self.Show()
class MainPanel(wx.Panel):
    def __init__(self, parent, id):
        wx.Panel.__init__(self, parent, id)
        #Blue panel, color specified as black
        self.blue_panel = wx.Panel(self, -1)
        self.blue_panel.SetBackgroundColour('#000000')
        #Yellow panel, color specified as black
        self.yellow_panel = wx.Panel(self, -1)
        self.yellow_panel.SetBackgroundColour('#000000')
        #Red panel, color specified as black
        self.red_panel = wx.Panel(self, -1)
        self.red_panel.SetBackgroundColour('#000000')
        #start,Button for stop
        self.start_button = wx.Button(self, -1, 'start')
        self.stop_button = wx.Button(self, -1, 'stop')
        #Layout related
        sizer1 = wx.FlexGridSizer(2, 1, gap=(0, 0))
        sizer2 = wx.GridSizer(1, 3, gap=(0, 0))
        sizer3 = wx.GridSizer(1, 2, gap=(0, 0))
        sizer1.Add(sizer2, flag=wx.EXPAND)
        sizer1.Add(sizer3, flag=wx.ALIGN_RIGHT)
        sizer1.AddGrowableCol(0)
        sizer1.AddGrowableRow(0)
        sizer2.Add(self.blue_panel, flag=wx.EXPAND)
        sizer2.Add(self.yellow_panel, flag=wx.EXPAND)
        sizer2.Add(self.red_panel, flag=wx.EXPAND)
        sizer3.Add(self.start_button)
        sizer3.Add(self.stop_button)
        self.SetSizer(sizer1)
        #Button event
        self.start_button.Bind(wx.EVT_BUTTON, self.start)
        self.stop_button.Bind(wx.EVT_BUTTON, self.stop)
    def start(self, event):
        global thread_stop_flag
        thread_stop_flag = False
        #Subthread instance creation, self as an argument
        traffic_light = TrafficLight(self)
        #Subthread operation started
        traffic_light.start()
        self.start_button.Disable()  #Prevents the creation of multiple button invalidation threads
    def stop(self, event):
        #Global variable thread_stop_flag to True=Set the while statement condition of thread to False
        global thread_stop_flag
        thread_stop_flag = True
        self.start_button.Enable()
if __name__ == "__main__":
    app = wx.App()
    MainFrame(None, -1, "traffic light")
    app.MainLoop()
It's not a big deal, but it feels like a long time. Please let me know if there is another good way to write ...
Thank you for reading. Naturally, the method using threading has other uses besides traffic lights. For example, when you want to execute a series of operations only once instead of executing them regularly. I use it for device initial settings.
I refer to the following articles.
Cause of error Python-I'm a beginner. After inserting the sleep statement, the app stopped working properly. | teratail
Thread interruption Your threading.Event is used incorrectly-Qiita
Recommended Posts