Fermer une fenêtre Tk en cours de process

Phil_1857 - 30 avril 2024 à 10:15
Diablo76 Messages postés 161 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 1 mai 2024 - 1 mai 2024 à 16:20

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

Diablo76 Messages postés 161 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 1 mai 2024 41
30 avril 2024 à 13:48

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480
30 avril 2024 à 14:32

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

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480
1 mai 2024 à 11:52

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

0

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480
1 mai 2024 à 12:31

je suggère ainsi:

    for el in anag_list:
        main_win.update()
        if el in dico:
            words_list.append(el)
0
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480
1 mai 2024 à 13:06

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480 > yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024
1 mai 2024 à 13:25

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

0

Vous n’avez pas trouvé la réponse que vous recherchez ?

Posez votre question

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480
1 mai 2024 à 13:09

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

0
Diablo76 Messages postés 161 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 1 mai 2024 41
Modifié le 1 mai 2024 à 14:14

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480
1 mai 2024 à 14:28

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
Diablo76 Messages postés 161 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 1 mai 2024 41 > yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024
Modifié le 1 mai 2024 à 14:59

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480 > Diablo76 Messages postés 161 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 1 mai 2024
1 mai 2024 à 16:11

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

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
Diablo76 Messages postés 161 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 1 mai 2024 41
1 mai 2024 à 15:32

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
yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024 1 480
1 mai 2024 à 16:14

dommage de relire ainsi le fichier à chaque fois.

0
Diablo76 Messages postés 161 Date d'inscription vendredi 25 novembre 2022 Statut Membre Dernière intervention 1 mai 2024 41 > yg_be Messages postés 22756 Date d'inscription lundi 9 juin 2008 Statut Contributeur Dernière intervention 5 mai 2024
1 mai 2024 à 16:20

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

0