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. #21
    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,

    Pour ce qui est de savoir si c'est un 'bug' je vais m'abstenir vu mon niveau.
    Je remarque juste que dans le 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
        def after(self, ms, func=None, *args):
            """Call function once after given time.
     
            MS specifies the time in milliseconds. FUNC gives the
            function which shall be called. Additional parameters
            are given as parameters to the function call.  Return
            identifier to cancel scheduling with after_cancel."""
            if not func:
                # I'd rather use time.sleep(ms*0.001)
                self.tk.call('after', ms)
            else:
                def callit():
                    try:
                        func(*args)
                    finally:
                        try:
                            self.deletecommand(name)
                        except TclError:
                            pass
                name = self._register(callit)
                return self.tk.call('after', ms, name)
    Il n'est à aucun moment fait une référence au Widget.
    Pour moi .after() est une méthode d'une instance (Misc) pour programmer un event via le gestionnaire d’événement, pas un 'bind' d'un event pour un Widget.
    Comme toujours en Python celui qui écrit le code est 'responsable' (pas moi ).

    Pour ce qui est de patcher c'est pour cela que j'utilise ici type.

    @+

  2. #22
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 782
    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 782
    Par défaut
    Salut,

    Citation Envoyé par PauseKawa Voir le message
    Il n'est à aucun moment fait une référence au
    Widget.
    Le lien vers le widget est construit ici:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
                name = self._register(callit)
                return self.tk.call('after', ms, name)
    C'est alors qu'on crée la commande TCL associée à callit (via ._register) et qu'on la stocke dans la liste self._tclCommands.

    Lorsqu'on passe par .destroy, le script TCL associé est "détruit" et la chose crée par self.tk.call('after', ms, name) se retrouve dans les choux => le message d'erreur initial.

    On peut considérer cela comme "bug" car .destroy oublie de détruire certaines entités qui ont pourtant été construites.

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

  3. #23
    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
    C'est bien ce qui me semble avoir compris.
    Ce que je veux dire c'est que lorsque j’écris:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    root = Tk()
    root.after(time, func)
    Il me semble que root n'est en aucun moment cité.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    self.tk.call('after', ms, name)
    Non ?

  4. #24
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 782
    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 782
    Par défaut
    Hu?!? On se réveille!!!

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    # création d'une instance de la classe Tk
    root = Tk()
    # appel d'une méthode de l'instance:
    root.after(time, func)
    ...Dans la méthode .after...
    qui peut être le self de self.tk.call('after', ms, name)" sinon "root"?

    Autre façon de patcher avec jonglerie entre "paramètre" de fonction et bound methods: je reprends mon ancien 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
    import tkinter as Tk
     
    # attention (self) est paramètre de la fonction locale
    # mais il est utilisé comme "reférence" à l'instance:
    # --- le self de self._tclCommands est "objet"...
    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() 
     
    # ne patchons plus Tk.Misc, il faut appeler "explictement" la fonction.
    #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))
    print(root.after( 1, lambda: destroy(root)))
    root.mainloop()
    input()
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #25
    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
    Je n'ai rien dit.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    import Tkinter as Tk
     
    root = Tk.Tk()
    e = Tk.Entry(root)
    e.pack()
    e.after(1000, root.destroy)
    e.destroy()
    root.mainloop()

  6. #26
    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,

    Désolé wiztricks: 'philosophiquement parlant' j'ai du mal à voir dans .after() une méthode propre à un widget.
    Oui, on a besoin d'une instance (self) ayant pour parent Misc pour l'utiliser et after dépend de celle ci mais dans l'idée je ne vois dans cette méthode qu'un accès à l’interpréteur Tcl (de même que .send() par exemple).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    >>> from tkinter import Tcl
    >>> tcl = Tcl()
    >>> tcl.after(6000, lambda: print('ok'))
    'after#0'
    >>> ok
    Sinon je préfère largement le dernier destroy m

  7. #27
    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,

    Désolé wiztricks: 'philosophiquement parlant' j'ai du mal à voir dans .after() une méthode propre à un widget.
    Oui, on a besoin d'une instance (self) ayant pour parent Misc pour l'utiliser et after dépend de celle ci mais dans l'idée je ne vois dans cette méthode qu'un accès à l’interpréteur Tcl (de même que .send() par exemple).
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    >>> from tkinter import Tcl
    >>> tcl = Tcl()
    >>> tcl.after(6000, lambda: print('ok'))
    'after#0'
    >>> ok
    Sinon je préfère largement le dernier destroy mais à ce sujet:
    Pourquoi utiliser self.destroy()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
        self.destroy() 
     
    # ne patchons plus Tk.Misc, il faut appeler "explictement" la fonction.
    #Tk.Misc._destroy = Tk.Misc.destroy
    #Tk.Misc.destroy = destroy
    Pourquoi self.tk.deletecommand(name) ne détruit pas l'event timer pour l'interpréteur Tcl ? Une piste ?

    Merci

  8. #28
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 782
    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 782
    Par défaut
    Salut,

    Désolé wiztricks: 'philosophiquement parlant' j'ai du mal à voir dans .after() une méthode propre à un widget.
    Je suis d'accord du point de vue de TCL maintenant, l'implémentation Tkinter (le code Python qu'il y a entre les deux) associe la commande produite par after à l'instance....

    Pourquoi self.tk.deletecommand(name) ne détruit pas l'event timer pour l'interpréteur Tcl ? Une piste ?
    C'est là que j'ose parler de "bug" ou de "undocumented feature":
    - bug: si on fait le ménage, autant le faire complètement mais quand on ouvre la boîte, c'est compliqué pour faire "propre",
    - undocumented feature: le programmeur "sachant" que les .after sont collés au "widget" évite .destroy (ou les associe à root).
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  9. #29
    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
    Citation Envoyé par wiztricks Voir le message
    collés au "widget" évite .destroy (ou les associe à root).
    C'est insuffisant comme le montre mon code en début de sujet
    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()
    Je me demande, à la limite, si on ne devrais pas gérer cela hors IHM (pour les gros projets), directement au niveau Tcl (Tkinter.Tcl). Après tout il est facile de suppléer le gestionnaire d’événement via une classe (Class Ge(Tkinter.Tcl), Ge(dict)/context manager, etc...) et comme vous le montrez bind intercepte le 'destroy'...
    Je ne parle pas de patcher quoi que se soit mais de revoir le after car je penche maintenant (et vous comprendrez que cela m'est désagréable car j'aime bien Tkinter) pour cette réponse: bug.
    Votre avis sur le sujet ?

    @+

  10. #30
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 782
    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 782
    Par défaut
    Salut,

    Comme je le disais tantôt, si .destroy se propose de faire le ménage, les manques côté traitement des associations sur les ".alert" laisseraient pencher vers "bug".

    Oublions un peu .alert. N'import quel callback se traduit par la construction d'une commande TCL et son pendant Python qui sera créé dans la registry associée au widget sous le compte duquel il (le callback) sera déclaré.

    Le callback pourra faire référence à ce qu'on veut: un lambda vers une méthode d'un autre widget qui pourra appeler une méthode d'un autre widget...

    Ces dépendances seront difficiles à maîtriser "par programme" et, à défaut, il appartiendra au programmeur d'éviter de faire "n'importe" quoi où plutôt de faire attention aux implications de ses appels à .destroy et/ou aux liens qu'il aura crée avec des widgets "volatiles".

    Dans ce cas, ce n'est plus un "bug" mais une "feature": un truc que doit apprendre le programmeur Tkinter plus ou moins à ses dépends, difficile à documenter mais connu par les vieux barbus qui hantent les forums de discussion.

    Ce n'est pas très gênant: c'est en général le cas pour toutes les bibliothèques un peu touffues...

    - 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 2 sur 2 PremièrePremière 12

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