Problème de référence de canvas sous Tkinter en Python
Fermémamiemando Messages postés 33459 Date d'inscription jeudi 12 mai 2005 Statut Modérateur Dernière intervention 8 janvier 2025 - 25 janv. 2023 à 14:24
- Problème de référence de canvas sous Tkinter en Python
- Reference pto - Accueil - Box & Connexion Internet
- Canvas gratuit - Télécharger - Divers Photo & Graphisme
- Citizen code python avis - Accueil - Outils
- Ce programme est écrit en python ✓ - Forum Python
8 réponses
Salut, ligne 18 ou à peu près. Tu ne dois pas appeler mouve, mais juste passer la référence de la fonction, et 500 ms, c'est beaucoup trop, mets 10 à 20 ms.
.
self.app.fen.after(10, self.mouve)
.
Sinon, python2 est mort, il serait plus adequat de travailler sur python3.
Sinon, move, il n'y a pas de u ^^
Modifié le 18 janv. 2023 à 14:54
Bonjour,
Ca ne résout rien, mais déjà, sais tu que pour convertir de degrés en radians,
tu peux utiliser la fonction math.radians() ?
rad = math.radians(deg)
et inversement:
deg = math.degrees(rad)
ca évite tous les self.alpha/180.*pi
pourquoi self.listeFleches[-1].mouve() ?, moi je ferais self.listeFleches[0].mouve()
Modifié le 18 janv. 2023 à 16:45
OK, mais moi je n'appellerais pas une fonction perso move, car c'est déjà un nom de méthode Python
comme dans
can.move(obj,dx,dy)
ça donne ça:
def mouve(self): self.x+=3*cos(self.alpha/180.*pi) self.y-=3*sin(self.alpha/180.*pi) self.can.coords(self.f,self.x,self.y,self.x+5*cos(self.alpha/180.*pi),self.y-5*sin(self.alpha/180.*pi)) if 0<self.x<500 and 0<self.y<500: self.can.after(10,self.mouve)
et pas besoin de liste de flèches :
def tirer(self,event): self.fleche = Fleche(self.app,self.can,self.alpha,self.x,self.y) self.fleche.mouve()
Modifié le 20 janv. 2023 à 02:08
Bonjour
Quelques recommandations :
- Essaye de suivre les conventions d'indentation PEP8 pour rendre le code plus lisible :
- Un espace derrière les virgules
- Un espace autour des opérateurs (par notamment *, +=, etc.)
- Les méthodes et fonctions s'écrivent selon ce style : ma_methode, ma_fonction pas en camel case comme pour creerArcher (qui devient creer_archer)
- Les classes s'écrivent en camel case.
- Comme suggéré par Phil
- travailler en radians allégerait le code
- python2 est désuet et ce serait mieux d'installer si possible python3 (par exemple anaconda si tu es sous windows), . Si tu le fais, il faut juste corriger ton print, car cela devient une fonction à part entière.
- Exemple : écris print(x, y, z) au lieu de : print x, y , z
- Comme suggéré par joubaho, tu devrais renommer mouve en move (il n'y a aucun risque d'ambiguïté)
- Selon moi le design du programme doit être revu. Ta classe Application devrait stocker une liste (initialement vide) d'archers et appeler en cas d'événement sur une touche une de ses méthodes qui en cascade appelle les méthodes sur les objets créés dans ton application (typiquement les archers et les flèches).
- Ainsi pas besoin de transporter le canvas dans les classes Archer et Fleche, de toute façon le canvas n'a rien à faire là car il ne caractérise ni un archer ni une flèche.
- L'appui sur une touche (m, n, t) qui dans le code actuel n'est pas fonctionnel probablement les binds correspondants être déclarés avant de lancer l'application. Il faut donc suivre le même design que ce que tu as fait pour le clic gauche.
from tkinter import * from math import sin, cos, pi class OrientedObject: def __init__(self, alpha, x, y): self.alpha = alpha self.x = x self.y = y def dessiner(self, canvas): raise NotImplementedError() class Fleche(OrientedObject): def __init__(self, *cls, **kwargs): super().__init__(*cls, **kwargs) def dessiner(self, canvas): print(f"{self}.dessiner({canvas})") canvas.create_line(self.x, self.y, self.x + 50 * cos(self.alpha / 180. * pi), self.y - 50 * sin(self.alpha / 180. * pi), arrow="last") class Archer(OrientedObject): def __init__(self, *cls, **kwargs): super().__init__(*cls, **kwargs) def monter(self): self.alpha += 1 def descendre(self,event): self.alpha -= 1 def tirer(self,event): return Fleche(self.alpha, self.x, self.y) def dessiner(self, canvas): print(f"{self}.dessiner({canvas})") canvas.create_line(self.x, self.y - 10, self.x, self.y + 30, width=2) canvas.create_line(self.x, self.y, self.x + 30 * cos(self.alpha / 180. * pi), self.y - 30 * sin(self.alpha / 180. * pi) ,width=2) canvas.create_line(self.x - 20, self.y + 60, self.x, self.y + 30, width=2) canvas.create_line(self.x + 20, self.y + 60, self.x, self.y + 30, width=2) canvas.create_oval(self.x - 10, self.y - 30, self.x + 10, self.y - 10) class Application: def __init__(self): self.root = Tk() self.canvas = Canvas(self.root, width=500, height=500, bg="beige") self.canvas.bind("<Button-1>", self.creer_archer) self.canvas.bind("<KeyPress>", self.on_key_press) self.canvas.pack() self.button = Button(self.root, text="Quitter", command=self.root.destroy) self.button.pack() self.archers = list() self.fleches = list() def creer_archer(self, event): archer = Archer(0, event.x, event.y) self.archers.append(archer) archer.dessiner(self.canvas) def on_key_press(self, event): key = event.char print("on_key_press", char) if key == "m": self.monter() elif key == "d": self.descendre() elif key == "t": self.tirer() def monter(self): for archer in self.archers: archer.monter() def descendre(self): for archer in self.archers: archer.descendre() def tirer(self): for archer in self.archers: fleche = archer.tirer() fleche.dessiner() self.fleches.append(fleche) def dessiner(self): for archer in self.archers: archer.dessiner(self.canvas) for fleche in self.fleches: fleche.dessiner(self.canvas) app = Application() app.root.mainloop()
Pardon, je manque de temps et je donne juste les grandes lignes dans l'extrait ci-dessus. Il reste à :
- déboguer les key press (chez moi, elles ne se déclenchent pas)
- reporter la partie permettant d'animer tout
Bonne chance
Vous n’avez pas trouvé la réponse que vous recherchez ?
Posez votre question19 janv. 2023 à 12:24
Bonjour,
Chez moi, les key press fonctionnent parfaitement sans rien ajouter de plus
La seule modif. que j'ai faite est de remplacer self.can.after(500,self.mouve())
par self.can.after(10,self.mouve)
Donc ceci marche :
# -*- coding:Utf-8 -*- from tkinter import * from math import sin,cos,pi class Fleche: def __init__(self,app,can,alpha,x,y): self.app=app self.can=can self.alpha=alpha self.x=x self.y=y self.f=self.can.create_line(self.x,self.y,self.x+50*cos(self.alpha/180.*pi),self.y-50*sin(self.alpha/180.*pi),arrow="last") def mouve(self): self.x+=3*cos(self.alpha/180.*pi) self.y-=3*sin(self.alpha/180.*pi) self.can.coords(self.f,self.x,self.y,self.x+5*cos(self.alpha/180.*pi),self.y-5*sin(self.alpha/180.*pi)) if 0<self.x<500 and 0<self.y<500: self.can.after(10,self.mouve) class Archer: def __init__(self,app,can,x=10,y=400,alpha=0): self.app=app self.can=can self.x=x self.y=y self.alpha=alpha self.listeFleches=[] self.can.bind_all("<KeyPress-m>",self.monter) self.can.bind_all("<KeyPress-n>",self.descendre) self.can.bind_all("<KeyPress-t>",self.tirer) def represente(self): self.corps=self.can.create_line(self.x,self.y-10,self.x,self.y+30,width=2) self.bras=self.can.create_line(self.x,self.y,self.x+30*cos(self.alpha/180.*pi),self.y-30*sin(self.alpha/180.*pi),width=2) self.jambeG=self.can.create_line(self.x-20,self.y+60,self.x,self.y+30,width=2) self.jambeD=self.can.create_line(self.x+20,self.y+60,self.x,self.y+30,width=2) self.tete=self.can.create_oval(self.x-10,self.y-30,self.x+10,self.y-10) def monter(self,event): self.alpha+=1 self.can.coords(self.bras,self.x,self.y,self.x+30*cos(self.alpha/180.*pi),self.y-30*sin(self.alpha/180.*pi)) def descendre(self,event): self.alpha-=1 self.can.coords(self.bras,self.x,self.y,self.x+30*cos(self.alpha/180.*pi),self.y-30*sin(self.alpha/180.*pi)) def tirer(self,event): self.fleche = Fleche(self.app,self.can,self.alpha,self.x,self.y) self.fleche.mouve() class Application: def __init__(self): self.appli=self self.fen= Tk() self.can = Canvas(self.fen,width=500,height=500,bg="beige") self.can.bind("<Button-1>",self.creerArcher) self.can.pack() self.b=Button(self.fen,text="Quitter",command=self.fen.quit) self.b.pack() self.fen.mainloop() def creerArcher(self,event): self.a1=Archer(self.appli,self.can,event.x,event.y) self.a1.represente() a=Application()
Modifié le 20 janv. 2023 à 02:16
Bonjour,
Suite aux messages #6, #7, #8, voici le code que je propose, et qui est fonctionnel :
from tkinter import * from math import sin, cos, pi def rad(deg): return pi * deg / 180 class OrientedObject: def __init__(self, alpha, x, y): self.alpha = alpha self.x = x self.y = y def dessiner(self, canvas): raise NotImplementedError() class Fleche(OrientedObject): def __init__(self, *cls, longueur = 50, **kwargs): if "longueur" in kwargs: self.longueur = kwargs.pop("longueur") else: self.longueur = longueur super().__init__(*cls, **kwargs) self.line = None def dessiner(self, canvas): if self.line is None: x = self.x y = self.y alpha = rad(self.alpha) self.line = canvas.create_line( x, y, x + self.longueur * cos(alpha), y - self.longueur * sin(alpha), arrow="last" ) def deplacer(self, canvas): if not self.line: self.dessiner(canvas) vitesse = 3 alpha = rad(self.alpha) dx = vitesse * cos(alpha) dy = - vitesse * sin(alpha) canvas.move(self.line, dx, dy) return 0 < self.x < 500 and 0 < self.y < 500 class Archer(OrientedObject): def __init__(self, *cls, **kwargs): super().__init__(*cls, **kwargs) self.line = None def monter(self): self.alpha += 1 print(f"monter {self.alpha}") def descendre(self): self.alpha -= 1 print(f"descendre {self.alpha}") def tirer(self): print("tirer") return Fleche(self.alpha, self.x, self.y) def dessiner(self, canvas): alpha = rad(self.alpha) x = self.x y = self.y canvas.create_line(x, y - 10, x, y + 30, width=2) self.line = canvas.create_line(x, y, x + 30 * cos(alpha), y - 30 * sin(alpha), width=2) canvas.create_line(x - 20, y + 60, x, y + 30, width=2) canvas.create_line(x + 20, y + 60, x, y + 30, width=2) canvas.create_oval(x - 10, y - 30, x + 10, y - 10) def deplacer(self, canvas): if self.line is None: self.dessiner(canvas) alpha = rad(self.alpha) x = self.x y = self.y canvas.coords(self.line, x, y, x + 30 * cos(alpha), y - 30 * sin(alpha)) class Application: def __init__(self): self.root = Tk() self.canvas = Canvas(self.root, width=500, height=500, bg="beige") self.root.bind("<Button-1>", self.creer_archer) self.root.bind("<KeyPress>", self.on_key_press) self.canvas.pack() self.button = Button(self.root, text="Quitter", command=self.root.destroy) self.button.pack() self.archers = list() self.fleches = list() def creer_archer(self, event): archer = Archer(0, event.x, event.y) self.archers.append(archer) archer.dessiner(self.canvas) def on_key_press(self, event): key = event.char if key == "m": self.monter() elif key == "d": self.descendre() elif key == "t": self.tirer() def monter(self): for archer in self.archers: archer.monter() archer.deplacer(self.canvas) def descendre(self): for archer in self.archers: archer.descendre() archer.deplacer(self.canvas) def tirer(self): for archer in self.archers: fleche = archer.tirer() fleche.dessiner(self.canvas) self.fleches.append(fleche) self.animer() def dessiner(self): for archer in self.archers: archer.dessiner(self.canvas) for fleche in self.fleches: fleche.dessiner(self.canvas) def animer(self): repeat = False for fleche in self.fleches: repeat |= fleche.deplacer(self.canvas) if repeat: self.root.after(500, self.animer) app = Application() app.root.mainloop()
Parmi les améliorations possibles, on peut envisager les points suivants :
- utiliser les flèches pour orienter le tir (plutôt que les touches M et D) et la barre espace pour tirer (plutôt que T) ;
- utiliser des noms anglais plutôt que français ;
- prévoir une liste de cases à cocher, remplie dynamiquement en fonction des archers créés, permettant d'indiquer quels archers on veut piloter avec le clavier.
Bonne chance
20 janv. 2023 à 11:07
Bonjour,
Ah ok, Tu introduis carrément de la POO...
Sinon une question bête: pourquoi une fonction rad alors qu'il existe math.radians ?
20 janv. 2023 à 12:31
Bonjour,
Ah ok, Tu introduis carrément de la POO...
Disons que dès le message #1, il y avait des classes. Du coup, de là à faire un héritage, il n'y a qu'un pas. Ça permet de factoriser et de mettre un peu l'intérêt des classes en évidence.
La notion la plus compliquée dans ce que je propose, c'est les *cls et les **kwargs, mais si on prend 5min pour regarder ce que ça signifie, il n'y a rien de bien méchant, c'est juste une syntaxe pour réutiliser la même signature que la méthode mère.
Sinon une question bête: pourquoi une fonction rad alors qu'il existe math.radians ?
Il n'y a pas de question bête, et d'ailleurs c'est une très bonne remarque :-)
J'ai juste oublié l'existence de cette fonction (que tu avais pourtant pointée dans #2). C'est mieux de l'utiliser, ça évite les erreur de programmation. Pour corriger le programme que j'ai proposé #8, il suffit de remplacer :
def rad(deg): return pi * deg / 180
... par :
from math import radians as rad
Après, on aurait aussi pu imaginer plus simplement que les angles soient dans tout le code exprimé en radians dans tout le code, car finalement manipuler des degrés n'a pas d'intérêt. Il faut juste corriger en conséquence la valeur qui permet d'incrémenter / décrémenter les angles quand on appelle les méthodes monter et descendre.
25 janv. 2023 à 10:48
Merci à Joubaho et Phil_1857 qui ont trouvé mon erreur, super !!!
Merci à Mamiemando pour le cours de programmation et aussi aux autres : je suis débutant en programmation et ça me permet de faire évoluer un peu plus mon code !
25 janv. 2023 à 14:24
Merci pour ton retour positif. As-tu d'autres question en rapport avec le sujet initial. Si ton problème est résolu, merci de suivre ces indications.