Fermer une fenêtre Tk en cours de process

Phil_1857 -  
Diablo76 Messages postés 344 Date d'inscription   Statut Membre Dernière intervention   -

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. Diablo76 Messages postés 344 Date d'inscription   Statut Membre Dernière intervention   140
     

    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
  2. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   Ambassadeur 1 588
     

    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
  3. geudin
     

    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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       

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

      0
  4. Phil_1857
     

    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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       

      je suggère ainsi:

          for el in anag_list:
              main_win.update()
              if el in dico:
                  words_list.append(el)
      0
    2. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       

      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
      1. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588 > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
         

        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. Vous n’avez pas trouvé la réponse que vous recherchez ?

    Posez votre question
  6. Phil_1857
     

    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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       

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

      0
  7. Diablo76 Messages postés 344 Date d'inscription   Statut Membre Dernière intervention   140
     

    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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       

      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
      1. Diablo76 Messages postés 344 Date d'inscription   Statut Membre Dernière intervention   140 > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
         

        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
      2. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588 > Diablo76 Messages postés 344 Date d'inscription   Statut Membre Dernière intervention  
         

        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
  8. Phil_1857
     

    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. Diablo76 Messages postés 344 Date d'inscription   Statut Membre Dernière intervention   140
       

      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. yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention   1 588
       

      dommage de relire ainsi le fichier à chaque fois.

      0
      1. Diablo76 Messages postés 344 Date d'inscription   Statut Membre Dernière intervention   140 > yg_be Messages postés 23437 Date d'inscription   Statut Contributeur Dernière intervention  
         

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

        0