J'ai actuellement (encore) un peu de mal a me mettre a tkinter pour faire des interfaces graphiques.
Actuellement, j'ai un petit code (voir ci-dessous), et j'ai du mal a comprendre la gestion des boucles avec tkinter...
Donc voici le code dans son etat actuel:
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# imports
from premier import *
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from time import *
# init var
varstop=False
# functions
def launch():
"""search first numbers between 2 and var entry"""
listbox.delete(0, "end") # clear listbox
itime=time() # init time
n=entry.get() # get limit
if n.isnumeric()==False or int(n)<2:
# check inputs
messagebox.showwarning("", "insert an integer (>=2) on the entry")
else:
stop_button["state"]="normal"
launch_button["state"]="disabled"
n=int(n)
i=2
nb=0 # count number of first numbers
varprogress=0 # var of the progress bar
global varstop
while i<=n:
if varstop==True:
# if click on stop button, stop
varstop=False
return
else:
if premier(i)==True:
nb+=1
listbox.insert("end", " "+str(i))
varprogress=int((i*100/n)-varprogress+1)
progress.step(varprogress)
i+=1
listbox.insert("end", " number of first number between 1 and "+str(n)+": "+str(nb))
temp=time()-itime
listbox.insert("end", " process time: "+str(int(temp))+"s")
stop_button["state"]="disabled"
launch_button["state"]="normal"
def stop():
global varstop
varstop=True
# define gui
gui=Tk()
gui.title("Premier.py")
frame1=Frame(gui)
frame2=Frame(gui)
frame3=Frame(gui)
label=Label(frame1, text="Limit (>=2) :")
listbox=Listbox(frame2, height=25, width=50, selectmode="extended")
progress=ttk.Progressbar(frame3, length=350)
entry=Entry(frame1, width=25)
launch_button=Button(frame3, width=10, text="launch", command=launch)
stop_button=Button(frame3, width=10, text="stop", state="disabled")
# create gui
frame1.pack(expand=True)
frame2.pack(expand=True)
frame3.pack(expand=True)
label.pack(side="left")
entry.pack(side="right")
listbox.pack(side="left")
progress.pack(pady=5)
launch_button.pack(side="left", padx=50, pady=5)
stop_button.pack(side="right", padx=50, pady=5)
gui.mainloop()
Et voici une petite vidéo de ce qui se passe quand je lance le calcul (des nombres premieres entre 1 et la limite définie dans l'entry du haut de la gui)
Vous remarquerez que quand la boucle de la fonction launch() en lancée, la gui ne repond plus et est totalement inactive...
J'ai beau avoir mis des instructions pour activer les bouton stop au debut de la fonction, il reste inactivé, et le bouton launch reste activé...
De meme, les valeurs de nombres premiers trouvé s'affichent toute d'un coup et pas une par une comme je l'aurais voulu.
En meme temps, il devient impossible de gerer la barre de progression malgré les instructions du scripts...
Je me demande donc d'ou viens le probleme, comment le resoudre ?
Y a t'il des spécificités a prendre en compte pour les fonctions lancées avec un bouton via le parametre command ?
Alors, ça avance un peu, apparement, ce probleme viens du fait que Tkinter ne fait pas du double threading...
voila les solutions proposées sur le post que j'ai trouvé:
move your time-intensive code to another thread, or
move your time-intensive code to another process, or
refactor your time-intensive code into small pieces that can be done in small iterations via the event loop, or
as an inefficient and possibly dangerous work-around, call update_idletasks after every iteration of your loop, which will at least allow "idle" events to be processed (such as redrawing the GUI
J'ai essayé la 4eme, mais ça met juste a jour l'apparence de l'interface graphique donc impossible de faire un bouton stop par exemple...
Et les autres alternatives je ne comprend pas vraiment... Quelqu'un a une idée de ce que ça veut dire ?
Je pense que le code est mal parti, car ce que je peux voir c'est que tu affiches au fur et à mesure de la découverte d'un nombre premier ce nombre dans le widget, ce qui demande une quantité de mémoire énorme, niveau rafraîchissement + calculs.
Ce que je conseillerais c'est de créer un générateur des nombres premiers trouvés
Puis afficher tous les nombres premiers en une fois
Certe, je pourrais stocker les nombres premiers dans un tuple pour les afficher a la fin... Mais la on en est quand meme a un probleme d'optimisation alors meme que le script ne marche pas vraiment...
Mon vrai probleme viens de la gestion des deux boutons et du fait que l'interface bloque quand je lance la boucle...
D'apres ce que j'en ai compris, le probleme pourrait etre resolu avec du threading. Ce n'est encore une fois pas une chose que j'ai l'habitude d'utiliser, donc je vais tout de meme vous demander conseil avant de me lancer...
En fait, j'avais penser a faire, pour le bouton de lancement, eux fonctions...
J'ai pas suivi du tout le sujet, mais rapidement :
N3mesis94 a écrit:
Mais sinon, encore une fois, si tu veux mettre un bouton pour stopper le processus au milieu comment ferait tu ?
Car il faudrait dans ce cas que la mainloop ne soit pas bloquée pendant que la boucle de recherche est en cours non ?
En effet : il faut alors que ton processus s'exécute dans un thread séparé. Régulièrement tu vérifies si une variable stop (ou autre) n'est pas à True. Et dans le thread principal, tu colles un bouton dont l'action est de setter cette variable stop à True.
J'ai pas suivi du tout le sujet, mais rapidement :
N3mesis94 a écrit:
Mais sinon, encore une fois, si tu veux mettre un bouton pour stopper le processus au milieu comment ferait tu ?
Car il faudrait dans ce cas que la mainloop ne soit pas bloquée pendant que la boucle de recherche est en cours non ?
En effet : il faut alors que ton processus s'exécute dans un thread séparé. Régulièrement tu vérifies si une variable stop (ou autre) n'est pas à True. Et dans le thread principal, tu colles un bouton dont l'action est de setter cette variable stop à True.
- Edité par nohar il y a environ 1 heure
Dans ce cas, ma question serait comment ferait tu pour lancer une fonction dans un nouveau thread via un bouton Tkinter ?
Bon alors j'ai resolu le probleme et tout marche visiblement...
J'ai utilisé le threading, mais je ne sais pas si je l'ai fait d'une maniere tres "elegante"...
Sinon, je vais probablement m'attaquer maintenant a l'optimisation du programme, mais en tout cas la GUI marche nickel, il faudrait juste que je pense a rajouter une scrollbar pour la liste...
Edit:
J'ai ajouté une scrollbar et optimisé le processus de recherche et d'affichage grace aux conseils de fred (4x plus rapide tout de meme...):
#!/usr/bin/python3
# -*- coding: utf-8 -*-
# imports
from tkinter import *
from tkinter import ttk
from tkinter import messagebox
from time import *
from threading import *
# init var
varstop=False
# functions
def thread_launch():
thread=Thread(name="thread", target=launch)
thread.start()
def launch():
"""search prime numbers between 2 and var entry"""
textlist.delete("1.0", "end") # clear textlist
n=entry.get() # get limit
if n.isnumeric()==False or int(n)<2:
# check inputs
messagebox.showwarning("", "insert an integer (>=2) on the entry")
else:
# change button state
stop_button["state"]="normal"
launch_button["state"]="disabled"
n=int(n)
itime=time() # init time
i=3
nb=1 # count number of first numbers
prime_list=[2] # list of prime numbers found between 2 and the limit
global varstop
# search prime numbers loop
while i<=n and varstop==False:
j=0
prime=True
while prime==True and j<len(prime_list):
# if i is not divisable by previous prime numbers, i is a prime number
if i%prime_list[j]==0:
prime=False
j+=1
if prime==True:
nb+=1
prime_list.append(i)
varprogress=(i*100)/n
progress["value"]=int(varprogress)
i+=2 # par numbers are not prime except 2
# form and insert the text in textlist
values="\n"
if varstop==True:
values=values+" process interrupted at "+str(varprogress)+"%"+"\n"
varstop=False
values=values+" number of prime numbers between 1 and "+str(i-1)+": "+str(nb)+"\n"
temp=time()-itime
values=values+" process time: "+str(int(temp))+"s"+"\n"
for elt in prime_list:
values=values+" "+str(elt)+"\n"
textlist.insert("end",values)
stop_button["state"]="disabled"
launch_button["state"]="normal"
progress["value"]=0
# textlist.see("1.0")
def stop():
global varstop
varstop=True
# create gui
gui=Tk()
gui.title("Prime")
frame1=Frame(gui)
frame2=Frame(gui)
frame3=Frame(gui)
scrollbar=Scrollbar(frame2)
label=Label(frame1, text="Limit (>=2) :")
textlist=Text(frame2, height=25, width=55)
progress=ttk.Progressbar(frame3, length=350)
entry=Entry(frame1, width=25)
launch_button=Button(frame3, width=10, text="launch", command=thread_launch)
stop_button=Button(frame3, width=10, text="stop", state="disabled", command=stop)
# link scrollbar with textlist yview
scrollbar.config(command=textlist.yview)
textlist.config(yscrollcommand=scrollbar.set)
# print gui with pack
frame1.pack(expand=True)
frame2.pack(expand=True)
frame3.pack(expand=True)
label.pack(side="left")
entry.pack(side="right")
textlist.pack(side="left", fill="y")
scrollbar.pack(side="left", fill="y")
progress.pack(pady=5)
launch_button.pack(side="left", padx=50, pady=5)
stop_button.pack(side="right", padx=50, pady=5)
gui.mainloop()
- Edité par N3mesis94 9 juillet 2013 à 10:54:43
"Ce qui ne tue pas rend plus fort", Nietzsche
Tkinter - probleme de boucle avec command
× Après avoir cliqué sur "Répondre" vous serez invité à vous connecter pour que votre message soit publié.
× Attention, ce sujet est très ancien. Le déterrer n'est pas forcément approprié. Nous te conseillons de créer un nouveau sujet pour poser ta question.