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 :

Thread et rafraichissement Tkinter


Sujet :

Tkinter Python

  1. #1
    Membre émérite
    Avatar de panda31
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Juin 2003
    Messages
    670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2003
    Messages : 670
    Par défaut Thread et rafraichissement Tkinter
    Bonjour à tous,

    Mon application lance via des threads des traitements externes (des scripts, d'autres programmes python, etc...). Le problème est que pendant l'exécution d'un traitement, mon IHM se fige.

    Pourtant, tous les traitements sont lancés via des threads. Je pensais que cela éviterait le problème.

    Vous auriez une idée pour contourner le problème. J'aimerais assez éviter de faire un thread pour le GUI et de faire communiquer les deux... parce que je ne sais pas faire

    Merci d'avance
    Michaël Mary
    Consultant PLM dans une société de conseil toulousaine
    Auditeur CNAM-IPST depuis septembre 2008
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
    John F. Woods
    mon cv et mon domaine et mon blog
    Aucune question technique par MP, svp

  2. #2
    Membre chevronné
    Avatar de vincent.mbg
    Homme Profil pro
    Développeur Python
    Inscrit en
    Décembre 2007
    Messages
    327
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Développeur Python

    Informations forums :
    Inscription : Décembre 2007
    Messages : 327
    Par défaut
    Pour faire communiquer des threads entre eux tu a le module queue

    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
     
    from Queue import Queue
    import threading
     
    def foo( uneFile ) :
        uneFile.put( 'data', 1 )#Ecrit 'data' dans la file 
        #Si la file est pleine, attente quelle se libére (1)
     
    def foo2( uneFile ) :
        print uneFile.get(1)# Si la file est vide, attente quelle se remplise
        # pour afficher l'element
     
    maFile = Queue( 8 ) # file de 8 elements
    t1 = threading.Thread( target = foo,  args =  (maFile, ) )
    t2 = threading.Thread( target = foo2, args =  ( maFile, ) )
     
    t1.start()
    t2.start()
    c'est étrange que ton IHM se fige a tu un bout de code simplifier
    Mon guide pour apprendre Tkinter - N'oubliez pas de consulter les FAQ Python ou de visiter mon blog

  3. #3
    Membre émérite
    Avatar de panda31
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Juin 2003
    Messages
    670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2003
    Messages : 670
    Par défaut
    Je peux expliquer mon IHM déjà. Je la lance normalement

    - j'ajoute des éléments à une racine créée avec Tkinter.Tk()
    - j'ai un ensemble de checkbox qui sont rattachées à des actions (reliées à des scripts, des exe, des prog python...).
    - Quand je clique sur Run, pour chaque action sélectionnée, je crée un Thread avec le programme/script associé.

    Commande associée au bouton Run
    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
     
        def run(self,):
            '''
            Run selected actions using action own 'run' method
            '''       
            import locale
            import datetime
     
            #locale.setlocale(locale.LC_ALL, 'fr_FR')
            now = datetime.datetime.now()
            now = now.strftime("%A %d %B %Y, %H:%M:%S")
     
            # Je vide la fenêtre Detail qui est un Tkinter.Text
            self.clearDetail()
            # J'écris dans Detail
            self.argList.insert(Tkinter.END,self.process.name+'\n','PROCESS')
            # J'écris dans la Log
            self.logText.insert(Tkinter.END,self.process.name+' ('+now+')\n','PROCESS')
     
            # Pour chaque action sélectionnée
            for but,lcheckb in self.boutons.items():
                sname = but.cget('text')
                for sect in self.process.sections:
                    if sname == sect.name:                            
                        lact = []
                        for c in lcheckb:
                            if (c.var.get() == 1):
                                lact.append(c.cget('text'))
                        if len(lact) != 0:
                            self.argList.insert(Tkinter.END,sname+'\n','SECTION')
                            self.logText.insert(Tkinter.END,sname+'\n','SECTION')
                            for a in lact:
                                self.current_process = None
                                self.running_threads.append(a)
                                for act in sect.actions:
                                    if a == act.name:                            
                                        self.argList.insert(Tkinter.END,a+' ('+act.path+')\n','ACTION')
                                        i=0
                                        for arg in act.args:
                                            i=i+1
                                            self.argList.insert(Tkinter.END,'      argument '+str(i)+' : ' +arg+'\n','ARG')
                                        self.argList.insert(Tkinter.END,'\n')
     
                                        self.logText.insert(Tkinter.END,a+' ('+act.path+')\n','ACTION')
                                        self.logText.focus_force()
     
                                        # Je lance mon thread
                                        self.current_process = threading.Thread(target = self.runOneAction(act),name=act.name)
                                        self.current_process.start()
    Code de runOneAction associée aux threads:
    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
     
        def runOneAction(self,act):
            '''
            Run one action
            '''
            self.cancel = 0
            # Run action
            self.current_action = act.run()
            # Get output and error streams
            outdata,errdata = self.current_action.communicate()
            if errdata == '':
                self.argList.insert(Tkinter.END,'Result for '+act.path+':\n\n','ACTION_SUCCESS')
                self.logText.insert(Tkinter.END,'Success\n','SUCCESS')
            else:
                self.argList.insert(Tkinter.END,'Running '+act.path+' failed!\n\n','ACTION_FAIL')
                for l in errdata:
                    self.logText.insert(Tkinter.END,l.decode("latin1", "replace"),'FAIL')
            for l in outdata:
                # Problem avec l.decode. Caractères bizarres sur windows...
                self.argList.insert(Tkinter.END,l.decode("latin1", "replace"))
            self.argList.insert(Tkinter.END,'\n')
            self.logText.insert(Tkinter.END,'\n')
    Je m'attends à pouvoir envoyer des signaux au thread par cette méthode via l'IHM mais elle se bloque.
    La seule communication que je veux faire, c'est arrêter le thread en cours.

    [Edit: Fonction qui est censée stopper le thread]
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
        def panic(self):        
            '''
            Callback for catch PANIC event
            Stop all threads running
            '''
            self.cancel = 1
            if self.current_action:
                while self.current_action.poll() == None: # While process has not yet terminated.
                    if self.cancel==1:
                        current_action.terminate()
                        break
    Michaël Mary
    Consultant PLM dans une société de conseil toulousaine
    Auditeur CNAM-IPST depuis septembre 2008
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
    John F. Woods
    mon cv et mon domaine et mon blog
    Aucune question technique par MP, svp

  4. #4
    Membre chevronné
    Avatar de vincent.mbg
    Homme Profil pro
    Développeur Python
    Inscrit en
    Décembre 2007
    Messages
    327
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Développeur Python

    Informations forums :
    Inscription : Décembre 2007
    Messages : 327
    Par défaut
    Je m'attends à pouvoir envoyer des signaux au thread par cette méthode via l'IHM mais elle se bloque.
    La seule communication que je veux faire, c'est arrêter le thread en cours.
    On ne peut que lancer un Thread et attendre qu'il s'arrête.

    Après tu a des astuces comme les objets Lock pour coordonner plusieurs threads les uns à la suite des autres.

    Ton code est trés gros.
    dit moi si ce bout de code résumerai ton 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
     
    from Tkinter import *
    from time import sleep
    import threading
     
    def foo() :
       for e in range(5) :
          sleep(1)
          label.config( text = e )#Le label n'est pas mit dirrectement a jour.
     
     
     
     
    root = Tk()
    label = Label(root )
     
    label.pack()
    Button( text = 'run', command = foo ).pack()
     
    root.mainloop()
    Mon guide pour apprendre Tkinter - N'oubliez pas de consulter les FAQ Python ou de visiter mon blog

  5. #5
    Membre chevronné
    Avatar de vincent.mbg
    Homme Profil pro
    Développeur Python
    Inscrit en
    Décembre 2007
    Messages
    327
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 37
    Localisation : France, Hérault (Languedoc Roussillon)

    Informations professionnelles :
    Activité : Développeur Python

    Informations forums :
    Inscription : Décembre 2007
    Messages : 327
    Par défaut
    Avec des Thread, sa marche on peut lancer les deux sans que IHM se fige.


    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 *
    from time import sleep
    import threading
     
    def foo() :
       for e in range(5) :
          sleep(1)
          label.config( text = e )
     
    def foo2() :
       for e in range(5) :
          sleep(1)
          label.config( text = 'foo2' )
     
     
    root = Tk()
    label = Label(root )
     
    label.pack()
    Button( text = 'run', command = threading.Thread( target = foo,  args = () ).start ).pack()
    Button( text = 'run', command = threading.Thread( target = foo2,  args = () ).start ).pack()
    root.mainloop()
    Mon guide pour apprendre Tkinter - N'oubliez pas de consulter les FAQ Python ou de visiter mon blog

  6. #6
    Expert confirmé
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 486
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Var (Provence Alpes Côte d'Azur)

    Informations professionnelles :
    Activité : Retraité

    Informations forums :
    Inscription : Décembre 2007
    Messages : 4 486
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Juste un petit témoignage concernant ce type de sujet.

    J'ai fait une calculatrice en tkinter (voir mon site dans ma signature), et les calculs d'expressions sont faits dans un thread lancé par un "start" (non suivi par "join").

    Pendant l'exécution d'un calcul, qui peut être long, la partie graphique n'est pas bloquée. Par exemple, l'utilisateur peut consulter l'aide, ou même demander l'arrêt du calcul (=du thread) avant sa fin normale.

    Le seul cas où le thread de calcul bloque la partie graphique, c'est quand le résultat d'un calcul est très grand (p. ex. 10000 caractères), car son affichage graphique prend du temps. Et comme c'est le thread qui affiche, la partie graphique principale est bloquée jusqu'à la fin d'affichage. Je n'ai pas trouvé de solution à ce problème, et je suppose qu'il est produit par le fait que tkinter utilise les ressources d'un autre langage (tk/tcl) qui n'est pas soumis aux mécanismes des threads de python.

    Donc, pour ton pb: regarde si le blocage n'est pas produit par le fait que ton thread utilise lui-même des fonctions graphiques trop longues.

    Sinon:

    - l'accès aux même données entre le programme principal (=thread principal) et les threads se fait sans pb avec threading.Lock().

    - si on veut que le programme principal puisse s'arrêter sans attendre que les threads soient arrêtés, il faut lancer les threads en "thread daemon".

    - quand on veut tester dans le programme principal si un thread est encore actif ou arrêté, il ne faut pas utiliser join (qui est bloquant) mais "isAlive".

    Dernière remarque: quand un truc ne marche pas en tkinter et qu'on développe avec idle (lui-même en tkinter), il faut essayer de lancer l'application graphique directement.

  7. #7
    Membre averti
    Inscrit en
    Mai 2008
    Messages
    36
    Détails du profil
    Informations personnelles :
    Âge : 39

    Informations forums :
    Inscription : Mai 2008
    Messages : 36
    Par défaut
    Bonjour j'ai eu le même soucis y'a quelques temps, sous Win32 le Tkinter à tendance a se figer lors de l'exécution d'un programme multithread, alors que paradoxalement on utilise les thread pour avoir une IHM vivante
    bref exécute ton même programme via la console sans IHM et tu constatera qu'il tourne normalement !
    maintenant oriente toi vers une autre Biblio graphique ou bosse sous un autre OS
    concrètement c'est ce que j'ai fais

    bon courage et bonne continuation

  8. #8
    Membre émérite
    Avatar de panda31
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Juin 2003
    Messages
    670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2003
    Messages : 670
    Par défaut
    Citation Envoyé par vincent.mbg Voir le message
    On ne peut que lancer un Thread et attendre qu'il s'arrête.

    Après tu a des astuces comme les objets Lock pour coordonner plusieurs threads les uns à la suite des autres.

    Ton code est trés gros.
    dit moi si ce bout de code résumerai ton 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
     
    from Tkinter import *
    from time import sleep
    import threading
     
    def foo() :
       for e in range(5) :
          sleep(1)
          label.config( text = e )#Le label n'est pas mit dirrectement a jour.
     
     
     
     
    root = Tk()
    label = Label(root )
     
    label.pack()
    Button( text = 'run', command = foo ).pack()
     
    root.mainloop()
    Bonjour,

    Non ce bout de code ne m'aide pas trop malheureusement et oui, mon code est gros désolé !
    Le code est long mais la partie destinée au lancement de thread pas tellement. En fait, je prends les éléments sélectionnés dans une liste de check boxes dans la méthode run associée au bouton de l'IHM Run, et je lance via des runOneAction la méthode de lancement de l'action.
    J'utilise un subprocess.Popen que je mets dans un thread donc.

    Je ne mets pas à jour un label mais une boîte de texte qui fait office de log.

    Le problème se situe bien dans le thread. Si je dois utiliser un Queue, je ne vois pas comment faire...
    Michaël Mary
    Consultant PLM dans une société de conseil toulousaine
    Auditeur CNAM-IPST depuis septembre 2008
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
    John F. Woods
    mon cv et mon domaine et mon blog
    Aucune question technique par MP, svp

  9. #9
    Membre émérite
    Avatar de panda31
    Homme Profil pro
    Conseil - Consultant en systèmes d'information
    Inscrit en
    Juin 2003
    Messages
    670
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 40
    Localisation : France

    Informations professionnelles :
    Activité : Conseil - Consultant en systèmes d'information
    Secteur : Conseil

    Informations forums :
    Inscription : Juin 2003
    Messages : 670
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Bonjour,

    Juste un petit témoignage concernant ce type de sujet.

    J'ai fait une calculatrice en tkinter (voir mon site dans ma signature), et les calculs d'expressions sont faits dans un thread lancé par un "start" (non suivi par "join").

    Pendant l'exécution d'un calcul, qui peut être long, la partie graphique n'est pas bloquée. Par exemple, l'utilisateur peut consulter l'aide, ou même demander l'arrêt du calcul (=du thread) avant sa fin normale.

    Le seul cas où le thread de calcul bloque la partie graphique, c'est quand le résultat d'un calcul est très grand (p. ex. 10000 caractères), car son affichage graphique prend du temps. Et comme c'est le thread qui affiche, la partie graphique principale est bloquée jusqu'à la fin d'affichage. Je n'ai pas trouvé de solution à ce problème, et je suppose qu'il est produit par le fait que tkinter utilise les ressources d'un autre langage (tk/tcl) qui n'est pas soumis aux mécanismes des threads de python.

    Donc, pour ton pb: regarde si le blocage n'est pas produit par le fait que ton thread utilise lui-même des fonctions graphiques trop longues.

    Sinon:

    - l'accès aux même données entre le programme principal (=thread principal) et les threads se fait sans pb avec threading.Lock().

    - si on veut que le programme principal puisse s'arrêter sans attendre que les threads soient arrêtés, il faut lancer les threads en "thread daemon".

    - quand on veut tester dans le programme principal si un thread est encore actif ou arrêté, il ne faut pas utiliser join (qui est bloquant) mais "isAlive".

    Dernière remarque: quand un truc ne marche pas en tkinter et qu'on développe avec idle (lui-même en tkinter), il faut essayer de lancer l'application graphique directement.

    Je développe avec Pydev.
    Pour les locks, je suis pas sur que ce soit adapté à mon utilisation car je suis dans un mode d'exécution séquentiel pur. Il manque dans mon code le test de l'état de mon thread donc je vais faire des tests...
    Merci
    Michaël Mary
    Consultant PLM dans une société de conseil toulousaine
    Auditeur CNAM-IPST depuis septembre 2008
    "Always code as if the guy who ends up maintaining your code will be a violent psychopath who knows where you live."
    John F. Woods
    mon cv et mon domaine et mon blog
    Aucune question technique par MP, svp

Discussions similaires

  1. rafraichissement ProgressBar thread
    Par greg13 dans le forum GTK+ avec C & C++
    Réponses: 2
    Dernier message: 29/01/2008, 18h08
  2. threads & rafraichissement d'affichage
    Par Pancake dans le forum EDT/SwingWorker
    Réponses: 3
    Dernier message: 18/01/2008, 14h51
  3. [Tkinter] Thread pas logique!
    Par airod dans le forum Tkinter
    Réponses: 4
    Dernier message: 26/11/2006, 22h36
  4. [MFC] CFormView, Thread et rafraichissement
    Par Philippe299 dans le forum MFC
    Réponses: 6
    Dernier message: 08/09/2005, 15h18
  5. [thread] Rafraichissement JLabel
    Par astyanax34 dans le forum Composants
    Réponses: 14
    Dernier message: 24/06/2004, 15h44

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