Fermer une fenêtre Tk en cours de process

Bonjour,

J'ai un petit programme avec une interface Tkinter qui affiche une liste de

mots dans un widget Text, après une boucle de recherche de quelques minutes

Pour interrompre la recherche, je clique sur la croix en haut à droite de la

barre de titre, mais ce n'est pas très net: j'ai le message habituel

"Python ne répond pas Fermer le programme, ...."

Comment faire ça de façon plus propre ?

Merci d'avance


Windows / Edge 124.0.0.0

7 réponses

  1. Salut Phil,

    Je ne sais si ça va correspondre à tes besoins, mais avec la méthode update() et un bind sur la croix de la fenêtre, je verrai qq chose comme ça :

    import tkinter as tk
    
    
    flag = True
    
    def loop():
            i = 0
            while flag:
                i+=1
                print(f"Loop n° {i}")
                root.update()
            print("Loop off")
    
    def on_exit():
            global flag
            flag = False
            root.destroy()
    
    root = tk.Tk()
    but_search = tk.Button(root, text="Search", command=loop)
    but_search.pack()
    but_quit = tk.Button(root, text="Quit", command=on_exit)
    but_quit.pack()
    root.protocol('WM_DELETE_WINDOW', on_exit)
    root.mainloop()
    0
    1. Contributeur

      bonjour,

      ceci donne un message d'erreur quand tu fermes la fenêtre?

      import tkinter
      racine = tkinter.Tk()
      label = tkinter.Label(racine, text="J'adore Python !")
      label.pack()
      racine.mainloop()
      print("fini")
      0
      1. Salut.

        En effet, tkinter ou n'importe quel GUI ne pourra se fermer si on ne lui redonne pas la main.

        Donc, ce n'est pas dans tkinter que ce trouve la solution mais dans ta méthode de recherche qui me semble bloquante. Comment grosso-modo est-elle effectuée ?

        Ne pourrais-tu pas mettre cela dans un thread ?

        Du style (en très gros résumé) :

        import tkinter
        import threading
        
        
        class Search(threading.Thread):
            def __init__(self, text):
                super().__init__()
                self._text = text
                self._stopped = threading.Event()
        
            def run(self):
                # simulation de qqchose qui prend du temps
                import time
                while not self._stopped.is_set():
                    self._text.insert(tkinter.END, f'{round(time.time())}\n')
                    time.sleep(2)
        
            def stop(self):
                self._stopped.set()
        
        
        def wait_done(ms, window, search):
            if search.is_alive():
                print('waiting...')
                window.after(ms, wait_done, ms, window, search)
            else:
                window.destroy()
        
        
        def on_close(window, search):
            search.stop()
            wait_done(100, window, search)
        
        
        def search_run(search, text, button):
            button.destroy()
            search.start()
        
        
        window = tkinter.Tk()
        text = tkinter.Text(window)
        text.grid()
        
        search = Search(text)
        
        button = tkinter.Button(window, text='rechercher')
        button.grid()
        button.configure(command=lambda: search_run(search, text, button))
        
        window.protocol('WM_DELETE_WINDOW', lambda: on_close(window, search))
        window.mainloop()

        ?

        0
        1. Contributeur

          N'est-il pas suffisant d'appeller régulièrement la méthode update() de tkinter?

          0
      2. Merci à tous,

        Les codes de Diablo et yg_be fonctionnent

        Comment ma méthode de recherche est effectuée ?

        C'est une double boucle pour comparer 2 listes élément par élément,

        le nombre de combinaisons peut être très grand

        Le code:

        # -*- coding:Utf-8 -*-
        # 19/04/2024 13:25:08
        
        import itertools as it
        from tkinter import *
        
        def show_result(evt):
        
            words_list = []
            t1.delete(0.0, END)
        
            # Builds anagrams list
            anag_list = set("".join(e) for e in it.permutations(e1.get(), len(e1.get())))
            main_win.update()
        
            # Search
            for el in anag_list:
                for word in dico:
                    if(el == word): words_list.append(word)
        
            if(words_list != []):
                for el in words_list: t1.insert(END, el+'\n')
            else:
                t1.insert(END, 'Rien')
        
        def on_exit():
                main_win.destroy()
        
        WIDTH, HEIGHT = 320, 200
        
        # Makes a list from the dictionnary file
        dico = []
        with open('dico.txt', 'r') as f:
            for line in f: dico.append(line[:-1])
        
        main_win = Tk()
        main_win.title('Décoder une anagramme')
        main_win.geometry(str(WIDTH)+'x'+str(HEIGHT)+'+400+100')
        main_win.protocol('WM_DELETE_WINDOW', on_exit)
        
        l1 = Label(main_win, text='Anagramme :')
        l1.place(x = 10,y = 20)
        
        e1 = Entry(main_win, width = 10)
        e1.place(x = 100,y = 20)
        e1.bind("<Return>", show_result)
        e1.focus()
        
        t1 = Text(main_win, width = 30, height = 7)
        t1.place(x = 10, y = 50)
        
        main_win.mainloop()
        

        dico.txt contient 125623 mots

        un mot de 6 lettres a 720 anagrammes

        0
        1. Contributeur

          je suggère ainsi:

              for el in anag_list:
                  main_win.update()
                  if el in dico:
                      words_list.append(el)
          0
        2. Contributeur

          Il n'est pas nécessaire d'ainsi comparer tous les couples d'éléments des deux listes.  Il est préférable de trier les deux listes, puis de les parcourir ensemble, afin d'y trouver les éléments communs.

          import itertools as it
          from tkinter import *
          
          def show_result(evt):
          
              words_list = []
              t1.delete(0.0, END)
          
              # Builds anagrams list
              anag_list = list("".join(e) for e in it.permutations(e1.get(), len(e1.get())))
              anag_list.sort()
              main_win.update()
          
              # Search
              ll1,ll2=len(anag_list),len(dico)
              i1,i2=0,0
              while i1<ll1 and i2<ll2:
                  x1,x2=anag_list[i1],dico[i2]
                  if x1>x2:
                      i2 += 1
                  elif x1<x2:
                      i1 += 1
                  else:
                      words_list.append(x1)
                      i1 += 1
                      i2 += 1
              if(words_list != []):
                  for el in words_list: t1.insert(END, el+'\n')
              else:
                  t1.insert(END, 'Rien')
          def on_exit():
                  main_win.destroy()
          
          WIDTH, HEIGHT = 320, 200
          
          # Makes a list from the dictionnary file
          dico = []
          with open('dico.txt', 'r') as f:
              for line in f: dico.append(line[:-1])
          dico.sort()
          
          main_win = Tk()
          main_win.title('Décoder une anagramme')
          main_win.geometry(str(WIDTH)+'x'+str(HEIGHT)+'+400+100')
          main_win.protocol('WM_DELETE_WINDOW', on_exit)
          
          l1 = Label(main_win, text='Anagramme :')
          l1.place(x = 10,y = 20)
          
          e1 = Entry(main_win, width = 10)
          e1.place(x = 100,y = 20)
          e1.bind("<Return>", show_result)
          e1.focus()
          
          t1 = Text(main_win, width = 30, height = 7)
          t1.place(x = 10, y = 50)
          
          main_win.mainloop()
          
          0
        3. Contributeur
          @yg_be

          Cela permet de comparer seulement une ou deux fois chaque mot du dico, plutôt que de comparer chaque mot à chaque anagramme possible.

          0
      3. Bonjour yg_be,

        C'est bien ça: j'ai fait le test avant d'avoir lu ta réponse, mais après avoir lu

        celle de 11h52

        Ca fonctionne bien

        Merci encore à tous, problème résolu !

        0
        1. Contributeur

          Tu auras vu aussi qu'il est préférable d'utiliser "if ... in ..." plutôt que faire une boucle.

          0
      4. Je pense que l'on peut aussi optimiser le code en créant un dictionnaire à partir de la liste de mots ayant pour clé la longeur et de créer ana_set au fur et à mesure de l'itération pour contrôler les boublons:

        import itertools as it
        from tkinter import *
        
        
        def show_result(evt):
            t1.delete(0.0, END)
            it_perm = it.permutations(e1.get())
            anag_set = set()
            for perm in it_perm:
                word = "".join(perm)
                if not len(word) in dico.keys():
                    break
                if word in dico[len(word)] and word not in anag_set:
                    t1.insert(END, word+"\n")
                    anag_set.add(word)
            if not anag_set:
                t1.insert(END, 'Rien')
        
        def on_exit():
            main_win.destroy()
        
        WIDTH, HEIGHT = 320, 200
        
        dico = {}
        with open('dico.txt', 'r') as f:
            for line in f:
                line = line.strip()
                longueur_mot = len(line)
                if longueur_mot not in dico:
                    dico[longueur_mot] = [line]
                else:
                    dico[longueur_mot].append(line)
        
        main_win = Tk()
        main_win.title('Décoder une anagramme')
        main_win.geometry(f"{WIDTH}x{HEIGHT}+400+100")
        main_win.protocol('WM_DELETE_WINDOW', on_exit)
        
        l1 = Label(main_win, text='Anagramme :')
        l1.place(x=10, y=20)
        
        e1 = Entry(main_win, width=10)
        e1.place(x=100, y=20)
        e1.bind("<Return>", show_result)
        e1.focus()
        
        t1 = Text(main_win, width=30, height=7)
        t1.place(x=10, y=50)
        
        main_win.mainloop()
        

        je pense que ça peut être encore optimiser

        0
        1. Contributeur

          Toutes les permutation ayant la même longueur:

          def show_result(evt):
              t1.delete(0.0, END)
              it_perm = it.permutations(e1.get())
              lenw=len(e1.get())
              anag_set = set()
              if lenw in dico.keys():
                  dicol=dico[lenw]
                  for perm in it_perm:
                      word = "".join(perm)
                      if word in dicol:
                          t1.insert(END, word+"\n")
                          anag_set.add(word)
              if not anag_set:
                  t1.insert(END, 'Rien')
          0
        2. @yg_be

          Tu ne tiens pas compte des doublons générés par it.permutations() pour l'affichage, autrement anag_set ne sert pas à grand-chose.

          0
        3. Contributeur
          @Diablo76

          Pourquoi tester si chacun des doublons est dans le dico?

          Mieux de les éliminer dès le départ:

          it_perm = set(it.permutations(e1.get()))
          0
      5. J'ai fini par faire ceci, et ça marche pas mal:

        # -*- coding:Utf-8 -*-
        # 19/04/2024 13:25:08
        
        import itertools as it
        from time import perf_counter
        from tkinter import *
        
        def time_format(_time):
        
            units = 's'
            if(_time > 60.0): _time, units = _time/60.0, 'mn'
        
            return(_time,units)
        
        def show_result(evt):
        
            # Makes a list from the dictionnary file
            dico = []
            with open('dico.txt', 'r') as f:
                for line in f: 
                    if(len(line[:-1]) == len(e1.get())): dico.append(line[:-1])
        
            words_list = []
            t1.delete(0.0, END)
            l3['text'] = 'Temps passé :               '
        
            # Builds anagrams list avoiding doubles
            anag_list = set("".join(e) for e in it.permutations(e1.get(), len(e1.get())))
        
            estimated_t, units = time_format((len(anag_list) * len(dico))/LOOPS_PER_SECOND)
            l2['text'] = 'Temps estimé : {:.2f} {}'.format(estimated_t, units)
        
            # Search
            t1.configure(cursor = 'wait')
            start = perf_counter()
            for el in anag_list:
                main_win.update()
                if(el in dico): t1.insert(END, el+'\n')
        
            end = perf_counter()
            t1.configure(cursor = '')
        
            elapsed_t, units = time_format(end-start)
            l3['text'] = f'Temps passé: {elapsed_t: 0.2f} {units}    '
        
        def on_exit():
                main_win.destroy()
        
        LOOPS_PER_SECOND = 16056487
        VOWELS = 'aeiouy'
        WIDTH, HEIGHT = 320, 200
        
        main_win = Tk()
        main_win.title('Décoder une anagramme')
        main_win.geometry(str(WIDTH)+'x'+str(HEIGHT)+'+400+100')
        main_win.protocol('WM_DELETE_WINDOW', on_exit)
        
        l1 = Label(main_win, text='Anagramme :')
        l1.place(x = 10,y = 20)
        
        e1 = Entry(main_win, width = 10)
        e1.place(x = 100,y = 20)
        e1.bind("<Return>", show_result)
        e1.focus()
        
        l2 = Label(main_win, text='Temps estimé :')
        l2.place(x = 170,y = 20)
        
        t1 = Text(main_win, width = 30, height = 7)
        t1.place(x = 10, y = 50)
        
        l3 = Label(main_win, text='Temps passé :')
        l3.place(x = 10,y = 170)
        
        main_win.mainloop()
        
        0
        1. Chez moi ça ne marche pas.

          Traceback :

          Traceback (most recent call last):
            File "/usr/lib/python3.10/tkinter/__init__.py", line 1921, in __call__
              return self.func(*args)
            File "/home/diablo76/Bureau/Test_Python/pythonTest/bon.py", line 39, in show_result
              t1.configure(cursor = 'wait')
            File "/usr/lib/python3.10/tkinter/__init__.py", line 1675, in configure
              return self._configure('configure', cnf, kw)
            File "/usr/lib/python3.10/tkinter/__init__.py", line 1665, in _configure
              self.tk.call(_flatten((self._w, cmd)) + self._options(cnf))
          _tkinter.TclError: bad cursor spec "wait"
          0
        2. Contributeur

          dommage de relire ainsi le fichier à chaque fois.

          0
        3. @yg_be

          Absolument d'accord et avec mes mixt's que l'on t'a fait , tu as une solution

          0