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