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 :

invalid command in after script


Sujet :

Tkinter Python

  1. #1
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut invalid command in after script
    Bonjour,

    J'obtiens un message rouge dans la log de la part de Tkinter
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    invalid command name "156099928callit"
        while executing
    "156099928callit"
        ("after" script)
    invalid command name "156099248callit"
        while executing
    "156099248callit"
        ("after" script)
    Je me doute bien que l'erreur est levée par un callback que j'ai passé en argument à une instruction widget.after(), mais j'en ai plusieurs.

    Connaisez vous un moyen d'avoir plus d'informations sur ce type d'erreur ?
    Merci d'avance

  2. #2
    Expert éminent
    Avatar de fred1599
    Homme Profil pro
    Lead Dev Python
    Inscrit en
    Juillet 2006
    Messages
    3 885
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Meurthe et Moselle (Lorraine)

    Informations professionnelles :
    Activité : Lead Dev Python
    Secteur : Arts - Culture

    Informations forums :
    Inscription : Juillet 2006
    Messages : 3 885
    Points : 7 233
    Points
    7 233
    Par défaut
    Tu viens sûrement de créer une boucle infinie avec after et si tu ne donnes pas de moyen de sortir de cette boucle, ça doit faire cette erreur.

    Enfin je pense, et sans code difficile à dire...
    Celui qui trouve sans chercher est celui qui a longtemps cherché sans trouver.(Bachelard)
    La connaissance s'acquiert par l'expérience, tout le reste n'est que de l'information.(Einstein)

  3. #3
    Membre éprouvé
    Avatar de afranck64
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Cameroun

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2009
    Messages : 592
    Points : 1 006
    Points
    1 006
    Par défaut
    Euh, c est pas très jolie, mais ca marche à tous les coups.
    Il vous suffit de parsemer votre de code de "print (infos_relativ_au_after_plus_numero_ligne)" juste avant d appeler "after" L ensemmencement se fait du haut vers le bas (pour ne pas modifier les valeurs des lignes déjà validées.)
    Win 10 64 bits / Linux Mint 18, - AMD A6 Quad: Py27 / Py35
    CONTENU D'UNE QUESTION
    Exemples:
    - Configuration (système d'exploitation, version de Python et des bibliothèques utilisées)
    - Code source du morceau de programme où il y a un bogue
    - Ligne de code sur laquelle le bogue apparaît
    - Erreur complète retournée pas l'interpréteur Python
    - Recherche déjà effectuée (FAQ, Tutoriels, ...)
    - Tests déjà effectués

  4. #4
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    Je doute que cela fonctionne: La fonction callit() registered par .after() est interne à Tkinter, ce n'est pas l'ID de la fonction mais celle de callit() que vous avez à l'écran.
    Il n'y a pas du thread la dessous ?

    Sinon l'erreur est facile a reproduire
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    try:
        import Tkinter as Tk
    except:
        import tkinter as Tk
     
    root = Tk.Tk()
    val = Tk.IntVar()
    root.after(3, lambda: val.set(1))
    root.after(1, root.destroy)
    root.mainloop()
    input()
    C'est donc bien l'objet qui n’existe plus pour Tkinter (Pas pour Python, ce n'est pas un NameError).


    @+
    Merci d'utiliser le forum pour les questions techniques.

  5. #5
    Membre éprouvé
    Avatar de afranck64
    Homme Profil pro
    Étudiant
    Inscrit en
    Janvier 2009
    Messages
    592
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 33
    Localisation : Cameroun

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Janvier 2009
    Messages : 592
    Points : 1 006
    Points
    1 006
    Par défaut
    Citation Envoyé par PauseKawa Voir le message
    C'est donc bien l'objet qui n’existe plus pour Tkinter (Pas pour Python, ce n'est pas un NameError).
    Je comprends bien, de par les explications, mais le code ne fonctionne pas (je veux dire, ne produit pas d'erreur.) lancé avec: PyScripter
    Win 10 64 bits / Linux Mint 18, - AMD A6 Quad: Py27 / Py35
    CONTENU D'UNE QUESTION
    Exemples:
    - Configuration (système d'exploitation, version de Python et des bibliothèques utilisées)
    - Code source du morceau de programme où il y a un bogue
    - Ligne de code sur laquelle le bogue apparaît
    - Erreur complète retournée pas l'interpréteur Python
    - Recherche déjà effectuée (FAQ, Tutoriels, ...)
    - Tests déjà effectués

  6. #6
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    Encore lui... (PyScripter).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    patrice@Zeus:~/Bureau$ /usr/bin/python3.1 testerreur.py 
    invalid command name "3076420044callit"
        while executing
    "3076420044callit"
        ("after" script)
     
    patrice@Zeus:~/Bureau$
    Merci d'utiliser le forum pour les questions techniques.

  7. #7
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Bonjour, et merci beaucoup pour vos réponses.
    J'ai isolé mon bug dans l'exemple suivant:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    import Tkinter as tk
    def disp(x): print(x)
    def go():
        R= tk.Tk()
        R.after(1000,lambda *a: disp(3))
        R.mainloop()
        RR= tk.Tk()
        RR.mainloop()
    if __name__ == '__main__':go()
    Si j'attends 1sec avant de fermer la première fenêtre R, pas de bug. Si je la ferme avant le délai d'1 seconde, le R.after() est toujours "en pile", et cela lèvera une erreur dans RR.mainloop() (erreur non levée si on commente RR.mainloop() !).

    - Pourquoi RR.mainloop() essaye de s'occuper de R.after() ? R et RR ne sont elles pas 2 roots indépendantes ?
    - Comment puis-je faire pour que le callback que je donne à R.after() n'existe plus une fois que R a été destroy() ? J’espère que cela corrige l'erreur.
    - Dans l'idéal, cela concerne aussi tous les widgets que l'on a placé dans R et qui ont subi un after() (sont-ils destroy lorsque l'on destroy la root ?)

    Merci d'avance

  8. #8
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    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
    import Tkinter as tk
     
    def disp(x):
        print(x)
     
    def onquit(w):
        if hasattr(w, 'ids'):
            for event in w.ids:
                try:
                    w.after_cancel(event)
                except:
                    pass
        try:
            w.destroy()
        except:
            pass
     
    def go():
        R = tk.Tk()
        R.ids = []
        R.ids.append(R.after(1000, lambda: disp(1)))
        R.ids.append(R.after(3000, lambda: disp(3)))
        R.ids.append(R.after(4000, R.quit))
        R.ids.append(R.after(5000, lambda: disp(5)))
        R.protocol("WM_DELETE_WINDOW", lambda: onquit(R))
        R.mainloop()
        onquit(R)
     
        RR = tk.Tk()
        RR.protocol("WM_DELETE_WINDOW", lambda: onquit(RR))
        RR.mainloop()
        onquit(RR)
     
    if __name__ == '__main__':
        go()
    Merci d'utiliser le forum pour les questions techniques.

  9. #9
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Merci beaucoup PauseKawa.

    Je dois néanmoins enlever le 2eme onquit() (après R.mainloop()) pour mes besoins, mais ça a l'air de marcher avec comme sans ce 2nd appel.

  10. #10
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    il reste tout de même un léger problème : j'ai appelé after sur des widgets non top-levels.
    * Peut on appeler protocol('destroy') sur un tel widget ? (j'ai l'impression que non)
    * Comment puis-je connaitre dans quel top level est situé un widget ?

    Merci d'avance

  11. #11
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Salut,

    Citation Envoyé par VV33D Voir le message
    il reste tout de même un léger problème : j'ai appelé after sur des widgets non top-levels.
    * Peut on appeler protocol('destroy') sur un tel widget ? (j'ai l'impression que non)
    * Comment puis-je connaitre dans quel top level est situé un widget ?

    Merci d'avance
    Je ne pense pas que .destroy soit un "protocol": c'est une méthode.
    Tkinter 'range' les widgets dans une arborescence issue de root (créée par tk.Tk())). Chaque widget a pour attributs:
    - children: côté Tkinter c'est un dict dont la clé est le nom du widget qui par défaut est construit avec l'id de l'instance,
    - master: le "parent",

    => Lorsqu'on remonte en suivant "master", le Toplevel sera la première instance X vérifiant isinstance(X, (Toplevel, Tk)).

    Cordialement,
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  12. #12
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Merci Wiztricks

  13. #13
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Ooops.
    Juste en regardant l'API pour autre chose, la méthode .winfo_toplevel permet de récupérer le toplevel directement (sans parcourir .master).
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  14. #14
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Pour ce qui est de:
    Citation Envoyé par VV33D Voir le message
    il reste tout de même un léger problème : j'ai appelé after sur des widgets non top-levels.
    * Peut on appeler protocol('destroy') sur un tel widget ? (j'ai l'impression que non)
    Ou: Comment savoir si un Widget non Toplevel est 'destroy'
    Je ne vois pas comment faire avec Tk. Pour le moment je reste dans des 'versions' Python du style
    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
    try:
        import Tkinter as Tk
    except:
        import tkinter as Tk
     
    def Trace_Maker(widget, func, master, *kwargs):
        def destroy(self):
            self.var.set(self)
            for c in self.children.values():
                c.destroy()
            self.tk.call('destroy', self._w)
            if self._name in self.master.children:
                del self.master.children[self._name]
            self.Misc.destroy(self)
     
        var = Tk.StringVar()
        var.trace_variable("w", func)
     
        cls = type(widget)
        cls = cls('Trace_%s' % widget.__name__, (widget,),
                  {'var': var, 'destroy': destroy, 'Misc': Tk.Misc})
        return cls(master, *kwargs)
     
    def callback(*args):
        w = r.globalgetvar(args[0])
        print(('Widget %s deleted') % w)
     
    r = Tk.Tk()
    l = Trace_Maker(Tk.Label, callback, r, {'text': 'Delete me !'})
    l.pack()
    t = Tk.Toplevel()
    Trace_Maker(Tk.Button, callback, t, {'text': 'Delete me !'}).pack()
    Tk.Button(r, text='Delete label', command=l.destroy).pack()
    Tk.Button(r, text='Delete top', command=t.destroy).pack()
    r.mainloop()
    @+
    Merci d'utiliser le forum pour les questions techniques.

  15. #15
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Salut,

    PauseKawa: pourquoi ne pas passer par un event?

    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
    import tkinter as tk
     
    def destroy(event):
        widget = event.widget
        print ('deleted ,', widget._w)
     
     
    root = tk.Tk()
    root.bind_all('<Destroy>', destroy)
     
    label = tk.Label(text='Delete me !!!')
    label.pack()
     
    tl = tk.Toplevel()
    tk.Button(root, text='delete label', command=label.destroy).pack()
    tk.Button(root, text='delete toplevel', command=tl.destroy).pack()
    root.mainloop()
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  16. #16
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonsoir,

    Et bien en fait .protocol() intercepte l'event alors que .bind() fait un callback.
    C'est pour cela que je fais un self.var.set(self) (maladroit car il faudrait plutôt lancer directement la fonction) dès le début de la méthode.
    C'est une erreur ?
    Maintenant c'est vrais que si nous n'avons rien a faire que le Widget existe encore ou pas (c'est le cas ici) bind est bien plus simple.

    @+
    Merci d'utiliser le forum pour les questions techniques.

  17. #17
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Salut,

    Citation Envoyé par PauseKawa Voir le message
    Et bien en fait .protocol() intercepte l'event alors que .bind() fait un callback.
    C'est pour cela que je fais un self.var.set(self) (maladroit car il faudrait plutôt lancer directement la fonction) dès le début de la méthode.
    C'est une erreur ?
    no idea!
    En fait, je ne suis pas sûr d'avoir compris le but.
    VV33D a fait un autre post sur un sujet similaire. Et j'ai pu constater que, par défaut, root.protocol("WM_DELETE_WINDOW") invoque .destroy.
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  18. #18
    Membre éprouvé
    Inscrit en
    Août 2010
    Messages
    1 124
    Détails du profil
    Informations forums :
    Inscription : Août 2010
    Messages : 1 124
    Points : 1 277
    Points
    1 277
    Par défaut
    Pour l'instant, j'ai une solution partielle, qui combine vos 1ers posts:
    Je surcharge after de sorte que l'id renvoyé par after soit stocké dans le toplevel master (attribut ids de pausekawa), puis je bind cancel_after à master.destroy.

    Pour avoir une solution 'parfaite' il faudrait que widget.destroy() désinscrive ses after, comme wiztricks le suggère : root.bind_all('<Destroy>', destroy)

    Mais les widgets peuvent déjà avoir été bind ! Comment faire pour ajouter un callback dans .bind() ou .protocol() sans perdre l'ancien callback ?

    C'est le sens de ma question #1 dans mon autre post.

  19. #19
    Expert confirmé 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
    Points : 4 005
    Points
    4 005
    Par défaut
    Bonjour,

    Citation Envoyé par wiztricks Voir le message
    Salut,



    no idea!
    En fait, je ne suis pas sûr d'avoir compris le but.
    VV33D a fait un autre post sur un sujet similaire. Et j'ai pu constater que, par défaut, root.protocol("WM_DELETE_WINDOW") invoque .destroy.
    - W
    On est bien d'accord sur cela et sur le fait que .bind() intervient après l'event et .protocol() l'intercepte.
    .protocol()/.wm_protocol() n'est utilisable qu'avec les 'fenêtres' (instance Tk/Toplevel) et il n'existe pas de méthode pour les Widgets.
    Dans l'exemple suivant l'Entry reste utilisable car le .destroy() de BaseWidget (et en particulier son self.tk.call('destroy', self._w)) est écrasé par un pass.
    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
    try:
        import Tkinter as Tk
    except:
        import tkinter as Tk
     
    def Trace_Maker(widget, master, *kwargs):
        def destroy(self):
            pass
     
        cls = type(widget)
        cls = cls('Trace_%s' % widget.__name__, (widget,), {'destroy': destroy})
        return cls(master, *kwargs)
     
    r = Tk.Tk()
    v = Tk.StringVar()
    l = Tk.Label(r, textvariable=v, bg='black', fg='white')
    l.pack(fill=Tk.BOTH, expand=1)
    e = Trace_Maker(Tk.Entry, r, {'textvariable':v})
    e.pack()
    Tk.Button(r, text='Delete entry', command=e.destroy).pack()
    r.mainloop()
    Nous avons donc bien un comportement 'proche' d'un Widget.protocol("WM_DELETE_WINDOW", fonction).
    Bien sur il faudrait écrire cela en plus Tkinter (self.Misc), style le code de ttk.

    C'est aussi la réponse à la dernière question de VV33D.
    En utilisant une classe dérivée il est possible de lui ajouter les attributs que l'on souhaite, comme par exemple ids.
    Cela donne (en gros, c'est dimanche après tout )
    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
    try:
        import Tkinter as Tk
    except:
        import tkinter as Tk
     
    def Trace_Maker(widget, master=None, *kwargs):
        def delete_events(self):
            for event in self.registered_func:
                try:
                    self.after_cancel(event)
                except:
                    pass
     
        def w_destroy(self):
            self.delete_events()
            for c in self.children.values():
                c.destroy()
            self.tk.call('destroy', self._w)
            if self._name in self.master.children:
                del self.master.children[self._name]
            self.Misc.destroy(self)
     
        def r_destroy(self):
            self.delete_events()
            for c in self.children.values():
                c.destroy()
            self.tk.call('destroy', self._w)
            self.Misc.destroy(self)
     
        def register(self, time, func):
            self.registered_func.append(self.after(time, func))
     
        try:
            if widget._w == '.':
                destroy = r_destroy
        except:
            destroy = w_destroy
     
        cls = type(widget)
        cls = cls('Trace_%s' % widget.__name__, (widget,),
                  {'delete_events': delete_events, 'registered_func': [],
                   'register': register, 'destroy': destroy, 'Misc': Tk.Misc})
        return cls(master, *kwargs)
     
    def disp(x):
        print(x)
     
    def go():
        R = Trace_Maker(Tk.Tk, None)
        #R = Tk.Tk()
        R.register(1000, lambda: disp(1))
        R.register(3000, lambda: disp(3))
        R.register(5000, lambda: disp(5))
        E = Trace_Maker(Tk.Entry, R, {'bg': 'red'})
        E.register(4000, lambda: disp(4))
        E.pack()
        R.mainloop()
     
        RR = Tk.Tk()
        RR.mainloop()
     
    if __name__ == '__main__':
        go()
    @+
    Merci d'utiliser le forum pour les questions techniques.

  20. #20
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 287
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Manche (Basse Normandie)

    Informations professionnelles :
    Activité : Architecte technique retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Juin 2008
    Messages : 21 287
    Points : 36 781
    Points
    36 781
    Par défaut
    Salut,

    Quand je disais ne pas comprendre le but, c'est parce j'avais l'impression que nous nous acharnions à vouloir faire fonctionner un truc qui pourrait être plus simple à réaliser si nous avions la possibilité d'avoir un peu de recul/hauteur.

    Ceci dit, je pense de plus en plus qu'il s'agit d'un "bug" ou d'une feature dont les conditions d'utilisation mériteraient d'être documentées.

    Reprenons le reproducteur de PauseKawa:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    try:
        import Tkinter as Tk
    except:
        import tkinter as Tk
     
    root = Tk.Tk()
    val = Tk.IntVar()
    root.after(3, lambda: val.set(1))
    root.after(1, root.destroy)
    root.mainloop()
    input()
    Ca bugge parce que .destroy a nettoyé le script associé au timer sans effectuer le .after_cancel qui va bien...

    L'id retourné par .after côté Python n'est pas celui du script:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
            name = self._register(callit)  # nom du script
            return self.tk.call('after', ms, name)  # ident pour after_cancel
    Autrement dit, .destroy vire le côté Python de l'after mais pas le côté TCL!
    => looks like a bug, au sens ou ce pourrait être le boulot de misc.destroy d'aller "nettoyer". Ce qui pourrait donner:

    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
    import tkinter as Tk
     
    def destroy(self):
        if self._tclCommands is not None:
            data =  self.tk.splitlist(self.tk.call('after', 'info'))
            for ident in data:
                script, cls =  self.tk.splitlist(
                    self.tk.call('after', 'info', ident))
                if cls == 'timer' and script in self._tclCommands:
                    self.after_cancel(ident)
        self._destroy()
     
    Tk.Misc._destroy = Tk.Misc.destroy
    Tk.Misc.destroy = destroy
     
    root = Tk.Tk()
    val = Tk.IntVar()
    print(root.after( 3, lambda: val.set(1)))
    print(root.after( 1, root.destroy))
    root.mainloop()
    input()
    note: patcher au lieu de sous classer est une possibilité "Python" dont il ne faut pas non plus abuser...

    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Paralleliser des commandes dans un script
    Par MC wacko dans le forum Shell et commandes GNU
    Réponses: 2
    Dernier message: 23/08/2008, 22h45
  2. Commandes Hyperterminal par script ou autre
    Par Abyss dans le forum Windows
    Réponses: 10
    Dernier message: 28/09/2007, 09h46
  3. commande awk dans script perl
    Par sorilazer dans le forum Langage
    Réponses: 3
    Dernier message: 19/07/2007, 10h16
  4. Commandes SSH dans script php
    Par furtif1 dans le forum Bibliothèques et frameworks
    Réponses: 2
    Dernier message: 05/03/2007, 19h03
  5. httpd.conf - php5 - Invalid command 'Action'
    Par julien.63 dans le forum Apache
    Réponses: 1
    Dernier message: 21/07/2006, 18h09

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