[PYTHON] [Tkinter] Steuert Threads mit Ereignisobjekt

Einführung

Im vorherigen Artikel (https://qiita.com/kotai2003/items/dc0b23fa95844fc1ba45) habe ich die Verwendung von Threads in der Tkinter-Benutzeroberfläche zur Verbesserung der Reaktionsfähigkeit vorgestellt.

Dieses Mal werde ich Ihnen zeigen, wie Sie das Verhalten dieses Threads steuern können.

Was du machen willst

Schauen Sie sich die Abbildung unten an. Ich möchte ein GUI-Programm erstellen, das Zahlen zählt, wenn die Starttaste gedrückt wird. Wenn Sie unterwegs die Stopp-Taste unten drücken, werden Sie aufgefordert, das Zählen der Zahlen zu beenden. Durch Drücken der Starttaste oben wird die Anzahl der Nummern neu gestartet.

image.png

Ein Thread hat einen Start, aber keinen Stop.

Zuerst dachte ich darüber nach, eine Funktion zum Zählen von Zahlen mit der While-Anweisung zu erstellen, und durch Drücken der Start-Taste würde der Thread gestartet, und durch Drücken der ** Stop-Taste würde der Thread gestoppt **.

Hier ist der Code, an den ich zuerst gedacht habe. Thread-intensive Benutzer werden möglicherweise wütend, wenn sie diesen Code sehen.

def _start_func(self):
    self.thread_main=threading.Thread(target=self._main_func)
    self.thread_main.start()

def _stop_func(self):
    self.thread_main=threading.Thread(target=self._main_func)
    self.thread_main.stop()

Der erste _start_func () ist nicht falsch. Aber das zweite _stop_func () ist ein großer Fehler. Korrekt. Der Thread hat eine .start () -Methode, aber keine **. Stop () -Methode. ** ** Gewinde sind nicht dafür ausgelegt, in der Mitte nach unten gedrückt zu werden. ** ** **

Was soll ich dann tun?

Zu diesem Zeitpunkt würden Sie einen Thread starten und ihn ** vorübergehend aussetzen ** oder ** fortsetzen **. Der Held, der hier erscheint, ist das Objekt ** Ereignis **.

Verwendung des Ereignisobjekts

Das Ereignisobjekt verwaltet das Verhalten von Threads anhand des Werts des internen Flags. Wenn der Wert des internen Flags True ist, führen Sie den Thread aus. Wenn False, stoppen Sie den Thread. Das ist alles. In der folgenden Tabelle sind die wichtigen Methoden zur Steuerung der internen Flags zusammengefasst.

Ereignisbezogene Methoden Erläuterung Bemerkungen
wait() Lass den Thread warten(=Vorübergehend anhalten. ) Lassen Sie den Thread warten, bis das interne Flag True ist.
set() Führen Sie den Thread aus Setzen Sie den Wert des internen Flags auf True.
clear() Stoppen Sie den Thread. Setzen Sie den Wert des internen Flags auf False.
is_set() Gibt den Wert des internen Flags zurück Wird verwendet, um den Betriebsstatus eines Threads zu beurteilen.

Es mag zunächst schwierig zu verstehen sein, da der Name der Methode set () anstelle von begin () ist, aber es ist leicht zu verstehen, wenn Sie der Meinung sind, dass Sie die internen Flags auf True und False setzen, um die Operation zu steuern. Ich denke.

Programmstruktur

Schreiben Sie mit dem Ereignisobjekt die Struktur des Programms wie folgt.

Wenn das Programm gestartet wird, wird der Thread gleichzeitig mit der Startmethode gestartet. Als Nächstes wird die Vorbereitung als Instanz des Ereignisses gestartet. Dieses gestartete interne Flag steuert das Verhalten des Threads.

Setzen Sie ** start.wait () ** an den Anfang der Funktion _main_func (), die das Ziel des Threads sein soll. _main_func () besteht aus while-Anweisungen, und die while-Anweisung wird von living gesteuert. (Alive wird verwendet, wenn das Programm endgültig gestartet wird, aber hier wird es nur im Code angezeigt und die Erklärung wird weggelassen.)

Binden Sie die .set () -Methode, mit der das interne Flag auf True gesetzt wird, an die Startschaltfläche der GUI und die clear () -Methode, mit der das interne Flag auf False gesetzt wird, an die Stoppschaltfläche der GUI.

Überprüfen Sie für die interne Struktur der While-Anweisung von _main_func () zunächst den Status des internen Flags mit der Methode .is_set (). Wenn der Wert des internen Flags True ist, tun Sie, was der Thread tun soll. Wenn festgestellt wird, dass das interne Flag False ist, wartet der Thread mit der Methode .wait ().

image.png

image.png

image.png

Programmcode

Ich werde den gesamten Programmcode veröffentlichen.

GUI_Contol_Thread_with_Event_Object.py


import tkinter as tk
from tkinter import ttk
from tkinter import font
import threading


class Application(tk.Frame):
    def __init__(self,master):
        super().__init__(master)
        self.pack()

        self.master.geometry("300x300")
        self.master.title("Tkinter GUI with Event")

        self.font_lbl_big = font.Font( family="Meiryo UI", size=30, weight="bold" )
        self.font_lbl_middle = font.Font( family="Meiryo UI", size=15, weight="bold" )
        self.font_lbl_small = font.Font( family="Meiryo UI", size=12, weight="normal" )

        self.create_widgets()
    #--------------------------------------------
    # Setup Threading Start
    #--------------------------------------------
        self.started = threading.Event() # Event Object
        self.alive = True #Schleifenzustand
        self._start_thread_main()


    def create_widgets(self):

        # Frame
        self.main_frame = tk.LabelFrame( self.master, text='', font=self.font_lbl_small )
        self.main_frame.place( x=25, y=25 )
        self.main_frame.configure( height=250, width=250 )
        self.main_frame.grid_propagate( 0 )
        self.main_frame.grid_columnconfigure( 0, weight=1 )

        #Start Button
        self.btn_Start = ttk.Button(self.main_frame)
        self.btn_Start.configure(text ='Start')
        self.btn_Start.configure(command = self._start_func)
        self.btn_Start.grid(column = 0, row = 0, padx=10, pady = 10,sticky='NESW' )

        # Stop Button
        self.btn_Stop = ttk.Button(self.main_frame)
        self.btn_Stop.configure(text = 'Stop')
        self.btn_Stop.configure(command = self._stop_func)
        self.btn_Stop.grid(column = 0, row = 1, padx=10, pady = 10,sticky='NESW')

        # Label
        self.lbl_result = ttk.Label(self.main_frame)
        self.lbl_result.configure(text = 'Threading Result Shown Here')
        self.lbl_result.grid(column = 0, row = 2, padx= 30, pady=10,sticky='NESW')

        # Kill Button
        self.btn_Kill = ttk.Button(self.main_frame)
        self.btn_Kill.configure(text = 'Kill Thread')
        self.btn_Kill.configure(command = self._kill_thread)
        self.btn_Kill.grid(column=0, row=3, padx = 10, pady=20,sticky='NESW')

    #--------------------------------------------------
    # Callback Function
    #--------------------------------------------------
    def _start_func(self):
        self.started.set()
        print("Threading Begin")
        print( 'Thread status', self.thread_main )


    def _stop_func(self):
        self.started.clear()
        print("\n Threading Stopped")
        print( 'Thread status', self.thread_main )


    def _start_thread_main(self):
        self.thread_main = threading.Thread(target=self._main_func)
        self.thread_main.start()
        print('main function Threading Started')
        print('Thread status', self.thread_main)

    def _kill_thread(self):
        if self.started.is_set() == False:
            self.started.set()
            self.alive = False
            self.thread_main.join()
        else:
            self._stop_func()
            self.started.set()
            self.alive = False
            #self.thread_main.join()
        print("Thread was killed.")
        print( 'Thread status', self.thread_main )

      

    def _main_func(self):
        i = 0
        self.started.wait()
        while self.alive:
            if self.started.is_set() == True:
                i = i + 1
                print( "{}\r".format( i ), end="" )
                self.lbl_result.configure( text=i ,font = self.font_lbl_big )

            else:
                self.lbl_result.configure( text= 'Stopped' ,font = self.font_lbl_big)
                self.started.wait()

        pass

def main():
    root = tk.Tk()
    app = Application(master=root)#Inherit
    app.mainloop()

if __name__ == "__main__":
    main()

Ausführungsergebnis

Dies ist das Ausführungsergebnis. Es ist jetzt möglich zu bestätigen, dass das GUI-Programm wie vorgesehen gesteuert wird.

bandicam-2020-07-31-19-01-28-328.gif

Zusammenfassung

  1. Verwenden Sie Threads, um die Reaktionsfähigkeit der Tkinter-Benutzeroberfläche zu verbessern.
  2. Verwenden Sie das Ereignisobjekt, um das Verhalten des Threads zu steuern.
  3. Threads können mit den vier Methoden gesteuert werden, die die internen Flags des Ereignisses steuern.

Verweise

  1. Python-offizielles Dokument Ereignisobjekt
  2. [Tkinter] Verbessern der GUI-Reaktionsfähigkeit
  3. Ihr threading.Event wird falsch verwendet
  4. Einfaches Beispiel zum Stoppen und Fortsetzen von Python-Threads

Recommended Posts

[Tkinter] Steuert Threads mit Ereignisobjekt
MVC mit Tkinter
Werde mit Tkinter Weihnachtsmann
Kontrollieren Sie Skripte mit Ausnahmen
Programmieren mit Python und Tkinter
Einführung in Tkinter 2: Button
Arbeiten Sie mit tkinter und Maus
Lassen Sie uns 2ch Thread mit WordCloud-Scraping- visualisieren
Kontrollieren Sie die Fehlerformulierung mit Nginx
Bildschirmumschaltung / Bildschirmübergang mit Tkinter
Erstellen Sie den Image Viewer mit Tkinter
Führen Sie Label mit tkinter [Python] aus.
Ich habe den BMI mit tkinter gemessen
Steuern Sie mehrere Roboter mit jupyter-lab