[PYTHON] [Tkinter] Contrôler les threads avec l'objet Event

introduction

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.

Chose que tu veux faire

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

image.png

Un thread a un début mais pas d'arrêt.

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

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

Structure du programme

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

image.png

image.png

image.png

Code programme

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

Résultat d'exécution

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.

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

Résumé

  1. Utilisez des threads pour améliorer la réactivité de l'interface graphique de Tkinter.
  2. Utilisez l'objet Event pour contrôler le comportement du thread.
  3. Les threads peuvent être contrôlés à l'aide des quatre méthodes qui contrôlent les indicateurs internes de l'événement.

Les références

  1. Objet d'événement de document officiel Python
  2. [Tkinter] Améliorez la réactivité de l'interface graphique
  3. Votre threading.Event n'est pas utilisé correctement
  4. Exemple simple pour arrêter et reprendre les threads Python

Recommended Posts

[Tkinter] Contrôler les threads avec l'objet Event
MVC avec Tkinter
Devenez Père Noël avec Tkinter
Contrôler les scripts avec des exceptions
Programmation avec Python et Tkinter
Introduction à Tkinter 2: Button
Travailler avec le tkinter et la souris
Visualisons le fil 2ch avec WordCloud-Scraping-
Contrôler le libellé des erreurs avec nginx
Changement d'écran / transition d'écran avec Tkinter
Créer une visionneuse d'images avec Tkinter
Exécuter Label avec tkinter [Python]
J'ai mesuré l'IMC avec tkinter
Contrôlez plusieurs robots avec jupyter-lab