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 :

Tk et Toplevel


Sujet :

Tkinter Python

  1. #1
    Membre averti
    Homme Profil pro
    Enseignant
    Inscrit en
    Août 2011
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2011
    Messages : 16
    Par défaut Tk et Toplevel
    Bonjour,

    Je suis débutant en Python (version 3.2) et j'ai le problème suivant (je vais essayer d'être simple et clair, merci de me demander des précisions sinon) :

    J'ai ouvert une fenêtre 'racine' Tk, qui me sert d'arrière-plan. Puis une fenêtre 'fen2', Toplevel avec un seul bouton 'Quitter' dans la fonction suivante :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def ouvfen0 (tx, bt) :
        fen2 = Toplevel()
        fen2.grab_set()
        fen2.focus_set()
        fen2.title ('--- VV ---')
        fen2.config(bg = 'dark grey')
        fen2.wm_geometry('%dx%d+%d+%d' % (560, 200, 440, 300))
        text = Label (fen2, text = tx+'\n', fg = 'black', bd = 16, bg = 'dark grey', font = beb)
        text.pack()
        bout9 =Button (fen2, command = fen2.destroy, text = bt, bd = 16, bg = 'yellow', font = beb)
        bout9.pack()
        text = Label (fen2, text = '\n', bg = 'dark grey')
        text.pack()
        fen2.mainloop()
    Problème lors de l'exécution : lorsque fen2 se referme lors de l'appui sur le bouton, la fenêtre racine, bien que ne se fermant pas à l'écran, est considérée comme étant aussi détruite et donc inutilisable.
    J'ai le droit à l'exception suivante lorsque je veux écrire sur la fenêtre racine :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    Exception in Tkinter callback
    Traceback (most recent call last):
    ...
    ...
    ...
        (widgetName, self._w) + extra + self._options(cnf))
    _tkinter.TclError: can't invoke "label" command:  application has been destroyed
    Quelqu'un peut-il m'aider ? Merci d'avance à toute bonne âme secourable.

  2. #2
    Membre émérite
    Homme Profil pro
    Inscrit en
    Décembre 2007
    Messages
    758
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 46
    Localisation : France

    Informations professionnelles :
    Secteur : Aéronautique - Marine - Espace - Armement

    Informations forums :
    Inscription : Décembre 2007
    Messages : 758
    Par défaut
    bonsoir,

    je suis surpris du fen2.mainloop(). Pour moi la mainloop doit être appelée sur l'élément racine dont tu parles.

  3. #3
    Membre averti
    Homme Profil pro
    Enseignant
    Inscrit en
    Août 2011
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2011
    Messages : 16
    Par défaut
    Alors, effectivement, le fen2.mainloop() semble inutile (??), je l'ai enlevé sans aucun changement dans l'exécution.

    Ma fenêtre d'arrière-plan est la suivante :

    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
    racine=Tk()
    racine.title ('--- VV ---')
    racine.wm_state(newstate="zoomed")
    division=tkinter.PanedWindow(orient=tkinter.HORIZONTAL) ### division gauche+droite en division
    division.pack(expand="yes",fill="both")
    gauche=tkinter.PanedWindow(orient=tkinter.VERTICAL) ### division mission+exterieur en gauche
    gauche.pack(expand="yes",fill="both")
    mission=tkinter.Label(gauche,text='MODE 0\n\nDEMARRAGE\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', bg = 'yellow', bd = 16, font = beb)
    gauche.add(mission)
    exterieur=tkinter.Label(gauche,text='Zone de relation avec extérieur', bg = 'orange', bd = 16, font = beb)
    gauche.add(exterieur)
    division.add(gauche)
    droite=tkinter.PanedWindow(orient=tkinter.VERTICAL) ### division secteur+dialogue en droite
    droite.pack(expand="yes",fill="both")
    secteur=tkinter.PanedWindow(orient=tkinter.HORIZONTAL) ### division reel+param en secteur
    secteur.pack(expand="yes",fill="both")
    #reel=tkinter.PanedWindow(orient=tkinter.VERTICAL) ### division titre+carte en reel
    reel.pack(expand="yes",fill="both")
    titre=tkinter.Label(reel,text='##########################################    VV    ##########################################', bd = 16, bg = 'green', font = 'BALLOONEFEXTRABOLD')
    reel.add(titre)
    carto=tkinter.Label(reel,text='\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n\n', bd = 16, font = beb)
    reel.add(carto)
    secteur.add(reel)
    param=tkinter.Label(secteur,text='Zone de paramètres', bg = 'yellow', bd = 16, font = beb)
    secteur.add(param)
    droite.add(secteur)
    dialogue=tkinter.Label(droite,text='Zone de dialogue', bg = 'pink', bd = 16, font = beb)
    droite.add(dialogue)
    division.add(droite)
    menu_init ()   ### menu initial (vers ouvfen0)
    racine.mainloop()
    racine.destroy  ### instruction sans effet ???
    Voilà, je ne peux reprendre la main sur 'racine', qui reste ouverte à l'écran, mais seulement à l'écran...

  4. #4
    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 Ilpirato Voir le message
    Voilà, je ne peux reprendre la main sur 'racine', qui reste ouverte à l'écran, mais seulement à l'écran...
    A première vue vous avez un code 'bloquant' dans menu_init.
    Vous pouvez le tester:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    racine.title("Avant menu_init")
    menu_init ()   ### menu initial (vers ouvfen0)
    racine.title("Après menu_init")
    racine.mainloop()
    @+

    Quelques notes:
    Vous devez avoir un souci dans vos import de tkinter
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    racine=Tk()
    racine.title ('--- VV ---')
    racine.wm_state(newstate="zoomed")
    division=tkinter.PanedWindow(orient=tkinter.HORIZONTAL) ### division
    Cela fleure bon le
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    from tkinter import *
    import tkinter
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    .wm_state(newstate="zoomed")
    zoomed n'existe que sous Windows.

    Il manque quelques .pack() (param, dialogue...) mais c'est sans doute voulu.

    Oui, le .destroy() (et non destroy)de la fin est inutile puisque... Il n'y a rien derrière.

    @++

  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
    Citation Envoyé par PauseKawa Voir le message
    Oui, le .destroy() (et non destroy)de la fin est inutile puisque... Il n'y a rien derrière.
    Vous avez sans doute trouver cela dans un exemple Tkinter sur le web et cela demande une explication.
    Certains environnements (dont IDLE) sont écrit en tkinter. Ils disposent donc de leurs propres gestionnaire d’événement (mainloop) ce qui fait que si le code est exécuté dans un tel environnement la fenêtre n'est pas détruite systématiquement.
    Le .destroy() après le .mainloop() est donc là pour la compatibilité.

    Nous avons donc ici
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    racine = Tk() # Création de l'instance Tk(). IL NE DOIS N'Y EN AVOIR QU'UNE.
    racine.mainloop() # Lancement du gestionnaire d’événement
    racine.destroy() # demande à l'interpréteur tcl la destruction de l'instance lorsque l'on termine le .mainloop()
    Le gestionnaire d’événement mainloop étant bloquant .destroy() n'est appelé que lorsque on le quitte. Pour terminer la gestion des événements on utilise .quit()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    racine = Tk()
    Button(racine, text='Quit', command=racine.quit).pack()
    racine.mainloop()
    racine.destroy()
    J'insiste bien : pour terminer le .mainloop() c'est quit. .destroy() lui détruit l'objet.
    Pourquoi cette précision ? Prenons le cas du code suivant.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    racine = Tk()
    Button(racine, text='Quit', command=racine.quit).pack()
    Button(racine, text='Destroy', command=racine.destroy).pack()
    racine.mainloop()
    racine.destroy()
    Dans le cadre ou vous fermer la fenêtre autrement que par le Widget Button 'Quit' (Soit par le Button 'Destroy', la 'croix' de fermeture de la fenêtre etc) la fenêtre est déjà détruite et cela provoque une erreur (non visible si vous êtes en fin du script bien sur) _tkinter.TclError: can't invoke "destroy" command: application has been destroyed. L'application se termine donc sur une erreur.
    Mais alors comment faire une application compatible avec de tels environnements ? Toujours utiliser quit pour finir le mainloop et bien associer les méthodes à votre instance Tk(), comprendre que command=quit fonctionne mais que command=racine.quit est préférable.
    Dans ce sens je ne suis pas trop d'accord avec la FAQ de site, soit :
    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()
    J'aurais plutôt écrit :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import Tkinter as Tk
     
    root = Tk.Tk()
     
    def Intercepte():
        print "Interception de la fermeture de la fenetre"
        root.quit()
     
    root.protocol("WM_DELETE_WINDOW", Intercepte)
    root.mainloop()
    root.destroy()
    Vous remarquerez au passage l'import import Tkinter as Tk et son utilisation, root = Tk.Tk(). Ce genre d'import est préférable et j'en parle ici.

    @++

  6. #6
    Membre averti
    Homme Profil pro
    Enseignant
    Inscrit en
    Août 2011
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2011
    Messages : 16
    Par défaut
    Alors, merci beaucoup pour ces propositions et commentaires, ils sont toujours utiles pour le novice que je suis...

    S'ils ne m'ont pas apporté la réponse directement, ils m'ont permis de faire mûrir une (longue) réflexion empreinte de nombreux essais infructueux.

    Et de déplacer le problème... Je n'avais pas compris que mainloop() tournait indéfiniment en boucle et que seul un appui sur un bouton (donc celui de la fenêtre fen2, vu qu'il n'y en avait pas dans la fenêtre racine) pouvait stopper l'effet de cette instruction.

    Voici donc ouvfen0 modifié :

    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
    def ouvfen0 (tx, bt) :
        fen2 = Toplevel()
        fen2.grab_set()
        fen2.focus_set()
        fen2.title ('--- VV ---')
        fen2.config(bg = 'dark grey')
        fen2.wm_geometry('%dx%d+%d+%d' % (560, 200, 440, 300))
        text = Tk.Label (fen2, text = tx+'\n', fg = 'black', bd = 16, bg = 'dark grey', font = beb)
        text.pack()
        shut = fen2.destroy
        pg = racine.destroy
        q0 = lambda: ouvfen_dotwo(shut, pg)
        bout9 =Tk.Button (fen2, command = q0, text = bt, bd = 16, bg = 'yellow', font = beb)
        bout9.pack()
        text = Tk.Label (fen2, text = '\n', bg = 'dark grey')
        text.pack()
    Ce qui a réglé le problème : les deux fenêtres se ferment de concert.

    Merci donc encore aux contributeurs.

  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
    Bonsoir,

    Heu...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        shut = fen2.destroy
        pg = racine.destroy
    etc...
    Vous vous perdez là.

    Relisez svp

    @+

  8. #8
    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 Ilpirato Voir le message
    Alors, merci beaucoup pour ces propositions et commentaires, ils sont toujours utiles pour le novice que je suis...

    S'ils ne m'ont pas apporté la réponse directement, ils m'ont permis de faire mûrir une (longue) réflexion empreinte de nombreux essais infructueux.
    C'est pourtant bien la réponse, il me semble.

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    def ouvfen0 (tx, bt) :
        fen2 = Toplevel()
        fen2.grab_set()
        fen2.focus_set()
        fen2.title ('--- VV ---')
        fen2.config(bg = 'dark grey')
        fen2.wm_geometry('%dx%d+%d+%d' % (560, 200, 440, 300))
        text = Label (fen2, text = tx+'\n', fg = 'black', bd = 16, bg = 'dark grey', font = beb)
        text.pack()
        bout9 =Button (fen2, command = fen2.destroy, text = bt, bd = 16, bg = 'yellow', font = beb)
        bout9.pack()
        text = Label (fen2, text = '\n', bg = 'dark grey')
        text.pack()
        fen2.mainloop()
    En associent un mainloop() au Toplevel et en détruisant celui ci (fen2.destroy) vous avez planter votre application.
    Il ne dois y avoir qu'une instance Tk() (racine = Tk()) et le mainloop doit lui être associer (racine.mainloop()).

    Citation Envoyé par Ilpirato Voir le message
    Je n'avais pas compris que mainloop() tournait indéfiniment en boucle et que seul un appui sur un bouton (donc celui de la fenêtre fen2, vu qu'il n'y en avait pas dans la fenêtre racine) pouvait stopper l'effet de cette instruction.
    Ce qui mets fin au gestionnaire d’événement c'est racine.quit()
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    def ouvfen0 (tx, bt) :
        fen2 = Toplevel(racine)
        fen2.grab_set()
        fen2.focus_set()
        fen2.title ('--- VV ---')
        fen2.config(bg = 'dark grey')
        fen2.wm_geometry('%dx%d+%d+%d' % (560, 200, 440, 300))
        text = Tk.Label (fen2, text=tx+'\n', fg='black', bd=16, bg='dark grey', font=beb)
        text.pack()
        Tk.Button (fen2, command=racine.quit, text=bt, bd=16, bg='yellow', font=beb).pack()
        text = Tk.Label (fen2, text='\n', bg='dark grey')
        text.pack()
    Le fait de mettre fin au mainloop() quitte racine (vous pouvez mettre le racine.destroy() après racine.mainloop() mais pensez a utiliser racine.protocol("WM_DELETE_WINDOW", Intercepte) et racine.quit()) et ses enfants, dont le Toplevel.
    C'est plus propre, non ?

    Au passage vous remarquerez que vos deux label se nomment 'text'.
    Bien que les deux existent pour tcl pour Python seul le deuxième est utilisable puisque le nom est écrasé dans l'espace de nom.
    Si vous n'avez pas besoin de les modifier pas besoin de les nommer (Voir Tk.Button(....).pack() command=racine.quit est stocké dans le Widget)
    Et si vous en avez besoin attention de bien les nommer. Je préfère text1 ou texte à text, c'est plus lisible (comprendre text='\n').
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    def ouvfen0 (tx, bt) :
        fen2 = Toplevel(racine)
        fen2.grab_set()
        fen2.focus_set()
        fen2.title ('--- VV ---')
        fen2.config(bg = 'dark grey')
        fen2.wm_geometry('%dx%d+%d+%d' % (560, 200, 440, 300))
        Tk.Label (fen2, text=tx+'\n', fg='black', bd=16, bg='dark grey', font=beb).pack()
        Tk.Button (fen2, command=racine.quit, text=bt, bd=16, bg='yellow', font=beb).pack()
        Tk.Label (fen2, text='\n', bg='dark grey').pack()
    Qu'en pensez vous ?

    Pensez aussi a donner le parent (Toplevel(racine)), cela évite les surprises.

    @+

  9. #9
    Membre averti
    Homme Profil pro
    Enseignant
    Inscrit en
    Août 2011
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2011
    Messages : 16
    Par défaut
    Citation Envoyé par PauseKawa Voir le message
    Bonsoir,

    Heu...
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        shut = fen2.destroy
        pg = racine.destroy
    etc...
    Vous vous perdez là.

    Relisez svp

    @+
    Non non, c'est bien ce que je veux... Et cela fonctionne très bien...

  10. #10
    Membre averti
    Homme Profil pro
    Enseignant
    Inscrit en
    Août 2011
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2011
    Messages : 16
    Par défaut
    En associent un mainloop() au Toplevel et en détruisant celui ci (fen2.destroy) vous avez planter votre application.
    Il ne dois y avoir qu'une instance Tk() (racine = Tk()) et le mainloop doit lui être associer (racine.mainloop()).
    J'ai bien enlevé le 'mainloop()' sur votre conseil, comme vous pouvez le voir plus haut... Et tout va bien...
    Par ailleurs, j'ai remplacé text par text1, Toplevel() par Toplevel(racine) aussi, merci de ces remarques judicieuses).

  11. #11
    Membre averti
    Homme Profil pro
    Enseignant
    Inscrit en
    Août 2011
    Messages
    16
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Enseignant
    Secteur : Enseignement

    Informations forums :
    Inscription : Août 2011
    Messages : 16
    Par défaut
    Citation Envoyé par PauseKawa Voir le message
    Le fait de mettre fin au mainloop() quitte racine (vous pouvez mettre le racine.destroy() après racine.mainloop() mais pensez a utiliser racine.protocol("WM_DELETE_WINDOW", Intercepte) et racine.quit()) et ses enfants, dont le Toplevel.
    C'est plus propre, non ?
    J'ai bien essayé "WM_DELETE_WINDOW", sans aucun effet opportun (fenêtre bloquée, impossible à refermer...) sur ce que je voulais en la circonstance.

    Merci encore...

+ Répondre à la discussion
Cette discussion est résolue.

Discussions similaires

  1. Pb de Toplevel en arrière plan
    Par lipaika dans le forum Tkinter
    Réponses: 4
    Dernier message: 30/12/2007, 12h16
  2. fenêtre principale et Toplevels
    Par Chris33 dans le forum Tkinter
    Réponses: 2
    Dernier message: 07/11/2006, 08h48
  3. problème avec toplevel
    Par Chris33 dans le forum Tkinter
    Réponses: 8
    Dernier message: 15/09/2006, 09h24
  4. [tkinter] maintien d'un toplevel en 1er plan
    Par airod dans le forum Tkinter
    Réponses: 1
    Dernier message: 07/03/2006, 16h28
  5. [VB.NET]Ma Form entre une toplevel Form et une topMost popup
    Par jazz matazz dans le forum Windows Forms
    Réponses: 15
    Dernier message: 28/02/2006, 14h51

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