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.
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.
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 **.
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.
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 ().
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()
Dies ist das Ausführungsergebnis. Es ist jetzt möglich zu bestätigen, dass das GUI-Programm wie vorgesehen gesteuert wird.
Recommended Posts