OVH Cloud OVH Cloud

Probleme threads

10 réponses
Avatar
j.cormouls
Nouveau sur Python, j'ai =E9crit un petit programme, ou un point se=20
d=E9place seul et ou l'utilisateur pilote une croix avec la souris.
Le programme plante r=E9guli=E8rement en donnant des messages d'erreur qui=20
me semblent aberrants..
J'ai l'impression qu'il y a un conflit entre les deux threads, mais je=20
ne trouve pas.
Pourriez vous m'aider ?
Merci

Voici le code :

from Tkinter import *
from math import *
import time,threading

class App(Tk):
def __init__(self):
Tk.__init__(self)

self.geometry('1024x768+0+0')
self.title("Essai")
can=3DCanvas(self,width=3D600,height=3D600,bg=3D'ivory')
can.grid(row=3D1,column=3D2)

cible=3Dcan.create_oval(0,0,0,0,fill=3D'blue')
ligneh=3Dcan.create_line(270,300,330,300,width=3D3)
lignev=3Dcan.create_line(300,270,300,330,width=3D3)

valCible=3DLabel(self,text=3D"")
valCible.grid(row=3D3,column=3D2)

valViseur=3DLabel(self,text=3D"0/100")
valViseur.grid(row=3D4,column=3D2)






Thread_cible(can,cible,valCible).start()

Thread_viseur(can,ligneh,lignev,valViseur).start()





class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur):
threading.Thread.__init__(self)
self.can,self.dessin,self.valCible=3Dcanevas,dessin,valeur


def run(self):
t=3D0.0
while 1:
t=3Dt+0.001
xa=3D280*sin(t)/(1+cos(t)**2)
ya=3D650*sin(t)*cos(t)/(1+cos(t)**2)
x=3Dint(ya+300)
y=3Dint(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text=3D"cible:"+str(x)+"/"+str(y))
time.sleep(0.010)


class Thread_viseur(threading.Thread):
def __init__(self,canevas,dessinh,dessinv,viseur):
threading.Thread.__init__(self)
=20
self.can,self.dessinh,self.dessinv,self.valViseur=3Dcanevas,dessinh,dessi
nv,viseur


def run(self):


def mouseMove(event):
x2,y2=3Devent.x,event.y

self.can.coords(self.dessinh,x2-30,y2,x2+30,y2)
self.can.coords(self.dessinv,x2,y2+30,x2,y2-30)
=20
self.valViseur.configure(text=3D"viseur=3D"+str(x2)+"/"+str(y2))


def touche(event) :
"hgkhghg"


#Recepteur d'evenements (souris)
self.can.bind("<Button1-Motion>",mouseMove)
self.can.bind_all('<Key>',touche) #Recepteur=20
d'evenements (clavier)

10 réponses

Avatar
hg
wrote:

from Tkinter import *
from math import *
import time,threading

class App(Tk):
def __init__(self):
Tk.__init__(self)

self.geometry('1024x768+0+0')
self.title("Essai")
canÊnvas(self,width`0,height`0,bg='ivory')
can.grid(row=1,column=2)

cibleÊn.create_oval(0,0,0,0,fill='blue')
lignehÊn.create_line(270,300,330,300,width=3)
lignevÊn.create_line(300,270,300,330,width=3)

valCible=Label(self,text="")
valCible.grid(row=3,column=2)

valViseur=Label(self,text="0/100")
valViseur.grid(row=4,column=2)






Thread_cible(can,cible,valCible).start()

Thread_viseur(can,ligneh,lignev,valViseur).start()





class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur):
threading.Thread.__init__(self)
self.can,self.dessin,self.valCibleÊnevas,dessin,valeur


def run(self):
t=0.0
while 1:
t=t+0.001
xa(0*sin(t)/(1+cos(t)**2)
yae0*sin(t)*cos(t)/(1+cos(t)**2)
x=int(ya+300)
y=int(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text="cible:"+str(x)+"/"+str(y))
time.sleep(0.010)


class Thread_viseur(threading.Thread):
def __init__(self,canevas,dessinh,dessinv,viseur):
threading.Thread.__init__(self)

self.can,self.dessinh,self.dessinv,self.valViseurÊnevas,dessinh,dessi
nv,viseur


def run(self):


def mouseMove(event):
x2,y2=event.x,event.y

self.can.coords(self.dessinh,x2-30,y2,x2+30,y2)
self.can.coords(self.dessinv,x2,y2+30,x2,y2-30)

self.valViseur.configure(text="viseur="+str(x2)+"/"+str(y2))


def touche(event) :
"hgkhghg"


#Recepteur d'evenements (souris)
self.can.bind("<Button1-Motion>",mouseMove)
self.can.bind_all('<Key>',touche)        #Recepteur
d'evenements (clavier)




1) Peux-tu stp envoyer un source sans tab ou autre ... difficile de lancer
ton code
2) quelle erreur ?
3) ça fait quoi:
def touche (event):
"hqsdjhqjhd"


hg

Avatar
Olivier Ravard
wrote:
Nouveau sur Python, j'ai écrit un petit programme, ou un point se
déplace seul et ou l'utilisateur pilote une croix avec la souris.
Le programme plante régulièrement en donnant des messages d'erreur qui
me semblent aberrants..
J'ai l'impression qu'il y a un conflit entre les deux threads, mais je
ne trouve pas.
Pourriez vous m'aider ?


Pour moi, le script marche.

Mais si tu as des problèmes, a priori, il s'agirait d'un problème classique
d'interaction des threads avec le graphique. Les calculs peuvent être réalisés
par les threads mais la mise à jour graphique doit être faite par le thread
principal (commandes graphiques dans une file d'attente par exemple).

Merci

Voici le code :

from Tkinter import *
from math import *
import time,threading

class App(Tk):
def __init__(self):
Tk.__init__(self)

self.geometry('1024x768+0+0')
self.title("Essai")
canÊnvas(self,width`0,height`0,bg='ivory')
can.grid(row=1,column=2)

cibleÊn.create_oval(0,0,0,0,fill='blue')
lignehÊn.create_line(270,300,330,300,width=3)
lignevÊn.create_line(300,270,300,330,width=3)

valCible=Label(self,text="")
valCible.grid(row=3,column=2)

valViseur=Label(self,text="0/100")
valViseur.grid(row=4,column=2)






Thread_cible(can,cible,valCible).start()

Thread_viseur(can,ligneh,lignev,valViseur).start()





class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur):
threading.Thread.__init__(self)
self.can,self.dessin,self.valCibleÊnevas,dessin,valeur


def run(self):
t=0.0
while 1:
t=t+0.001
xa(0*sin(t)/(1+cos(t)**2)
yae0*sin(t)*cos(t)/(1+cos(t)**2)
x=int(ya+300)
y=int(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text="cible:"+str(x)+"/"+str(y))
time.sleep(0.010)


class Thread_viseur(threading.Thread):
def __init__(self,canevas,dessinh,dessinv,viseur):
threading.Thread.__init__(self)

self.can,self.dessinh,self.dessinv,self.valViseurÊnevas,dessinh,dessi
nv,viseur


def run(self):


def mouseMove(event):
x2,y2=event.x,event.y

self.can.coords(self.dessinh,x2-30,y2,x2+30,y2)
self.can.coords(self.dessinv,x2,y2+30,x2,y2-30)

self.valViseur.configure(text="viseur="+str(x2)+"/"+str(y2))


def touche(event) :
"hgkhghg"


#Recepteur d'evenements (souris)
self.can.bind("<Button1-Motion>",mouseMove)
self.can.bind_all('<Key>',touche) #Recepteur
d'evenements (clavier)



Avatar
Eric Brunel
On Tue, 30 Jan 2007 20:39:44 +0100, wrote:

Nouveau sur Python, j'ai écrit un petit programme, ou un point se
déplace seul et ou l'utilisateur pilote une croix avec la souris.
Le programme plante régulièrement en donnant des messages d'erreur qui
me semblent aberrants..
J'ai l'impression qu'il y a un conflit entre les deux threads, mais je
ne trouve pas.


Tu ne trouveras pas: ce n'est pas dans ton code. C'est un problème de
compatibilité entre les threads et Tkinter. C'est apparemment le cas avec
la plupart des toolkits d'interfce graphique: ils n'aiment pas du tout
être appelés à partir de deux threads différents. Si on le fait, ça
provoque des plantages incompréhensibles comme ceux qui tu sembles avoir.

Pour ce que tu cherches à faire, il vaut mieux éviter les threads. Tkinter
a une possibilité beaucoup plus simple à utiliser qui permet de lancer du
code régulièrement sans bloquer les événements utilisateurs. Regarde la
méthode "after" sur les widgets Tkinter; elle te permettra de faire ce que
tu veux sans problème, sans utiliser aucun thread.

Si tu veux absolument utiliser les threads, passes par un binding sur un
événement utilisateur dans le thread principal pour faire les actions, et
génère cet événement dans le thread secondaire. Le binding se fait par
exemple par:
can.bind('<<DeplaceCible>>', self.deplaceCible)
et la génération (dans the thread) par:
self.can.event_generate('<<DeplaceCible>>', when='tail')

Concernant ton code, je plussoie sur ce qu'a dit hg: ce n'est pas très
lisible tout ça, et il y a pas mal de code mort. Par ailleurs, concernant
ton "thread" Thread_viseur:
class Thread_viseur(threading.Thread):
def __init__(self,canevas,dessinh,dessinv,viseur):
threading.Thread.__init__(self)
self.can,self.dessinh,self.dessinv,self.valViseurÊnevas,dessinh,dessi
nv,viseur


(Sur plusieurs lignes, stp: c'est complètement illisible comme çà. Si tu
veux faire du code illisible: http://www.perl.org/ ;-) )



def run(self):


def mouseMove(event):


Pourquoi cette fonction est-elle a l'intérieur de la méthode run? Ca sert
à quoi? Elle n'utilise de toutes façons que ses paramètres et des
attributs, donc elle serait aussi bien comme méthode de la classe. Ce
serait beaucoup plus facile à comprendre et ça permettrait aussi d'éviter
les pièges usuels des "nested scopes" de Python...

x2,y2=event.x,event.y

self.can.coords(self.dessinh,x2-30,y2,x2+30,y2)
self.can.coords(self.dessinv,x2,y2+30,x2,y2-30)
self.valViseur.configure(text="viseur="+str(x2)+"/"+str(y2))


def touche(event) :
"hgkhghg"


#Recepteur d'evenements (souris)
self.can.bind("<Button1-Motion>",mouseMove)
self.can.bind_all('<Key>',touche) #Recepteur
d'evenements (clavier)


Un thread ne sert à rien si il n'y a pas une boucle ou un traitement long
dedans. Là, il y a deux appels de méthodes et c'est tout. Tout ce code
serait aussi bien dans App.__init__: ça ferait exactement la même chose.
Du coup, je me demande si tu as bien compris le principe des bindings en
Tkinter...

HTH
--
python -c "print ''.join([chr(154 - ord(c)) for c in
'U(17zX(%,5.zmz5(17l8(%,5.Z*(93-965$l7+-'])"

Avatar
hg
wrote:

Comme je le disais sur mon premier post, je débute en python,
donc il est fort possible que je n'ai pas bien compris le principe des
bindings en Tkinter...

En tout cas, merci pour vos explications et conseils avisés qui me
permettront de faire des progrès (peut être)...
et excusez moi pour les problèmes de visibilité que je résoudrai dans
mes prochains posts.

Pour ce que tu cherches à faire, il vaut mieux éviter les threads. Tkinter
a une possibilité beaucoup plus simple à utiliser qui permet de lancer du
code régulièrement sans bloquer les événements utilisateurs. Regarde la
méthode "after" sur les widgets Tkinter; elle te permettra de faire ce que
tu veux sans problème, sans utiliser aucun thread.


Dans une première version, j'avais utilisé cette méthode qui
fonctionnait,
mais je pensais plus élégant les threads (à tort peut être) et
surtout, je pensais
pouvoir plus facilement gérer la vitesse de déplacement...

Jerome


"...à tort peut être..."

Pour moi c'est plus élégant /portable: si tu passe à une autre librairie
graphique, tu limite tes chances de te planter.

hg


Avatar
j.cormouls
Comme je le disais sur mon premier post, je débute en python,
donc il est fort possible que je n'ai pas bien compris le principe des
bindings en Tkinter...

En tout cas, merci pour vos explications et conseils avisés qui me
permettront de faire des progrès (peut être)...
et excusez moi pour les problèmes de visibilité que je résoudrai dans
mes prochains posts.

Pour ce que tu cherches à faire, il vaut mieux éviter les threads. Tki nter
a une possibilité beaucoup plus simple à utiliser qui permet de lancer du
code régulièrement sans bloquer les événements utilisateurs. Regar de la
méthode "after" sur les widgets Tkinter; elle te permettra de faire ce q ue
tu veux sans problème, sans utiliser aucun thread.


Dans une première version, j'avais utilisé cette méthode qui
fonctionnait,
mais je pensais plus élégant les threads (à tort peut être) et
surtout, je pensais
pouvoir plus facilement gérer la vitesse de déplacement...

Jerome

Avatar
hg
wrote:

En essayant de tenir compte de toutes vos remarques,
voici le code modifié et qui semble fonctionner.
Je reste preneur de tous vos commentaires pour progresser.
Merci

# -*- coding: cp1252 -*-
from Tkinter import *
from math import *
import os
import wx
import time,threading


class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur):
threading.Thread.__init__(self)
self.can,self.dessin,self.valCibleÊnevas,dessin,valeur

def run(self):
t=0.0
while 1:
t=t+0.005
xa(0*sin(t)/(1+cos(t)**2)
yae0*sin(t)*cos(t)/(1+cos(t)**2)
x=int(ya+300)
y=int(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text="cible:"+str(x)+"/"+str(y))
time.sleep(0.001)




class App(Tk):
def __init__(self):
Tk.__init__(self)

self.geometry('1024x768+0+0')
self.title("ADI Version 2006")
canÊnvas(self,width`0,height`0,bg='ivory')
can.grid(row=1,column=2)

cibleÊn.create_oval(0,0,0,0,fill='blue')
lignehÊn.create_line(270,300,330,300,width=3)
lignevÊn.create_line(300,270,300,330,width=3)

valCible=Label(self,text="")
valCible.grid(row=3,column=2)

valViseur=Label(self,text="0/100")
valViseur.grid(row=4,column=2)



def mouseMove(event):
global x2,y2
x2,y2=event.x,event.y
can.coords(ligneh,x2-30,y2,x2+30,y2)
can.coords(lignev,x2,y2+30,x2,y2-30)
valViseur.configure(text=str(x2)+"/"+str(y2))

#Recepteur d'evenements (souris)
can.bind("<Button1-Motion>",mouseMove)

Thread_cible(can,cible,valCible).start()


if __name__=="__main__":
App().mainloop()


Tu remarque que ton application tourne toujours après la fermeture de la
fenêtre:

# -*- coding: cp1252 -*-
from Tkinter import *
from math import *
import os
import wx
import time,threading


class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur, p_parent):
threading.Thread.__init__(self)
self.m_parent = p_parent
self.can,self.dessin,self.valCibleÊnevas,dessin,valeur

def run(self):
t=0.0
while False == self.m_parent.m_quit:
t=t+0.005
xa(0*sin(t)/(1+cos(t)**2)
yae0*sin(t)*cos(t)/(1+cos(t)**2)
x=int(ya+300)
y=int(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text="cible:"+str(x)+"/"+str(y))
time.sleep(0.001)




class App(Tk):
def __init__(self):
Tk.__init__(self)


self.geometry('1024x768+0+0')
self.title("ADI Version 2006")
canÊnvas(self,width`0,height`0,bg='ivory')
can.grid(row=1,column=2)

cibleÊn.create_oval(0,0,0,0,fill='blue')
lignehÊn.create_line(270,300,330,300,width=3)
lignevÊn.create_line(300,270,300,330,width=3)

valCible=Label(self,text="")
valCible.grid(row=3,column=2)

valViseur=Label(self,text="0/100")
valViseur.grid(row=4,column=2)

#Recepteur d'evenements (souris)
can.bind("<Button1-Motion>",self.mouseMove)
can.bind("<Destroy>",self.Destroy)


self.m_quit = False
self.m_thread = Thread_cible(can,cible,valCible,self)
self.m_thread.start()


def Destroy(self,event):
print 'QUITTING'
self.m_quit = True
self.m_thread.join()



def mouseMove(self,event):
# global x2,y2
x2,y2=event.x,event.y
can.coords(ligneh,x2-30,y2,x2+30,y2)
can.coords(lignev,x2,y2+30,x2,y2-30)
valViseur.configure(text=str(x2)+"/"+str(y2))





if __name__=="__main__":
App().mainloop()

Avatar
hg
hg wrote:

wrote:

En essayant de tenir compte de toutes vos remarques,
voici le code modifié et qui semble fonctionner.
Je reste preneur de tous vos commentaires pour progresser.
Merci

# -*- coding: cp1252 -*-
from Tkinter import *
from math import *
import os
import wx
import time,threading


class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur):
threading.Thread.__init__(self)
self.can,self.dessin,self.valCibleÊnevas,dessin,valeur

def run(self):
t=0.0
while 1:
t=t+0.005
xa(0*sin(t)/(1+cos(t)**2)
yae0*sin(t)*cos(t)/(1+cos(t)**2)
x=int(ya+300)
y=int(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text="cible:"+str(x)+"/"+str(y))
time.sleep(0.001)




class App(Tk):
def __init__(self):
Tk.__init__(self)

self.geometry('1024x768+0+0')
self.title("ADI Version 2006")
canÊnvas(self,width`0,height`0,bg='ivory')
can.grid(row=1,column=2)

cibleÊn.create_oval(0,0,0,0,fill='blue')
lignehÊn.create_line(270,300,330,300,width=3)
lignevÊn.create_line(300,270,300,330,width=3)

valCible=Label(self,text="")
valCible.grid(row=3,column=2)

valViseur=Label(self,text="0/100")
valViseur.grid(row=4,column=2)



def mouseMove(event):
global x2,y2
x2,y2=event.x,event.y
can.coords(ligneh,x2-30,y2,x2+30,y2)
can.coords(lignev,x2,y2+30,x2,y2-30)
valViseur.configure(text=str(x2)+"/"+str(y2))

#Recepteur d'evenements (souris)
can.bind("<Button1-Motion>",mouseMove)

Thread_cible(can,cible,valCible).start()


if __name__=="__main__":
App().mainloop()


Tu remarque que ton application tourne toujours après la fermeture de la
fenêtre:

# -*- coding: cp1252 -*-
from Tkinter import *
from math import *
import os
import wx
import time,threading


class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur, p_parent):
threading.Thread.__init__(self)
self.m_parent = p_parent
self.can,self.dessin,self.valCibleÊnevas,dessin,valeur

def run(self):
t=0.0
while False == self.m_parent.m_quit:
t=t+0.005
xa(0*sin(t)/(1+cos(t)**2)
yae0*sin(t)*cos(t)/(1+cos(t)**2)
x=int(ya+300)
y=int(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text="cible:"+str(x)+"/"+str(y))
time.sleep(0.001)




class App(Tk):
def __init__(self):
Tk.__init__(self)


self.geometry('1024x768+0+0')
self.title("ADI Version 2006")
canÊnvas(self,width`0,height`0,bg='ivory')
can.grid(row=1,column=2)

cibleÊn.create_oval(0,0,0,0,fill='blue')
lignehÊn.create_line(270,300,330,300,width=3)
lignevÊn.create_line(300,270,300,330,width=3)

valCible=Label(self,text="")
valCible.grid(row=3,column=2)

valViseur=Label(self,text="0/100")
valViseur.grid(row=4,column=2)

#Recepteur d'evenements (souris)
can.bind("<Button1-Motion>",self.mouseMove)
can.bind("<Destroy>",self.Destroy)


self.m_quit = False
self.m_thread = Thread_cible(can,cible,valCible,self)
self.m_thread.start()


def Destroy(self,event):
print 'QUITTING'
self.m_quit = True
self.m_thread.join()



def mouseMove(self,event):
# global x2,y2
x2,y2=event.x,event.y
can.coords(ligneh,x2-30,y2,x2+30,y2)
can.coords(lignev,x2,y2+30,x2,y2-30)
valViseur.configure(text=str(x2)+"/"+str(y2))





if __name__=="__main__":
App().mainloop()



PS: ça serait plus propre d'utiliser une Queue au lieu d'un simple flag

hg


Avatar
hg
wrote:

Merci beaucoup pour cette "astuce" que je vais m'efforcer
de comprendre...


au moment de l'évènement "Destroy", tu dis à ta thread de quitter "run" puis
tu attends (join() ) que ce soit fait.

hg

Avatar
j.cormouls
En essayant de tenir compte de toutes vos remarques,
voici le code modifié et qui semble fonctionner.
Je reste preneur de tous vos commentaires pour progresser.
Merci

# -*- coding: cp1252 -*-
from Tkinter import *
from math import *
import os
import wx
import time,threading


class Thread_cible(threading.Thread):
def __init__(self,canevas,dessin,valeur):
threading.Thread.__init__(self)
self.can,self.dessin,self.valCibleÊnevas,dessin,valeur

def run(self):
t=0.0
while 1:
t=t+0.005
xa(0*sin(t)/(1+cos(t)**2)
yae0*sin(t)*cos(t)/(1+cos(t)**2)
x=int(ya+300)
y=int(xa+300)


self.can.coords(self.dessin,x-4,y-4,x+4,y+4)
self.valCible.configure(text="cible:"+str(x)+"/"+str(y))
time.sleep(0.001)




class App(Tk):
def __init__(self):
Tk.__init__(self)

self.geometry('1024x768+0+0')
self.title("ADI Version 2006")
canÊnvas(self,width`0,height`0,bg='ivory')
can.grid(row=1,column=2)

cibleÊn.create_oval(0,0,0,0,fill='blue')
lignehÊn.create_line(270,300,330,300,width=3)
lignevÊn.create_line(300,270,300,330,width=3)

valCible=Label(self,text="")
valCible.grid(row=3,column=2)

valViseur=Label(self,text="0/100")
valViseur.grid(row=4,column=2)



def mouseMove(event):
global x2,y2
x2,y2=event.x,event.y
can.coords(ligneh,x2-30,y2,x2+30,y2)
can.coords(lignev,x2,y2+30,x2,y2-30)
valViseur.configure(text=str(x2)+"/"+str(y2))

#Recepteur d'evenements (souris)
can.bind("<Button1-Motion>",mouseMove)

Thread_cible(can,cible,valCible).start()


if __name__=="__main__":
App().mainloop()
Avatar
j.cormouls
Merci beaucoup pour cette "astuce" que je vais m'efforcer
de comprendre...