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 :

Débutant, animation Tkinter


Sujet :

Tkinter Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé
    Avatar de EpiTouille
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2009
    Messages
    372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2009
    Messages : 372
    Par défaut Débutant, animation Tkinter
    Bonjour,
    Je suis nouveau dans python, et j'aimerai savoir pourquoi ce code ne fonctionne pas.
    J'ai essayé de faire la boule qui rebondit sur les 4 coins de l'écran, mais je me suis heurté à un gros problème:

    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
     
    from Tkinter import * 
    from time import * 
     
    def initialize(title = 'MonApplication'):
      global app
      app = Tk()
      app.title(title)
      app.geometry('400x420')
     
      #Canvas
      global can
      can = Canvas(app, width = 400, height = 400, bg = 'green')
      can.pack()
     
      #Label
      new = Label(app,text = 'Appuyer sur N pour demarrer')
      new.pack()
     
      #Evenement
      app.bind_all('<n>',Nouveau)
      app.bind_all('<q>'Quit)
     
      #Coordonnée balle
      global x,y
      x , y = 0, 50
     
    def Nouveau(event): #Quand on appui sur N
      Animate()
     
     
    def Animate()
      global x,y
     
      while 1:
        boule = can.create_oval(x,x,y,y,fill = 'red')
        x = x+10
        y = y+10
        sleep(1) #On attent 1 seconde
     
    def Quit(event):
      app.destroy()
     
     
    #Le main
    initialize('Une balle qui rebondit')
    app.mainloop()
    app.destroy()
    Le problème est la boucle infinie, 'While 1'.
    Elle provoque un plantage du programme, alors que normalement, c'est
    juste un thread qui est lancé, donc on pourrait toujours appuyé sur Q ?

    Merci de vos réponses

  2. #2
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Je ne connais pas du tout tkinter, mais*:

    * Tu parles de thread pour Animate(), je n’en vois nulle trace dans ton code*?
    * Tu utilises can dans cette même fonction, sans le lui passer*????
    * Enfin, de façon générale, il est rarement conseillé d’utiliser des fonctions de dessin d’une bibliothèque graphique dans d’autres threads que le principal.

    Donc, si tu veux calculer le mouvement de tes boules dans des threads (ce qui n’est pas forcément une mauvaise idée , même si tu pourrais sans doute te contenter de la boucle principale), je te conseillerais de juste calculer cette position, et de faire l’affichage depuis le thread principal (en utilisant un verrou pour synchroniser le tout*!)…

    PS*: Pourquoi appeler app.destroy() à la fin de ton script*? Quit() s’en charge déjà, non*?

    [EDIT] Mea culpa, j’avais pas vu que can était global…

  3. #3
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonsoir,

    Déjà un remise en forme du 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
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #
    #
    from Tkinter import *
     
    def Affiche(x, y):
        can.coords(b, (x, x, y, y))
        can.update()
     
    def Animate(x, y):
        while x < 400 and y < 400:
            x += 10
            y += 10
            can.after(50, Affiche(x, y))
     
    def Quit(event):
        app.quit()
     
    #Le main
    app = Tk()
    app.title('MonApplication')
    app.geometry('400x420')
    #Canvas
    can = Canvas(app, width=400, height=400, bg='green')
    can.pack()
    x = 0
    y = 50
    b = can.create_oval(x, x, y, y, fill='red')
    #Label
    new = Label(app, text='Appuyer sur N pour demarrer')
    new.pack()
    #Evenement
    app.bind('<n>', lambda event=None: Animate(x, y))
    app.bind('<q>', Quit)
    app.mainloop()
    A vous de corriger. Il reste pas mal à voir.
    Pourquoi ne pas suivre un des tutos du site ?

    Citation Envoyé par mont29 Voir le message
    PS*: Pourquoi appeler app.destroy() à la fin de ton script*? Quit() s’en charge déjà, non*?
    Habitude de l'Education Nationale.
    Si c'est exécuter dans un shell Tk cela détruit l'objet tcl (persistance par rapport au wrapper), sinon l'erreur de l'interpréteur tcl (can't invoke "destroy" command: application has been destroyed) n'est plus visible puisque le script est terminer.
    quit arrête le gestionnaire d'événement (mainloop).
    destroy détruit l'objet dans l’interpréteur tcl.

    C'est sans doute pour cela que je trouve bizarre la faq
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import Tkinter as Tk
     
    root = Tk.Tk()
     
    def Intercepte():
        print "Interception de la fermeture de la fenetre"
        root.destroy()
     
    root.protocol("WM_DELETE_WINDOW", Intercepte)
    root.mainloop()
    @+

    Edit : Si un étudiant a noter l'explication donnée pour le destroy après le mainloop je prend.

  4. #4
    Membre éclairé
    Avatar de EpiTouille
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2009
    Messages
    372
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Mai 2009
    Messages : 372
    Par défaut
    Bonjour,
    merci de vos réponses qui m'ont aidé à finir.
    J'avais oublié le
    can.after et aussi le can.uptdate()
    et ce sont ces 2 fonctions qui provoquaient l'erreur.

    Je vous donne le code de fin :

    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
     
     
    from Tkinter import * 
    from time import * 
     
    def initialize(title = 'MonApplication'):
      global app
      app = Tk()
      app.title(title)
      app.geometry('640x500')
     
      #Canvas
      global can
      can = Canvas(app, width = 640, height = 480, bg = 'green')
      can.pack()
     
      #Label
      new = Label(app,text = 'Appuyer sur N pour demarrer')
      new.pack()
     
      #Evenement
      app.bind_all('<n>',Nouveau)
      app.bind_all('<q>',Quit)
     
      #Coordonnée balle
      global x,y
      x , y = 50, 50
     
    def Nouveau(event): #Quand on appui sur N
      Animate()
     
     
     
     
    def affiche(x,y):
      drawcircle(x,y,30)
      can.update()
     
    def drawcircle(x,y,rad):
      global boule
      boule = can.create_oval(x-rad, y-rad, x+rad, y+rad,fill = 'red')
     
    def Animate():
      global dx,dy #Les directions
      global x,y 
      dx,dy = 1,1
     
      while 1:
      #On change les coordonnée    
        x = x+ (dx* 10)
        y = y+ (dy*10)
     
        if x > 610:
          dx = -dx
        if x < 30:
          dx = -dx
        if y >450:
          dy = -dy
        if y<30:
          dy = -dy
     
        can.after(30,affiche(x,y))
        can.delete(boule)
     
     
    def Quit(event):
      app.destroy()
     
     
    #Le main
    initialize('Une balle qui rebondit')
    app.mainloop()
    app.destroy()
    J'ai vu sur un site que en faite le programme restait à attendre les evenements sur le mainloop(), et que quand il se passait un évenement ici l'appui des touches n ou q, c'était un nouveau thread qui se lancait, comme ça le programme continue à lire les évenements

    et si j'ai mis 2 fois destroy, c'est ce que j'ai trouvé sur internet car on n'apprend pas ça en cours

  5. #5
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour,

    Et bien... Oui, cela tourne...
    Mais, sans vouloir vous offenser, quel beau mélange.

    @+

    Ps:
    J'ai trouver la source: 24.1.2.2. A Simple Hello World Program.
    Le mainloop() est sur le Frame, d’où le destroy() pour l'instance Tk().

  6. #6
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Bonjour,

    Citation Envoyé par titeeee Voir le message
    J'ai vu sur un site que en faite le programme restait à attendre les evenements sur le mainloop(), et que quand il se passait un évenement ici l'appui des touches n ou q, c'était un nouveau thread qui se lancait, comme ça le programme continue à lire les évenements
    Pourriez vous donner votre source ?
    En fait oui le mainloop() (gestionnaire d’événement) est bloquant. Il gère non seulement les events clavier, souris mais bien d'autres plus 'internes'.

    Le code suivant vous montre que l'instance Tk() existe après le quit()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #
    #
    from Tkinter import *
     
    root = Tk()
    Button(root, text='Quit (.quit())', command=root.quit).pack()
    root.mainloop()
    Label(root, text='Après le premier mainloop()').pack()
    root.mainloop()
    Button(root, text='Je veux quitter ! (.destroy())', command=root.destroy).pack()
    root.mainloop()
    root.mainloop()
    Donc, effectivement, on pourrais faire un .destroy() si le code continu par la suite. A condition d'intercepter (voir la faq) la fermeture de la fenêtre pour mettre un .quit() (et non un .destroy() comme dans la faq). Soit, avec un bouton, la fermeture de la fenêtre et un bind:
    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
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #
    #
    try:
        import Tkinter as Tk
    except:
        import tkinter as Tk
     
    root = Tk.Tk()
     
    def Intercepte(event=None):
        print("Interception de la fermeture de la fenetre")
        root.quit()
     
    Tk.Button(root, text='Quit', command= Intercepte).pack()
    root.protocol("WM_DELETE_WINDOW", Intercepte)
    root.bind('<Escape>', Intercepte)
     
    root.mainloop()
    root.destroy()
    Pourquoi pas destroy() ? Voici ce qui se passe si j’enlève la ligne root.protocol("WM_DELETE_WINDOW", Intercepte)
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    Traceback (most recent call last):
      File "titeeee.py", line 21, in <module>
        root.destroy()
      File "/usr/lib/python2.6/lib-tk/Tkinter.py", line 1689, in destroy
        self.tk.call('destroy', self._w)
    _tkinter.TclError: can't invoke "destroy" command:  application has been destroyed
    Normal non ?
    A vrais dire Intercepte n'est la que pour les traitements supplémentaires (ici le print mais cela peux être l'enregistrement d'un fichier par exemple). Vous pouvez utiliser root.quit. Il suffit de penser que bind retourne un event (pas protocol ni command)
    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
    #!/usr/bin/env python
    # -*- coding: UTF-8 -*-
    #
    #
    try:
        import Tkinter as Tk
    except:
        import tkinter as Tk
     
    root = Tk.Tk()
     
    def Intercepte(event):
        print('widget', event.widget)
        print('x, y', event.x, event.y)
        print('x_root, y_root', event.x_root, event.y_root)
        print('char', event.char)
        print('keysym', event.keysym)
        print('keycode', event.keycode)
        print('num', event.num)
        print('width, height', event.width, event.height)
        print('type', event.type)
        root.quit()
     
    Tk.Button(root, text='Quit', command=root.quit).pack()
    root.protocol("WM_DELETE_WINDOW", root.quit)
    root.bind('<Escape>', Intercepte)
     
    root.mainloop()
    root.destroy()
    Ceci dit revoyez votre code
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    from Tkinter import *
     
    def initialize(title = 'MonApplication'):
        global app
        app = Tk()
        app.title(title)
        app.geometry('640x500')
     
    #Le main
    initialize('Une balle qui rebondit')
    app.mainloop()
    Celle la je n'avais jamais vu...

    A la limite essayer de le revoir sans utiliser global. Cela devrais vous aider pas mal.

    @+

  7. #7
    Membre Expert Avatar de PauseKawa
    Homme Profil pro
    Technicien Help Desk, maintenance, réseau, système et +
    Inscrit en
    Juin 2006
    Messages
    2 725
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Technicien Help Desk, maintenance, réseau, système et +
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Juin 2006
    Messages : 2 725
    Par défaut
    Petite précision.
    .destroy demande la destruction à l'interpréteur tcl.
    Pour Python il vas falloir attendre que les balayeurs (comprendre pour titeeee que Python a un cycle de collecte des ordures, mais c'est pour plus tard) passent.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    import Tkinter as Tk
     
    app = Tk.Tk()
    Tk.Button(app, text='Quit', command=app.quit).pack()
    app.mainloop()
    app.destroy()
    print locals()['app']
    @++

Discussions similaires

  1. [Débutant] Animation avec AsyncTask et onDraw();
    Par Pif_Paf_Pouf dans le forum Android
    Réponses: 0
    Dernier message: 09/06/2014, 20h17
  2. Débutant sur tkinter
    Par adise dans le forum Tkinter
    Réponses: 3
    Dernier message: 18/12/2010, 09h09
  3. [Débutant] Animation 3D
    Par alfadev dans le forum Développement 2D, 3D et Jeux
    Réponses: 3
    Dernier message: 09/10/2006, 17h26
  4. Réponses: 1
    Dernier message: 17/02/2006, 19h18
  5. [Tkinter] Afficher une image animée
    Par Chris33 dans le forum Tkinter
    Réponses: 3
    Dernier message: 16/12/2005, 23h14

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