IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

Tkinter Python Discussion :

mise à jour d'une fenêtre graphique


Sujet :

Tkinter Python

  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    176
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 176
    Par défaut mise à jour d'une fenêtre graphique
    Bonjour,

    Considérez ce code:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     def resolution(self,etape):
            #procédure récursive
     
     
            "déplacement de la balle"
            global x1,y1,dx,dy,flag
            self.x1,self.y1=self.x1+self.dx,self.y1+self.dy
            if self.x1>210:
                self.x1,self.dx,self.dy=210,0,15
            if self.y1>210:
                self.y1,self.dx,self.dy=210,-15,0
            if self.x1<10:
                self.x1,self.dx,self.dy=10,0,-15
            if self.y1<10:
                self.y1,self.dx,self.dy=10,15,0
            self.gra.coords(self.oval1,self.x1,self.y1,self.x1+30,self.y1+30)
     
            self.fen.after(50,self.resolution(1))
     
            print 'la balle a bougé'
    Le morceau de code placé ici n'a pas grande signification, il n'est qu'un problème que j'ai isolé dans un code plus gros.

    Quand on lance la fonction self.resolution(1), très vite, j'ai un message d'erreur à rallonge:

    Exception in Tkinter callback
    Traceback (most recent call last):
    File "/System/Library/Frameworks/Python.framework/Versions/2.5/lib/python2.5/lib-tk/Tkinter.py", line 1403, in __call__
    File "main2.py", line 88, in demarrage
    self.resolution(1)
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 146, in resolution

    ........
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 146, in resolution
    self.fen.after(50,self.resolution(1))
    File "main2.py", line 144, in resolution
    self.gra.coords(self.oval1,self.x1,self.y1,self.x1+30,self.y1+30)
    RuntimeError: maximum recursion depth exceeded


    Mais quand j'enlève l'argument etape à la fonction, c'est à dire que j'ai déclare

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
     def resolution(self):
            #procédure récursive
     
     
            "déplacement de la balle"
            global x1,y1,dx,dy,flag
            self.x1,self.y1=self.x1+self.dx,self.y1+self.dy
            if self.x1>210:
                self.x1,self.dx,self.dy=210,0,15
            if self.y1>210:
                self.y1,self.dx,self.dy=210,-15,0
            if self.x1<10:
                self.x1,self.dx,self.dy=10,0,-15
            if self.y1<10:
                self.y1,self.dx,self.dy=10,15,0
            self.gra.coords(self.oval1,self.x1,self.y1,self.x1+30,self.y1+30)
     
            self.fen.after(50,self.resolution)
     
            print 'la balle a bougé'
    en l'appelant avec self.resolution(), cette fois, plus de problème, je vois bien ma petite balle se déplacer à l'écran.
    Faut-il croire que la fonction passée en argument de la fonction after ne doit pas avoir d'argument?


    Mathieu

  2. #2
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Dans le 1er cas, quand tu appelles self.fen.after(50,self.resolution(1)), tu transmets comme paramètre le résultat de self.resolution(1) qui doit donc être calculé avant de passer la main à self.fen.after. Mais l'appel est récursif et aucune condition d'arrêt de cette récursion n'est prévue, ça boucle jusqu'à ce que la pile soit pleine.

    Dans le 2ème cas, tu appelles self.fen.after(50,self.resolution), mais là, tu transmets la référence à la fonction self.resolution() (c'est à dire son adresse). Cela ne boucle plus, mais cela suppose que tu l'utilises à l'intérieur de self.fen.after, sinon, ça ne sert à rien.

    Tyrtamos

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    176
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 176
    Par défaut
    Bien bien... et la solution à tout ça? Comment passer par référence avec un argument?

    Mathieu

  4. #4
    Membre Expert Avatar de pacificator
    Profil pro
    Inscrit en
    Août 2006
    Messages
    1 074
    Détails du profil
    Informations personnelles :
    Âge : 45
    Localisation : France

    Informations forums :
    Inscription : Août 2006
    Messages : 1 074
    Par défaut
    En passant par une fonction lambda, cela devrait fonctionner:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def resolution(self, etape):
     
            ...
            self.fen.after(50, lambda: self.resolution(etape))

  5. #5
    Membre averti
    Profil pro
    Inscrit en
    Mai 2008
    Messages
    13
    Détails du profil
    Informations personnelles :
    Âge : 39
    Localisation : France, Moselle (Lorraine)

    Informations forums :
    Inscription : Mai 2008
    Messages : 13
    Par défaut
    Je ne comprends pas quel est l'intérêt de la variable 'etape' dans le code initial... ?
    Si c'est un compteur, pourquoi ne pas le déclarer comme attribut de ta classe et l'incrémenter ?

    Je ne comprends pas non plus pourquoi une fonction lambda permettrait d'échaper à l'erreur ? Elle va quand même faire appel à self.resolution, et donc induire une boucle infinie ?


    ##
    Christophe

  6. #6
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    176
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 176
    Par défaut
    Bonsoir,

    en fait, il n'y a pas que le paramètre etape, mais plein d'autres paramètres: c'était juste pour isoler le problème...

    J'ai l'impression que le fait d'utiliser une fonction lambda permet bien de rafraîchir la fenêtre graphique à chaque récursion, mais cela ne fonctionne pas avec une boucle for.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
     def resolution(self,etape):
            #procédure récursive
            #global compteursolutions
     
     
            "déplacement de la balle"
            global x1,y1,dx,dy,flag
            self.x1,self.y1=self.x1+self.dx,self.y1+self.dy
            if self.x1>210:
                self.x1,self.dx,self.dy=210,0,15
            if self.y1>210:
                self.y1,self.dx,self.dy=210,-15,0
            if self.x1<10:
                self.x1,self.dx,self.dy=10,0,-15
            if self.y1<10:
                self.y1,self.dx,self.dy=10,15,0
            self.gra.coords(self.oval1,self.x1,self.y1,self.x1+30,self.y1+30)
     
            if etape==1:
                for i in range(20):
                    self.fen.after(50,lambda:self.resolution(etape+1))
     
                    print 'la balle a bougé',etape
            else:
                print 'etape 2'
    Ceci provoque directement le déplacement de la balle à sa dernière étape. Pouvez-vous m'éclairer sur ce mystère?

    Mathieu

  7. #7
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    176
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 176
    Par défaut
    Bonjour,

    j'ai de plus en plus de mal à comprendre comment fonctionne la mise à jour des fenêtre graphiques... Pour preuve considérez ces quelques lignes:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    def resolution(self):
     
            "déplacement de la balle"
            global x1,y1,dx,dy
            self.x1,self.y1=self.x1+self.dx,self.y1+self.dy
            if self.x1>210:
                self.x1,self.dx,self.dy=210,0,15
            if self.y1>210:
                self.y1,self.dx,self.dy=210,-15,0
            if self.x1<10:
                self.x1,self.dx,self.dy=10,0,-15
            if self.y1<10:
                self.y1,self.dx,self.dy=10,15,0
            self.can.coords(self.oval1,self.x1,self.y1,self.x1+30,self.y1+30)
     
     
            for i in range(20):
                    self.fen.after(50,self.resolution)
                    print 'la balle a bougé'
    Quand on appelle cette fonction, la balle change une fois de position au début, puis après s'arrête de bouger, mais les messages "la balle a bougé" continuent à s'afficher.
    Si on remplace la ligne:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
                    self.fen.after(50,self.resolution)
    par
    [CODE]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
                    self.fen.after(50,resolution)
    ,
    alors le message "la balle a bougé" ne s'affiche que 20 fois...

    Mais ce qui est étonnant, c'est que sans la boucle for, avec simplement:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    self.fen.after(50,self.resolution)
            print 'la balle a bougé'
    ,
    on a bien le résultat escompté: la balle bouge régulièrement par petits intervalles et est synchronisé avec l'affichage sur la fenêtre bash.

    (Le fait d'utiliser une boucle ici n'est que l'isolement d'un certain problème: le programme qui m'intéresse est autre, et une telle boucle y est vraiment nécessaire.)

    Comment faire pour que l'affichage dans la fenêtre graphique soit le même avec et sans la boucle for?



    Pour vous permettre de faire des tests, voici le programme complet du problème isolé:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    74
    75
    # -*- coding:Latin-1 -*-
     
     
    from Tkinter import *
     
    class Main:
     
     
        def __init__(self,boss=None):
            """Constructeur de la fenêtre principale"""        
            self.fen=Tk()
            self.fen.title('Jeu fou du chien')
     
            self.can=Canvas(self.fen,bg='dark grey')
            self.can.configure(width=620,height=620,bg='ivory')
     
            self.x1,self.y1=10,10    #coordonnées initiales
            self.dx,self.dy=15,0     #'pas' du déplacement
     
            self.oval1=self.can.create_oval(self.x1,self.y1,self.x1+30,self.y1+30,width=2,fill='red')
            self.can.pack(side=TOP,pady=5)
     
            self.fen.after(50,self.resolution)
     
            self.fen.mainloop()
     
     
     
        def demarrage(self):
     
            self.resolution()
     
     
     
     
        def resolution(self):
     
            "déplacement de la balle"
            global x1,y1,dx,dy
            self.x1,self.y1=self.x1+self.dx,self.y1+self.dy
            if self.x1>210:
                self.x1,self.dx,self.dy=210,0,15
            if self.y1>210:
                self.y1,self.dx,self.dy=210,-15,0
            if self.x1<10:
                self.x1,self.dx,self.dy=10,0,-15
            if self.y1<10:
                self.y1,self.dx,self.dy=10,15,0
            self.can.coords(self.oval1,self.x1,self.y1,self.x1+30,self.y1+30)
     
     
            for i in range(20):
                    self.fen.after(50,self.resolution)
                    print 'la balle a bougé'
     
            #avec lambda: tout bouge dés le début et s'arrête
            #sans lambda: continue à boucler car "la balle a bougé" s'affiche à l'écran, mais affichage graphique change soudainement au début puisse reste fixe
     
     
     
     
     
     
     
            #self.fen.after(50,self.resolution)
            #print 'la balle a bougé'
     
     
     
     
     
     
    #programme principal:
    if __name__=='__main__':
        Main()


    Merci
    Mathieu

  8. #8
    Expert confirmé
    Avatar de Guigui_
    Homme Profil pro
    Ingénieur développement logiciels
    Inscrit en
    Août 2002
    Messages
    1 864
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 44
    Localisation : France, Saône et Loire (Bourgogne)

    Informations professionnelles :
    Activité : Ingénieur développement logiciels
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2002
    Messages : 1 864
    Par défaut
    c'est normal que ca bogue:
    resolution est une fonction récursive (vue que tu l'as rapelle dans la boucle for) et tournue indéfiniment

    La mise à jour d'une fenêtre graphique ne se réalise qu'à la fin du déroulement de la fonction sauf si tu lui forces le raffraichissement self.fen.update()

    =>
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
                ...
                for i in range(20):
                    self.fen.after(50,self.resolution)
                    self.fen.update()
                    print 'la balle a bougé'

  9. #9
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Comme je n'avais jamais fait cela, j'ai recodé ton pb à ma manière. Peut-être pourras-tu t'en inspirer:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
     
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
     
    from Tkinter import *
    import time
     
    class Application(Frame):
     
        def __init__(self, master=None, larg=None, haut=None, diam=None):
            Frame.__init__(self, master)
     
            # sauvegarde des paramètres
            self.master = master
            self.larg = larg
            self.haut = haut
            self.diam = diam
     
            # création d'un layout_manager sur self
            self.grid()
     
            # création du canvas
            self.can = Canvas(self, width=self.larg, height=self.haut, bg='ivory')
            self.can.grid(sticky="nsew")
     
            # Création de la boule
            self.x, self.y = self.larg/2-self.diam/2, self.haut/2-self.diam/2    #coordonnées initiales
            self.dx, self.dy = 1, 2.2     #'pas' du déplacement
            self.boule = self.can.create_oval(self.x, self.y, self.x+self.diam, self.y+self.diam, width=2, fill='red')
     
        def demarre(self):
            # lancement
            while True:
                try:
                    # calcule la nouvelle position    
                    if self.x>self.larg-self.diam:
                        self.dx = -self.dx
                    if self.y>self.haut-self.diam:
                        self.dy = -self.dy
                    if self.x<0:
                        self.dx = -self.dx
                    if self.y<0:
                        self.dy = -self.dy
                    self.x, self.y = self.x+self.dx, self.y+self.dy
     
                    # dessine la boule à la nouvelle position
                    self.can.move(self.boule, self.dx, self.dy)
                    self.master.update()
     
                    # temporisation pour ralentir l'affichage
                    time.sleep(0.003)
                except:
                    break
     
    ########## => programme principal:
    if __name__=='__main__':
        try:
            fen = Tk()
            fen.title('Jeu fou du chien')
            app = Application(fen, 600, 600, 50)
            fen.after(50,app.demarre)
            fen.mainloop()
        except:
            fen.destroy()
        finally:
            print "bye!"
    Tyrtamos

  10. #10
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    176
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 176
    Par défaut
    Merci à tous pour vos réponses...
    Un problème connexe est alors la suspension temporaire d'une procédure complexe jusqu'à ce que l'utilisateur appuie sur un bouton. On dispose pour ça des 2 précédures, appelés par des boutons, rendus sensibles avec mainloop:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    def stop_it():
    "arret de l'animation"
    self.flag =0
     
    def start_it():
    "démarrage de l'animation"
    if self.flag ==0: # pour ne lancer qu'une seule boucle
    self.flag =1


    J'avais bien pensé à quelque chose du genre:
    inséré judicieusement dans la procédure à arrêter, mais sans grand succès... Pouvez-vous m'aider?


    Mathieu

  11. #11
    Membre confirmé
    Profil pro
    Inscrit en
    Janvier 2007
    Messages
    176
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Janvier 2007
    Messages : 176
    Par défaut
    Bien sûr, on souhaite que la procédure à arrêter reparte exactement là où on l'avait laissée, c'est là la difficulté.

Discussions similaires

  1. Réponses: 0
    Dernier message: 04/06/2008, 13h00
  2. Mise à jour d'une fenêtre parent
    Par flavia dans le forum Général JavaScript
    Réponses: 3
    Dernier message: 19/10/2007, 12h26
  3. Mise à jour d'une table avec un fichier csv
    Par blackangel dans le forum PostgreSQL
    Réponses: 4
    Dernier message: 26/05/2005, 14h46
  4. Mise à jour d'une bd mysql
    Par joquetino dans le forum SQL Procédural
    Réponses: 3
    Dernier message: 11/01/2005, 14h05
  5. [FireBird 1.5]Mise à jour d'une SGBD ?
    Par Sitting Bull dans le forum Débuter
    Réponses: 3
    Dernier message: 03/09/2004, 16h45

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo