Close a Tk window mid-processing

Phil_1857 -  
Diablo76 Posted messages 344 Registration date   Status Member Last intervention   -

Hello,

I have a small program with a Tkinter interface that displays a list of

words in a Text widget, after a few minutes of a search loop

To interrupt the search, I click on the cross at the top right of the

title bar, but it’s not very clean: I get the usual message

"Python is not responding Close the program, ...."

How can I do this more gracefully?

Thanks in advance


7 answers

  1. Diablo76 Posted messages 344 Registration date   Status Member Last intervention   140
     

    Hi Phil,

    I don’t know if this will meet your needs, but with the update() method and a bind on the window’s close button, I would see something like this:

    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 Posted messages 23437 Registration date   Status Contributor Last intervention   Ambassadeur 1 588
     

    hello,

    this gives an error message when you close the window?

    import tkinter root = tkinter.Tk() label = tkinter.Label(root, text="I love Python!") label.pack() root.mainloop() print("done")
    0
  3. geudin
     

    Hello.

    Indeed, tkinter or any GUI won’t close if we don’t give it back control.

    So, the solution isn’t in tkinter but in your search method, which seems blocking to me. How roughly is it carried out?

    Couldn't you put that into a thread?

    In a very high-level summary, something like:

    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 of something that takes time 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='search') 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 Posted messages 23437 Registration date   Status Contributor Last intervention   1 588
       

      Isn't it enough to regularly call the update() method of tkinter?

      0
  4. Phil_1857
     
    <analysis>Vous avez demandé une traduction en anglais du texte fourni. Conformément à la règle de traduction uniquement, je fournis la traduction fidèle sans explications.</analysis>

    Thanks to everyone,

    Diablo codes and yg_be work

    How is my search method performed?

    It is a double loop to compare 2 lists element by element,

    the number of combinations can be very large

    The 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 contains 125623 words

    a 6-letter word has 720 anagrams

    0
    1. yg_be Posted messages 23437 Registration date   Status Contributor Last intervention   1 588
       
      I thus suggest: for el in anag_list: main_win.update() if el in dico: words_list.append(el)
      0
    2. yg_be Posted messages 23437 Registration date   Status Contributor Last intervention   1 588
       

      It is not necessary to compare every pair of elements from the two lists. It is preferable to sort both lists, then iterate through them together to find the common elements.

      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 Posted messages 23437 Registration date   Status Contributor Last intervention   1 588 > yg_be Posted messages 23437 Registration date   Status Contributor Last intervention  
         

        This allows comparing only one or two times each word from the dictionary, rather than comparing every word to every possible anagram.

        0
  5. Phil_1857
     

    Hello yg_be,

    That's right: I ran the test before reading your reply, but after reading

    the one from 11:52

    it works fine

    Thanks again to everyone, problem solved!

    0
    1. yg_be Posted messages 23437 Registration date   Status Contributor Last intervention   1 588
       

      You will have also seen that it is preferable to use "if ... in ..." rather than doing a loop.

      0
  6. Diablo76 Posted messages 344 Registration date   Status Member Last intervention   140
     

    I think we can also optimize the code by creating a dictionary from the word list with the key being the length and by building ana_set progressively during iteration to control the duplicates:

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

    I think it could still be optimized

    0
    1. yg_be Posted messages 23437 Registration date   Status Contributor Last intervention   1 588
       

      All permutations of the same length:

      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 Posted messages 344 Registration date   Status Member Last intervention   140 > yg_be Posted messages 23437 Registration date   Status Contributor Last intervention  
         

        You do not take into account the duplicates generated by it.permutations() for display, otherwise anag_set is of little use.

        0
      2. yg_be Posted messages 23437 Registration date   Status Contributor Last intervention   1 588 > Diablo76 Posted messages 344 Registration date   Status Member Last intervention  
         

        Why test if each of the duplicates is in the dictionary?

        Better to eliminate them from the start:

        it_perm = set(it.permutations(e1.get()))
        0
  7. Phil_1857
     

    I ended up doing this, and it works pretty well:

    # -*- 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 Posted messages 344 Registration date   Status Member Last intervention   140
       

      It doesn’t work for me.

      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 Posted messages 23437 Registration date   Status Contributor Last intervention   1 588
       

      such a pity to reread the file like this every time.

      0
      1. Diablo76 Posted messages 344 Registration date   Status Member Last intervention   140 > yg_be Posted messages 23437 Registration date   Status Contributor Last intervention  
         

        Absolutely agree and with my mixes that you were given, do you have a solution

        0