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 :

Accès à des StringVar par threads


Sujet :

Tkinter Python

  1. #1
    Membre Expert
    Profil pro
    Développeur en systèmes embarqués retraité
    Inscrit en
    Mars 2006
    Messages
    952
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2006
    Messages : 952
    Par défaut Accès à des StringVar par threads
    Salut,

    j'ai une appli qui s'arrête aléatoirement avec le message "Python a cessé de fonctionner", je soupçonne un problème de thread.

    A la base, l'appli créée une liste de StringVar. Elle lit un gateway can/usb et récupère les valeurs pour les mettre dans les variables associées grâce à un thread. Comme il s'agit de variables Tk, les valeurs sont automatiquement affichées dans des widgets dédiés. Un autre thread lit cycliquement les variables pour les stocker dans un fichier csv. Un dernier thread vérifie cycliquement que les variables sont régulièrement mise à jour, dans le cas contraire une exception "Capteur perdu" est déclenchée.

    j'ai encapsulé les StringVar dans la classe ci-dessous pour éviter les problèmes d'accès simultané, mais rien à faire le problème persiste. Si quelqu'un a une idée...

    A+

    Pfeuh

    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
    import Tkinter as tk
    import threading
     
    class MONITOR_STRINGVAR(tk.StringVar):
        def __init__(self, *arg, **args):
            self.lock = threading.Lock()
            tk.StringVar.__init__(self, *arg, **args)
     
        def set(self, *args, **kwds):
            self.lock.acquire()
            retvar =  tk.StringVar.set(self, *args, **kwds)
            self.lock.release()
            return retvar
     
        def get(self, *args, **kwds):
            self.lock.acquire()
            # do a real copy, not a pointer or a reference
            retvar =  ''.join(tk.StringVar.get(self, *args, **kwds))
            self.lock.release()
            return retvar

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

    Une variable[=V] Tk est un "objet" de l’interpréteur TCL qui a été démarré dans un thread X. Mettre un verrou pour sérialiser les accès a V fait sens si tous les "utilisateurs" se plient a acquire/release.
    Ici, la relation entre ce "V" et d’éventuels widgets est réalisée par l’interpréteur TCL - qui fait ce qu'il veut.

    => il serait plus "safe" de faire faire les update des "Variable" via l'insertion d'un callback dans l'event loop (.after).

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

  3. #3
    Membre Expert
    Profil pro
    Développeur en systèmes embarqués retraité
    Inscrit en
    Mars 2006
    Messages
    952
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2006
    Messages : 952
    Par défaut
    Merci Wiztricks, je m'en doutais, tu as confirmé. Du coup, j'ai mis ma classe à jour comme ceci:

    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
    class MONITOR_STRINGVAR():
        def __init__(self, widget=None, value=""):
            self.lock = threading.Lock()
            self.widget = widget
            self.value_str = value
     
        def set(self, value, *args, **kwds):
            self.lock.acquire()
            self.value_str = value
            self.lock.release()
            if self.widget != None:
                try:
                    self.widget['text'] = self.value_str
                except:
                    pass # object can be already destroyed
            return self.value_str
     
        def get(self, *args, **kwds):
            self.lock.acquire()
            retvar = ''.join(self.value_str)
            self.lock.release()
            return retvar

  4. #4
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 670
    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 670
    Par défaut
    Salut,
    Tu as vire les Variable pour mettre a jour l'attribut "text" du /widget/ dans le thread courant (pas nécessairement) le thread de l’interpréteur TCL.
    Ca fonctionne parce que _tkinter (la librairie C) gère a peu prés le multithreading: l’opération TK sera "postée" dans le bon thread.
    Le soucis est dans le "a peu près": l'appel de certaines méthodes "plante".
    Il serait plus "sage" d'adapter le proxy développé ici
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  5. #5
    Membre Expert
    Profil pro
    Développeur en systèmes embarqués retraité
    Inscrit en
    Mars 2006
    Messages
    952
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2006
    Messages : 952
    Par défaut
    Salut,

    Je n'ai pas tout compris, mais j'ai quand même compris que changer l'attribut "text" d'un widget est plus stable que d'utiliser une "textvariable"... Mais n'est pas stable à 100%, c'est en tout cas ce que j'ai constaté. Est ce que mettre à jour widget['text'] par sa méthode widget.after() garantirait que le bon thread serait utilisé?

    A+

    Pfeuh

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 670
    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 670
    Par défaut
    Citation Envoyé par pfeuh Voir le message
    Est ce que mettre à jour widget['text'] par sa méthode widget.after() garantirait que le bon thread serait utilisé?
    Oui.

    Mais:
    1. .after ou .after_idle sont des commandes TCL que tkinter rend accessible via des méthodes associées au widget.
    2. Une Variable apporte d'autres fonctionnalités, dommage de s'en passer: w.after_idle(var.set, 123) devrait fonctionner.
    3. Le scénario:
    Comme il s'agit de variables Tk, les valeurs sont automatiquement affichées dans des widgets dédiés. Un autre thread lit cycliquement les variables pour les stocker dans un fichier csv. Un dernier thread vérifie cycliquement que les variables sont régulièrement mise à jour, dans le cas contraire une exception "Capteur perdu" est déclenchée.
    ne fonctionnera pas pareil avec .after (passage de message) et acquire/release (synchrone).
    - W
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

  7. #7
    Membre Expert
    Profil pro
    Développeur en systèmes embarqués retraité
    Inscrit en
    Mars 2006
    Messages
    952
    Détails du profil
    Informations personnelles :
    Localisation : France, Bas Rhin (Alsace)

    Informations professionnelles :
    Activité : Développeur en systèmes embarqués retraité
    Secteur : Industrie

    Informations forums :
    Inscription : Mars 2006
    Messages : 952
    Par défaut
    J'ai du mal à te suivre. Si j'ai bien compris, tous les problèmes de threads sont réglés par acquire/release. Reste le problème de tkinter que j'ai résolu comme ceci pour l'instant:

    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
    class MONITOR_STRINGVAR():
        def __init__(self, widget=None, value=""):
            self.lock = threading.Lock()
            self.widget = widget
            self.value_str = value
            self.display_str = value
     
        def set(self, value, *args, **kwds):
            self.lock.acquire()
            self.value_str = value
            self.display_str = ''.join(value)
            self.lock.release()
            if self.widget != None:
                try:
                    self.widget['text'] = self.display_str
                except:
                    pass # object can be already destroyed
     
        def get(self, *args, **kwds):
            self.lock.acquire()
            retvar = ''.join(self.value_str)
            self.lock.release()
            return retvar
    Et tu me dis que je pourrais quand même utiliser des StringVar avec quelque chose du style:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
     
    import Tkinter as tk
     
    class MONITOR_STRINGVAR(tk.StringVar):
        def __init__(self, *args, **kwds):
            tkStringVar.__init__(self, *args, **kwds)
     
        def set(self, *args, **kwds):
             w.after_idle(self, tk.StringVar.set, 123)

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

Discussions similaires

  1. Réponses: 0
    Dernier message: 21/09/2011, 16h07
  2. Réponses: 1
    Dernier message: 03/07/2007, 18h23
  3. Acces controles c# par des threads
    Par voyageur dans le forum Windows Forms
    Réponses: 2
    Dernier message: 05/03/2007, 19h04
  4. accès à des fichiers par une japplet
    Par gabest dans le forum Applets
    Réponses: 4
    Dernier message: 27/05/2006, 17h39

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