Dans l'article précédent (https://qiita.com/kotai2003/items/dc0b23fa95844fc1ba45), j'ai présenté comment améliorer la réactivité en utilisant des threads dans l'interface graphique de Tkinter.
Cette fois, je vais vous montrer comment contrôler le comportement de ce fil.
Jetez un œil à la figure ci-dessous. Je voudrais créer un programme GUI qui compte les nombres lorsque le bouton de démarrage est enfoncé. Si vous appuyez sur le bouton d'arrêt en bas sur le chemin, il vous sera demandé d'arrêter de compter les nombres. De plus, en appuyant sur le bouton de démarrage en haut, le décompte des nombres est redémarré.
Au début, j'ai pensé à créer une fonction pour compter les nombres à l'aide de l'instruction While, et appuyer sur le bouton de démarrage démarrerait le fil, et appuyer sur le bouton ** stop arrêterait le fil **.
Voici le code auquel j'ai pensé en premier. Les utilisateurs gourmands en threads peuvent se fâcher lorsqu'ils voient ce code.
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()
Le premier _start_func () n'est pas faux. Mais le second _stop_func () est une grosse erreur. C'est vrai. Le thread a une méthode .start () mais pas de méthode **. Stop (). ** ** Les threads ne sont pas conçus pour être forcés au milieu. ** **
Alors que dois-je faire?
À ce moment-là, vous lanceriez un fil de discussion et le ** suspendriez temporairement ** ou le ** le reprendriez **. Le héros qui apparaît ici est l'objet ** Event **.
L'objet Event gère le comportement des threads par la valeur de l'indicateur interne. Si la valeur de l'indicateur interne est True, exécutez le thread. Si False, arrêtez le thread. C'est tout. Le tableau suivant résume les méthodes importantes de contrôle des indicateurs internes.
Méthodes liées aux événements | La description | Remarques |
---|---|---|
wait() | Faire attendre le fil(=Arrêtez temporairement. ) | Faites attendre le thread jusqu'à ce que l'indicateur interne soit True. |
set() | Exécutez le fil | Définissez la valeur de l'indicateur interne sur True. |
clear() | Arrêtez le fil. | Définissez la valeur de l'indicateur interne sur False. |
is_set() | Renvoie la valeur de l'indicateur interne | Utilisé pour juger de l'état de fonctionnement d'un thread. |
Cela peut être difficile à comprendre au début car le nom de la méthode est set () au lieu de begin (), mais il est facile à comprendre si vous pensez que vous définissez les indicateurs internes sur True et False pour contrôler l'opération. Je pense.
À l'aide de l'objet Event, écrivez la structure du programme de ce que vous voulez faire comme suit.
Tout d'abord, lorsque le programme démarre, le thread est également démarré avec la méthode start en même temps. Ensuite, prepare a démarré comme une instance de l'événement. Cet indicateur interne démarré contrôlera le comportement du thread.
Mettez ** started.wait () ** au début de la fonction _main_func () qui sera la cible du thread. _main_func () est composé d'instructions while et l'instruction while est contrôlée par alive. (Alive est utilisé lorsque le programme est finalement lancé, mais ici, il n'est affiché que dans le code et l'explication est omise.)
Liez la méthode .set () qui définit l'indicateur interne sur True au bouton de démarrage de l'interface graphique et liez la méthode clear () qui définit l'indicateur interne sur False au bouton d'arrêt de l'interface graphique.
Pour la structure interne de l'instruction While de _main_func (), vérifiez d'abord l'état de l'indicateur interne par la méthode .is_set (). Si la valeur de l'indicateur interne est True, faites ce que vous voulez que le thread fasse. Si l'indicateur interne s'avère être False, la méthode .wait () fait attendre le thread.
Je publierai l'intégralité du code du programme.
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 #État de la boucle
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()
C'est le résultat de l'exécution. Il est maintenant possible de confirmer que le programme GUI est contrôlé comme prévu.
Recommended Posts