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 :

Tkinter. Finir mainloop() ?


Sujet :

Tkinter Python

  1. #1
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2005
    Messages
    9
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2005
    Messages : 9
    Points : 10
    Points
    10
    Par défaut Tkinter. Finir mainloop() ?
    Bonjour à tous,

    J'étudie l'informatique théorique depuis septembre et j'ai un travail de projet à rendre pour bientôt qui me pose quelques problèmes. Je ne peux guère aller voir mes profs qui sont tous des chercheurs et qui sont assez mauvais en programmation.

    J'ai décidé de programmer une interface graphique pour un jeu de Poker en python sur lequel je travaille depuis un mois.
    Pour éviter de passer mes nuits à débugger j'ai choisi pour le programme une structure séquentielle très simple (il ne se passe jamais qu'une chose à la fois) mais lorsque je m'attaque à l'interface graphique tout se complique alors que fondamentalement je n'en ai pas besoin.

    Voici en gros le code qui pose 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
    class GUI(Tk):
        def __init__(self)
            Tk.__init__(self)
            Button(self,text='youpi',command=get_info).grid()
        def get_info(self):
            self.a=Toplevel(self)
            self.b=Entry(self.a,text='coucou')
            self.b.bind("<Return>",self.couic)
            self.b.grid()
            self.a.mainloop()
            return self.result
        def couic(self,event):
            self.result=self.b.get()
            self.a.destroy()
     
    if __name__=='__main__':
        test=GUI()
        test.mainloop()
    (ce n'est pas le vrai code )

    Ce que je voulais c'est que get_info fonctionne comme input() en console : qu'elle renvoie une variable entrée par l'utilisateur et qu'elle bloque l'exécution du programme en attendant le résultat.

    Le problème c'est que lorsque la fenêtre est détruite self.a.mainloop() ne finit pas et je n'arrive jamais jusqu'au return...

    Comment faire dans self.couic pour mettre fin à la mainloop ?

    Et globalement comment créer un Toplevel et le lancer sans bloquer l'éxecution du script courant ?

    Merci d'avance

    Vincent

  2. #2
    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
    A vrais dire je ne comprend pas ton code exemple.

    Cela correspond t'il a ta demande ? :

    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
     
    from Tkinter import *
     
    class GUI(Tk):
     
        def __init__(self):
            Tk.__init__(self)
            Button(self,text='youpi',command=self.get_info).grid()
     
        def get_info(self):
            self.a=Toplevel(self)
            self.b=Entry(self.a,text='coucou') # Pour ce qui est du text= regarde mieux les entry
            self.b.grid()
            self.b.bind("<Return>", lambda event: self.couic(self.b.get()))
     
        def couic(self, result):
            self.a.destroy()
            print result
            return result
     
    if __name__=='__main__':
        test=GUI()
        test.mainloop()
    Si oui c'est juste une question d'ordre.

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

  3. #3
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2005
    Messages
    9
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2005
    Messages : 9
    Points : 10
    Points
    10
    Par défaut
    Tout d'abord merci d'avoir pris le temps de lire mon post et d'y répondre.

    Pour ce qui est du text= dans le Entry c'est une erreur de report. Le vrai script a un Label juste avant le Entry et j'ai fait un amalgame des deux...

    Pour ce qui est du script lui-même :
    si je ne me trompe pas (mais il est probable que ce soit le cas) dans ton script la fonction get_info s'exécute et renvoie None puis la fonction couic s'exécute et renvoie l'information entrée par l'utilisateur (mais la renvoie à quoi ?).

    Or ce que je voulais c'est que la fonction get_info :
    -crée la fenêtre
    -se bloque en attendant que l'utilisateur entre son info
    -se débloque lorsque la fenêtre est détruite par self.couic
    -renvoie l'information qui aura été lue entre temps

    En fait j'ai mon script principal qu'on pourrait schématiser :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    test=GUI()
    a=test.get_info() #comme on aurait écrit a=input()
    b=f(a)       #etc...
    L'idée c'est que je ne veut pas que le script principal continue à s'exécuter alors que l'utilisateur n'a pas entré l'information.
    J'ai bien conscience que je pourrais modifier le script principal pour que la suite des instructions soit déclenchée par test.couic() mais le script principal ne peut pas vraiment être modifié (j'ai passé beaucoup de temps à bosser sur la structure pour que tout soit bien compartimenté, la modification du GUI pour passer de la version console à la version graphique ne devrait pas nécessiter de modifications ailleurs que dans la classe GUI elle-même, mais peut-être que ce n'est pas possible).

  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
    Citation Envoyé par sigmar_avenger Voir le message
    Pour ce qui est du script lui-même :
    si je ne me trompe pas (mais il est probable que ce soit le cas) dans ton script la fonction get_info s'exécute et renvoie None puis la fonction couic s'exécute et renvoie l'information entrée par l'utilisateur (mais la renvoie à quoi ?).
    Que neni : get_info attend l'évènement 'entrer' pour renvoyer la valeur de b à couic

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    self.b.bind("<Return>", lambda event: self.couic(self.b.get()))
    Après cela je retrouve le self.b.get en variable dans couic :

    def couic(self, result):

    A partir de la plus besoin de a (donc self.a.destroy()) puisque result est une locale de couic.

    Pour ce qui est du print et return de couic j'ai mis cela car je ne sais pas ce que tu souhaite en faire.

    Cela reviens au même que ton :

    Citation Envoyé par sigmar_avenger Voir le message
    Or ce que je voulais c'est que la fonction get_info :
    -crée la fenêtre
    -se bloque en attendant que l'utilisateur entre son info
    -se débloque lorsque la fenêtre est détruite par self.couic
    -renvoie l'information qui aura été lue entre temps
    La preuve :

    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
    from Tkinter import *
    
    class GUI(Tk):
    
        def __init__(self):
            Tk.__init__(self)
            self.Retour = StringVar()
            AfficheRetour = Label(self, textvariable=self.Retour)
            AfficheRetour.grid()
            Button(self,text='youpi', command=self.get_info).grid()
    
        def get_info(self):
            self.a=Toplevel(self)
            self.b=Entry(self.a,text='coucou')
            self.b.grid()
            self.b.bind("<Return>", lambda event: self.couic(self.b.get()))
    
        def couic(self, result):
            self.a.destroy()
            self.Retour.set(str(result))
    
    if __name__=='__main__':
        test=GUI()
        test.mainloop()
    (Avec un petit str(result) au cas ou...)

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

  5. #5
    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
    Citation Envoyé par sigmar_avenger Voir le message
    Et globalement comment créer un Toplevel et le lancer sans bloquer l'éxecution du script courant ?
    Je n'avais pas vu cette question qui est tout autre que le fait de bloquer l'utilisateur dans l'attente de la réponse.
    Toutefois le fait d'avoir un toplevel indépendant permet de vérifier la réponse de l'utilisateur en arrière plan.
    Dans l'exemple suivant MonEvent tourne tant que Retour != 'FIN' et Affiche tant que Retour != 'fin'. La fermeture du Toplevel Affiche position Terminated à True ce qui ferme le Thread qui la lance.
    Je laisse aussi un entry de basse pour l'exemple.

    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
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    from Tkinter import *
    import time
    from threading import Thread
     
    class Globals():
        Retour = None
        Terminated = False
     
    class MonEvent(Thread):
        def __init__(self):
            Thread.__init__(self)
     
        def run(self):
            print 'lancement du thread MonEvent'
            ind = 0
            while not Globals.Retour.get() != 'FIN':
                ind += 1
                print ind
                time.sleep(2)
            print 'fin du thread MonEvent'
     
    class Affiche(Toplevel):
        def __init__(self):
            print "lancement d'Affiche"
            Toplevel.__init__(self)
            self.protocol("WM_DELETE_WINDOW", self.Intercepte)
            self.b=Entry(self)
            self.b.grid()
            self.b.bind("<Return>", lambda event: self.couic(self.b.get()))
     
        def couic(self, result):
            Globals.Retour.set(str(result))
            if str(result) == 'fin':
                print 'demande de fin du thread MaFenetre'
                Globals.Terminated = True
                print "fermeture d'Affiche"
                self.destroy()
     
        def Intercepte(self, event = None):
            Globals.Terminated = True
            self.destroy()
     
    class MaFenetre(Thread):
        def __init__(self):
            Thread.__init__(self)
            Globals.Terminated = False
     
        def run(self):
            print 'debut du thread MaFenetre'
            fen = Affiche()
            while not Globals.Terminated:
                pass
            print "le thread MaFenetre s'est termine proprement"
     
     
     
    class GUI(Tk):
     
        def __init__(self):
            Tk.__init__(self)
            Globals.Retour = StringVar()
            self.protocol("WM_DELETE_WINDOW", self.Intercepte)
            AfficheRetour = Label(self, textvariable=Globals.Retour)
            AfficheRetour.grid()
            Button(self,text='entry simple', command=self.get_info).grid()
            Button(self,text='thread input', command= lambda:MaFenetre().start()).grid()
            Button(self,text='thread calcul', command= lambda:MonEvent().start()).grid()
     
        def get_info(self):
            self.a=Toplevel(self)
            self.b=Entry(self.a)
            self.b.grid()
            self.b.bind("<Return>", lambda event: self.couic(self.b.get()))
     
        def couic(self, result):
            Globals.Retour.set(str(result))
            if str(result) == 'fin':
                print 'fermeture de a'
                self.a.destroy()
     
        def Intercepte(self, event = None):
            Globals.Terminated = True
            self.destroy()
     
    if __name__=='__main__':
        test=GUI()
        test.mainloop()
    Le code n'est sans doute pas très propre et un pro du threading corrigeras cela sans doute mais l'idée y est.

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

  6. #6
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2005
    Messages
    9
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2005
    Messages : 9
    Points : 10
    Points
    10
    Par défaut
    Pour ce qui est du threading, je savais que ça existait mais je veux justement éviter de m'en servir parce que ça complexifie beaucoup le programme (déjà sans, je vais manquer de temps pour débugger). Ce qui m'aurait arrangé c'est que la destruction de la fenêtre finisse l'instruction mainloop() et passe à la suite.

    Pour ce qui est de l'autre problème j'ai peur qu'on ne se soit toujours pas compris
    Cela reviens au même que ton :

    Or ce que je voulais c'est que la fonction get_info :
    -crée la fenêtre
    -se bloque en attendant que l'utilisateur entre son info
    -se débloque lorsque la fenêtre est détruite par self.couic
    -renvoie l'information qui aura été lue entre temps
    Justement non, si j'écris get_info comme ceci :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    def get_info(self):
            self.a=Toplevel(self)
            self.b=Entry(self.a,text='coucou')
            self.b.grid()
            self.b.bind("<Return>", lambda event: self.couic(self.b.get()))
            print "hello"         #Nouvelle ligne
    Le print hello s'exécute au moment où l'on crée la fenêtre alors que je voudrais qu'il s'exécute au moment ou l'utilisateur a fini d'entrer sa réponse.

    Mon objectif c'est que get_info renvoie l'information entrée par l'utilisateur ce qui suppose de mettre une instruction return après la création de la fenêtre et que cette instruction ne soit pas exécutée tant que l'utilisateur n'a pas entré l'info.

    En fait je veux écrire une fonction qui marcherait exactement comme si j'avais :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    def get_info(self):
        a=input("Veuillez entrer l'information : ")
        return a
    mais qui afficherait une petite fenêtre au lien de prendre l'input dans la console.

    La solution que tu propose fonctionne mais nécessite une réécriture du programme principal (je ne peux pas récupérer l'information directement avec a=interface.get_info() où interface est un objet de classe GUI).

    J'ai une classe GUI déjà écrite (qui fonctionne entièrement dans la console) et je veux modifier sa façon de fonctionner en interne (fenêtres graphiques à la place de la console) sans modifier sa "signature" (entrées/sorties des fonctions/attributs qu'utilisent les programmes qui se servent de ma classe).
    Je me suis mis d'accord avec mon binôme de projet (qui travaille avec moi depuis un mois sur ce programme) sur le fonctionnement de la classe GUI(). Si je modifie complètement le fonctionnement des fonctions dont il a besoin il va devoir réécrir la moitié de son code.

    Merci en tout cas d'essayer de m'aider

  7. #7
    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
    C'est ma courge attitude : je n'avais pas compris dans ce sens.

    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
    from Tkinter import *
    
    class GUI(Tk):
    
        def __init__(self):
            Tk.__init__(self)
            self.Retour = StringVar()
            AfficheRetour = Label(self, textvariable=self.Retour)
            AfficheRetour.grid()
            Button(self,text='youpi', command=self.get_info).grid()
            self.wait_variable(self.Retour)
            print 'la suite'
    
        def get_info(self):
            self.a=Toplevel(self)
            self.b=Entry(self.a,text='coucou')
            self.b.grid()
            self.b.bind("<Return>", lambda event: self.couic(self.b.get()))
    
        def couic(self, result):
            self.a.destroy()
            self.Retour.set(str(result))
    
    if __name__=='__main__':
        test=GUI()
        test.mainloop()
    Merci d'utiliser le forum pour les questions techniques.

  8. #8
    Membre à l'essai
    Profil pro
    Inscrit en
    Août 2005
    Messages
    9
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Août 2005
    Messages : 9
    Points : 10
    Points
    10
    Par défaut
    Voilà,
    c'est exactement ce que je voulais,
    je ne connaissais pas cette fonction (wait_variable).
    Désolé de ne pas avoir été plus clair

    Si je vais vite je peux avoir programmé l'interface pour vendredi soir

    Merci pour ton aide

    Je met le sujet en résolu

  9. #9
    Futur Membre du Club
    Profil pro
    Inscrit en
    Mars 2010
    Messages
    12
    Détails du profil
    Informations personnelles :
    Localisation : France, Paris (Île de France)

    Informations forums :
    Inscription : Mars 2010
    Messages : 12
    Points : 8
    Points
    8
    Par défaut
    Une petite astuce pour 'continuer le code' après l'apparition du TopLevel : utiliser la fonction after() de la classe TopLevel qui exécute une fonction juste après son apparition. On prend le reste du code sous une nouvelle fonction, on l'appelle comme ça et on continue... Du coup le mainloop() ne devient plus un problème.

    Certes c'est assez moche, mais c'est le seul truc que j'ai trouvé depuis trois heures que je cherche, sans avoir à utiliser des threads.

    edit : la fonction after() prend 2 paramètres : un temps en ms (déclenchement d'exécution, à 0 donc dans notre cas), la fonction.

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

Discussions similaires

  1. débutant en VBA je n'arrive pas à finir ma macro excel
    Par jeanpierreco dans le forum Macros et VBA Excel
    Réponses: 1
    Dernier message: 19/01/2005, 12h20
  2. Réponses: 3
    Dernier message: 26/10/2004, 07h31
  3. Réponses: 3
    Dernier message: 16/08/2004, 10h57
  4. [TPW][cours]Demande d'aide pour finir un programme
    Par jf dans le forum Turbo Pascal
    Réponses: 21
    Dernier message: 16/06/2003, 18h10
  5. Finir une application
    Par Lucien dans le forum Composants VCL
    Réponses: 4
    Dernier message: 08/04/2003, 09h15

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