Close a Tk window mid-processing
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
-
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() -
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") -
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()?
-
<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
-
-
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()
-
-
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!
-
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
-
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')
-
-
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()-
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" -
-