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. 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
  2. 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
  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. 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
  5. 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
  6. 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 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