How to achieve time wait processing with wxpython

Introduction

When I first tried to write a time wait process in wxpython

  1. Try waiting for time with time.sleep ()
  2. The screen becomes unresponsive and the cause is investigated.
  3. Support with wx.Timer and Thread

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.

Cause of error in time.sleep ()

Roughly speaking, the cause is that the Mainloop running behind the GUI stops at sleep.

environment

Mac OS wxpython 4.1.0

import

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

Method 1 wx.Timer

This is effective when processing is performed at regular intervals. Is it the easiest to use to update the current time? WxTimer1.gif

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.

WxTimer2.gif

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.

Method 2 Parallel processing threading.Thread

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?)

WxThread1.gif

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.

End thread at any time

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.

WxThread

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 ...

At the end

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.

Reference article

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

How to achieve time wait processing with wxpython
How to do multi-core parallel processing with python
How to measure execution time with Python Part 1
[wxpython] How to use wx.lib.plot basic & time axis
How to measure execution time with Python Part 2
How to measure processing time in Python or Java
How to measure mp3 file playback time with python
How to update with SQLAlchemy?
How to cast with Theano
How to Alter with SQLAlchemy?
How to separate strings with','
How to RDP with Fedora31
How to Delete with SQLAlchemy?
How to cancel RT with tweepy
Python: How to use async with
How to use virtualenv with PowerShell
How to deal with imbalanced data
How to install python-pip with ubuntu20.04LTS
How to deal with imbalanced data
How to get started with Scrapy
How to get started with Python
How to deal with DistributionNotFound errors
How to get started with Django
How to Data Augmentation with PyTorch
How to use FTP with Python
How to calculate date with python
How to install mysql-connector with pip3
How to INNER JOIN with SQLAlchemy
How to install Anaconda with pyenv
How to authenticate with Django Part 2
How to authenticate with Django Part 3
How to extract features of time series data with PySpark Basics
Summarize how to preprocess text (natural language processing) with tf.data.Dataset api
How to write offline real time Solve F01 problems with Python
How to calculate "xx time" in one shot with Python timedelta
How to do arithmetic with Django template
[Blender] How to set shape_key with script
How to title multiple figures with matplotlib
How to get parent id with sqlalchemy
How to compare time series data-Derivative DTW, DTW-
How to add a package with PyCharm
How to set the server time to Japanese time
How to install DLIB with 2020 / CUDA enabled
How to write offline real time I tried to solve E11 with python
How to use ManyToManyField with Django's Admin
How to use OpenVPN with Ubuntu 18.04.3 LTS
How to use Cmder with PyCharm (Windows)
How to get the date and time difference in seconds with python
How to prevent package updates with apt
How to work with BigQuery in Python
How to use Ass / Alembic with HtoA
How to deal with enum compatibility errors
How to use Japanese with NLTK plot
How to do portmanteau test with python
How to search Google Drive with Google Colaboratory
How to display python Japanese with lolipop
How to download youtube videos with youtube-dl
How to use jupyter notebook with ABCI
How to power off Linux with Ultra96-V2
"How to pass PATH" to learn with homebrew
How to scrape websites created with SPA