|
Publicité ' | |||||||||||||||||||||||
|
|
#1 | |||
|
Invité de passage
![]() Inscription : janvier 2013 Messages : 3 ![]() |
Bonsoir,
comme dit dans le titre, je rencontre quelques problèmes avec mon code. Je l'ai réduit au maximum pour ne garder que ce qui coince : Code :
Citation:
Je ne pense pas que c'est lié à Tkinter, mais plutôt à une mauvaise utilisation du Threading de ma part. Des idées d'où cela peut venir ? |
|||
|
|
00
|
|
|
#2 |
|
Expert Confirmé
![]() ![]() Inscription : décembre 2007 Messages : 1 798 ![]() |
Bonjour,
Il y a longtemps que je n'ai pas travaillé avec tkinter, mais je crois qu'un thread ne doit pas toucher au graphique. Sur la bibliothèque graphique que j'utilise (PyQt4), le thread envoie un message au programme principal qui, lui, agit sur le graphique.
__________________
Ne rien ranger permet d'observer la loi universelle d'entropie: l'inévitable convergence vers le chaos... Mes recettes python: http://www.jpvweb.com |
|
|
00
|
|
|
#3 | ||||
|
Expert Confirmé Sénior
![]() Inscription : juin 2008 Messages : 3 739 ![]() |
Salut,
tkinter supporte le threading en sérialisant les appels à la librairie TCL/Tk via un verrou. Là ou çà coince, c'est côté "objets" Python, i.e. sérialiser les références pour transformer l'appel à "self.parent.coords(self.id, x, x, x+40, x+40)" jusqu'au verrouillage. Pour faire "marcher" le code, il faut limiter les dépendances sur les références. Exemple: Code :
De toutes façons, les appels à TCL/Tk sont sérialisés, on ne gagne pas grand chose à les distribuer sur plusieurs threads. De plus, Python à son propre mécanisme de sérialisation (GIL): un ou plusieurs threads consommeront difficilement plus que la capacité d'un seul CPU. La solution (basique) sera de réaliser: - le calcul de la position suivante, - le déplacement des objets, dans le même thread et de séquencer ces activités via l'event loop de TCL: Code :
Si le nombre d'objets devient important et qu'on s'amuse à calculer des collisions, il faudra peut être pouvoir répartir la charge sur plusieurs CPU. Cela ne va pas trop changer ce design, jusque compliquer l'initialisation de l'application et l'interface entre do_update et la récupération de la position des objets dans la frame suivante. - W |
||||
|
|
00
|
|
|
#4 |
|
Membre expérimenté
![]() Inscription : août 2010 Messages : 516 ![]() |
Il est possible d'alterner le code Tkinter et votre code Python sur le thread principal à l'aide des générateurs, car la mainloop() de tkinter peut etre limitée en durée. Twisted peut surement aider pour cette approche.
Je l'ai fait moi même, from scratch, et le seul patch nécessaire concerne les appels périodique de tkinter. J'ai également croisé une Python recipe qui permettait de faire tourner Tkinter sur un thread non principal. |
|
|
00
|
|
|
#5 |
|
Invité de passage
![]() Inscription : janvier 2013 Messages : 3 ![]() |
Bonsoir à tous,
tout d'abord, merci pour vos réponses Je pensais avoir à peu près compris la cause du problème, mais je viens de me rendre compte qu'en remplaçant mon coords(...) par un move(...), le programme fonctionne correctement ! Je ne comprends du tout pourquoi... wiztricks, les deux codes que tu as proposé m'ont appris pas mal de choses, mais quelque chose m'intrigues dans le deuxième : tu bind le clic gauche sur une fonction on_left_click qui va instantanément appeler une deuxième fonction : create_ball. Pourquoi ne pas directement bind le clic gauche sur create_ball ? |
|
|
00
|
|
|
#6 | |||
|
Expert Confirmé Sénior
![]() Inscription : juin 2008 Messages : 3 739 ![]() |
Salut,
Citation:
C'est du code écrit à la volée. Par réflexe, je fais des "bind" sur des on_... et des "commands" sur des do_..., J'ai une préférence pour l'auto-documentation et la production de code où les fonctions réalisent peu de modifs. Le but étant de pouvoir construire autrement sans tout avoir à remettre à plat. Est ce qu'on optimise quoi que ce soit "en faisant bind de clic gauche sur create_ball"? En fait, create_ball n'attendant pas d'event en paramètre, on pourrait écrire: Code :
canvas.bind("<Button-1>", lambda e: create_ball(e.widget)) Code :
canvas.bind("<Button-1>", on_left_click) Moins de lignes dans le script, n'optimise pas grand chose surtout si on change d'avis et accrocher plus d'action à cet événement. Pour optimiser "vraiement", on pourrait écrire: Code :
Note: pousser .create_ball dans une instance de canvas permet de ne pas expliquer la syntaxe: Code :
canvas.bind("<Button-1>", self.register(create_ball, '%W')) - W
__________________
Architectures Post-Modernes |
|||
|
|
00
|
|
|
#7 | |
|
Expert Confirmé Sénior
![]() Inscription : juin 2008 Messages : 3 739 ![]() |
Salut,
Citation:
Vous verrez que les dépendances de .coords avec ce qui se passe côté Python sont bien plus importantes que celle d'un .move.(*) Dans le cas threads, on a seulement réduit la probabilité de se vautrer. Ce "mieux" est insuffisant pour construire un code robuste. - W (*) soit on dit: - çà devrait marcher "pareil": Dans ce cas + de dépendances = plus probable de passer dans des branches de code buggé, - çà ne peut pas marcher: Et + de dépendances = plus de chance de le "montrer"
__________________
Architectures Post-Modernes |
|
|
|
00
|
|
|
#8 |
|
Invité de passage
![]() Inscription : janvier 2013 Messages : 3 ![]() |
Salut,
je pense aussi que pour des programmes plus complexes, il vaut mieux décomposer au maximum les fonctions. Une dernière question qui m'a traversé l'esprit en voyant ta fonction do_update() : dans des programmes plus importants, c'est un concept à éviter ? Parce que si (par exemple) je veux que chaque rond se déplace dans une direction différente, il faudrait passer l'argument dans toutes les fonctions intermédiaire. Et puis, j'imagine qu'au bout d'un moment, la fonction update ressemblerait à un fourre-tout de tous les objets à actualiser... Donc, ne vaut-il pas mieux gérer chaque objet avec un thread "autonome" ? (au passage, désolé pour le temps de réponse plutôt long, j'avais pas mal de boulot ces dernier temps |
|
|
01
|
|
|
#9 | ||||||||
|
Expert Confirmé Sénior
![]() Inscription : juin 2008 Messages : 3 739 ![]() |
Citation:
Dans l'exemple, le calcul de .next_position et le .move sont imbriqués. Si on complique, il faudra séparer/décomposer. Le "dt" est lié à l'intervalle de temps des do_update. Ca donne un code du genre: Code :
Citation:
Si la mesure est le coût CPU par balles, associer un thread à chaque balle sera moins bon que de réduire cela à: Code :
Ce qui fait pousser une fonction, genre: Code :
Normalement, en fonction des collisions, mais ce n'est pas le sujet. La quantité de calculs dépend du nombre de balles et d'un nombre de collisions qui varie à chaque itération. Je ne sais pas combien de balles, on pourrait traiter de cette façon. Pour en gérer "plus", il pourra être intéressant de traiter la partie calcul de façon asynchrone pour calculer N coups d'avance et "lisser la charge". Ceci dit, le threading ne permettra pas d'utiliser les autres CPU du système: pour cela il faut pousser compute_next dans un autre process via multiprocessing et le mettre en pause lorsqu'il aura N itérations d'avance sur l'affichage. Mais, le code à réaliser pour synchroniser ces deux taches sera plus lourd et beaucoup plus difficile à mettre au point. - W
__________________
Architectures Post-Modernes |
||||||||
|
|
00
|
Copyright © 2000-2013 - www.developpez.com