Partage
  • Partager sur Facebook
  • Partager sur Twitter

Tkinter freeze

    21 décembre 2021 à 22:03:52

    Bonjour,

    J'essaie de faire un compteur en parallèle d'un programme principal. J'essaie de prendre une variable et de l'afficher avec tkinter.

    J'en suis arrivé jusqu'à là :

    import queue
    from threading import Thread
    import tkinter as tk
    from time import sleep
    
    
    class Counter(tk.Tk):
        def __init__(self, queue, font="Courier", size=30, foreground="black"):
            super().__init__()
            self.queue = queue
            self.data = self.queue.get()
            self.font = font
            self.size = size
            self.foreground = foreground
            self.l = tk.Label(self, text=self.data)
            self.l.config(font=(self.font, self.size), fg=self.foreground)
            self.l.pack()
            
        def getNumber(self): 
            self.data = self.queue.get()
            print(self.data)
            self.after(100, self.l.configure(text=self.data))
            sleep(1)
            self.getNumber() # Loop
                
            
    class ThreadCounter(Thread):
        def __init__(self, queue):
            super().__init__()
            self.queue = queue
    
        def run(self):
            self.root = Counter(self.queue) # create my tkinter object
            self.root.after(500, self.root.getNumber) # Find value in the main thread
            self.root.mainloop()
            
    q = queue.Queue()
    cnt = 0
    q.put(cnt)
    threadCounter = ThreadCounter(q)
    threadCounter.start()
    while True:
        sleep(2)
        cnt += 1
        q.put(cnt)

    J'ai créé un thread pour le compteur et je stock ma variable dans un queue pour pouvoir l'échanger entre mes threads.

    Ma fonction "getNumber()" permet de boucler pour actualiser mon label. J'ai mis un print dedans et on voit bien que la fonction boucle.

    Malheureusement, mon compteur freeze sur 0 sans aucun message d'erreur.

    Merci d'avance.

    • Partager sur Facebook
    • Partager sur Twitter
      21 décembre 2021 à 22:41:36

      Avec:
          def getNumber(self):
              self.data = self.queue.get()
              print(self.data)
              self.after(100, self.l.configure(text=self.data))
              sleep(1)
              self.getNumber() # Loop

      getNumber est un callback appelé par tkinter. Comme il se rappelle lui même, impossible à tkinter de faire les mises à jour.

      Un code comme:

          def getNumber(self):
              self.data = self.queue.get()
              print(self.data)
              self.l.configure(text=self.data)
              self.l.after(500, self.getNumber)
      

      devrait fonctionner mieux.

      Ceci dit, il est préférable de faire tourner le GUI dans le thread principal.

      • Partager sur Facebook
      • Partager sur Twitter
        21 décembre 2021 à 23:08:26

        Merci ça fonctionne très bien.

        Je ne comprends pas trop l’intérêt de l'objet passé avant after sachant que le callback est pour moi suffisant.

        "Ceci dit, il est préférable de faire tourner le GUI dans le thread principal."

        Mon GUI n'ayant pas d'impact sur le programme mais mon programme ayant du l'impact sur le GUI, je me suis dit que c'était plus logique sachant que je n'utilise pas de bouton ou autre élément interactif. Peut être que tkinter n'est pas la bonne solution pour de l'affichage mais je n'ai rien trouvé d'autre.

        Encore merci.

        • Partager sur Facebook
        • Partager sur Twitter
          22 décembre 2021 à 17:19:54

          Hyderman a écrit:

          Je ne comprends pas trop l’intérêt de l'objet passé avant after sachant que le callback est pour moi suffisant.

          n'importe quel widget fait l'affaire... lorsque j'ai écris ça, j'ai omis que self était une instance de Tk.

          Hyderman a écrit:

          Mon GUI n'ayant pas d'impact sur le programme mais mon programme ayant du l'impact sur le GUI, je me suis dit que c'était plus logique

          Il faut essayer de comprendre la logique avec laquelle ont été construites les bibliothèques utilisées et se méfier de ses intuitions.

          Pour moi, il serait plus correct d'écrire:

          from queue import Queue, Empty
          from threading import Thread, Event
          import tkinter as tk
          from time import sleep
          
          def counter(queue, done):
              count = 0
              while not done.is_set():
                  count += 1
                  queue.put(count)
                  sleep(1)
              
          def do_update(label, queue, delay=200):
              try:
                  n = queue.get(False)
              except Empty:
                  pass
              else:
                  label['text'] = n
              finally:
                  label.after(delay, do_update, label, queue)
          
          
          if __name__ == '__main__':
          
              root = tk.Tk()
              label = tk.Label(root)
              label.pack()
              queue = Queue()
              done = Event()
              th = Thread(target=counter, args=(queue, done))
              th.start()
              do_update(label, queue)
              tk.mainloop()
              done.set()
              th.join()
          


          mais c'est vous qui voyez...

          • Partager sur Facebook
          • Partager sur Twitter
            23 décembre 2021 à 10:26:36

            D'accord je vois, cela signifie que je devrais mettre mon programme principal dans un thread.

            Je suppose que le done permet d'arrêter le counter si on ferme la fenêtre.

            Je n'arrive pas à voir l’intérêt de mettre le block à False ligne 15. c'est au cas où on voudrait faire quelque chose dans l'except ?

            • Partager sur Facebook
            • Partager sur Twitter
              23 décembre 2021 à 11:06:19

              Hyderman a écrit:

              Je n'arrive pas à voir l’intérêt de mettre le block à False ligne 15. c'est au cas où on voudrait faire quelque chose dans l'except ?


              Un callback  est supposé se terminer le plus rapidement possible. S'il n'y a rien dans la Q, on attend indéfiniment... Techniquement, il faudrait vider la Q (et pas seulement récupérer un item) avant de revenir tester son état plus tard.

              • Partager sur Facebook
              • Partager sur Twitter
                23 décembre 2021 à 11:09:27

                block à False permet a queue.get() de ne pas être bloquant et ne pas attendre qu'il y ait quelque chose dans la queue. Avec block ) False, si la queue est vide, on sort directement avec l'exception Empty.

                https://docs.python.org/fr/3/library/queue.html#queue.Queue.get 

                • Partager sur Facebook
                • Partager sur Twitter

                Tkinter freeze

                × Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
                • Editeur
                • Markdown