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

Python Discussion :

IHM, Logs et Threads


Sujet :

Python

  1. #1
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 28
    Par défaut IHM, Logs et Threads
    Bonjour tout le monde!

    Je vais tenter de faire simple pour exposer mon problème.
    Je travaille sur un outil python déjà existant sur mon projet.
    Cet outil est un script auquel on passe des options (-a, -d, etc etc..) et qui examine plein de fichiers et crée des fichiers de sortie.
    Bref jusque là, tout va bien.

    Je dois créer une interface graphique qui sera appelée lorsque la ligne de commande contiendra l'option -g.

    Cette interface graphique est alors affichée et est sensée réceptionner les logs qui étaient jusque là affiché dans la console.

    Voici donc le cheminement que j'ai adopté :

    Dans le script actuel, si l'option -g est présente dans la ligne de commande, je fais afficher mon IHM juste après avoir lancé le processus d'analyse des fichiers dans un thread.

    Question (enfin...lol) : comment faire pour que les logs soit désormais affichés en temps réel dans mon IHM et non plus dans la console?

    Merci d'avance pour votre aide

  2. #2
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    J’imagine que tes logs sont produits par de simples print ?

    Dans ce cas, il faut que tu rediriges stdout et/ou stderr (sys.stdout et sys.stderr ) vers des io stream quelconques, que tu liras ensuite :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import sys, io
    stream = sys.stdout = io.StringIO()
     
    # Lire les logs avec stream.readline()…
    # Attention, plus aucun print ne s’affichera dans la console, à moins d’écrire dans
    # sys.__stdout__
     
    # Pour restaurer le comportement par défaut…
    sys.stdout = sys.__stdout__

  3. #3
    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
    bonjour,

    et dans le cas ou tu utilises le module logging, tu peux passer par la création d'un Handler spécifique:

    http://docs.python.org/library/loggi...andler-objects

  4. #4
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 28
    Par défaut
    Ah merci pour vos réponses!
    Alors oui effectivement, actuellement c'est la fonction "print" qui est utilisée un peu partout dans le code.

    Cependant, si je fais :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import sys, io
    stream = sys.stdout = io.StringIO()
     
    # Lire les logs avec stream.readline()…
    # Attention, plus aucun print ne s’affichera dans la console, à moins d’écrire dans
    # sys.__stdout__
     
    # Pour restaurer le comportement par défaut…
    sys.stdout = sys.__stdout__
    Lorsqu'ensuite je fais print "test", il me sort une erreur du style:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    >>> print "test"
    Traceback (most recent call last):
      File "<stdin>", line 1, in <module>
      File "/usr/lib/python2.6/io.py", line 1500, in write
        s.__class__.__name__)
    TypeError: can't write str to text stream
    Du coup, comment dois-je faire?
    Et faut il que la variable "stream" (dans l'exemple) soit une variable globale dont je pourrai avoir accès dans tous les modules de mon projet?
    Et est-ce cette variable qui contiendra mes logs?

    Que de questions....

    Merci d'avance

  5. #5
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Ton problème provient du fait qu’en python 2.x, il y a distinction entre chaînes “standards” et chaînes unicode…

    Deux solutions : soit tu t’assures que tous tes print utilisent des chaînes unicode (forme u""), soit tu change l’encodage au moment de la création du StringIO…

    Pour info, l’encodage par défaut d’un stringIO, c’est utf-8

  6. #6
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 28
    Par défaut
    merci pour la réponse.
    Mais ça ne répond qu'à une partie de ma question, c'est à dire l'erreur générée...
    Pour ce qui est de mon histoire de pouvoir récupérer la valeur du log depuis n'importe quel module (variable "stream"), qu'en est-il s'il vous plait?

  7. #7
    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
    Citation Envoyé par syrius31 Voir le message
    Et faut il que la variable "stream" (dans l'exemple) soit une variable globale dont je pourrai avoir accès dans tous les modules de mon projet?
    Et est-ce cette variable qui contiendra mes logs?
    oui et oui

    si cette variable sert à du logging, je te suggère de jeter un coup d'oeil au module logging Python, qui, même s'il est peu intuitif au tout début, propose une souplesse et une facilité d'utilisation très agréables

  8. #8
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Citation Envoyé par kango Voir le message
    oui et oui
    Oui et non (ou plutôt, non et oui)

    En fait, puisque tu l’assignes à sys.stdout, il te suffit d’importer sys pour pouvoir la récupérer :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    import sys
    stream = sys.stdout

  9. #9
    Membre averti
    Profil pro
    Inscrit en
    Février 2006
    Messages
    28
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Février 2006
    Messages : 28
    Par défaut
    en fait, je suis en wxPython et j'aimerai ecrire tous les logs dans un wx.TextCtrl multiligne.
    je sais comment créer le composant en question, je sais comment l'ecrire (methode WriteText pour chaque ligne que je veux rajouter) mais là où je bloque, c'est quand je veux l'ecrire depuis d'autres modules.

    Au depart, je m'etais fait une méthode WriteLog(msg) et depuis les autres endroits du code, je faisais un truc du style <mon composant>.WriteLog(msg)

    Le souci, c'est que avec les Threads, une fois sur deux, vu qu'il y a beaucoup de log, ça me sort une erreur (qui n'est pas toujours la même) du style 'memory fault' ou un truc dans le genre....

    Je voudrais donc passer par le module logging mais je trouve pas d'exemples simples! Sachant que j'ai déjà mon IHM construite et tout.

    L'ideal serait de pouvoir rediriger ma sortie standard vers ma TextBox...

    Vous avez une idée?

  10. #10
    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
    as tu essayé quelque chose comme ça ?

    dans la classe qui gère le TextCtrl

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    def write(msg):
        self.textCtrl.AppendText(msg)
    et dans le __init__ de cette même classe:


  11. #11
    Membre Expert

    Homme Profil pro
    Diverses et multiples
    Inscrit en
    Mai 2008
    Messages
    662
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Diverses et multiples

    Informations forums :
    Inscription : Mai 2008
    Messages : 662
    Par défaut
    Et ne pas oublier un lock pour éviter que les threads se marchent sur les pieds quand ils logs

  12. #12
    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
    tout à fait !

    tes ça logs ressembler sinon à vont

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

    Un truc un peu sophistiqué dans mes bricos.

    Pour faire vendeur commençons par la fin, i.e:
    - le code qui permet de se rendre compte que çà fait ce qu'on veut,
    - et au plus c'est simple, au plus vite on saura qu'on voulait autre chose

    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
    import threading
    import logger
    from tk_application import TkApplication, LogView
    # l'application graphique...
    class MyApp(TkApplication):
        def __init__(self, title):
            TkApplication.__init__(self, title)
    # le point d'entree du thread qui va afficher des messages via logging
    # ils iront sur la console, dans un fichier et dans des TextBox...
    def threading_callable(count):
     
        import logging
        logger = logging.getLogger('')
        for x in range(count):
            logger.info('%d test line' % x)
            time.sleep(0.2)
     
    if __name__ == '__main__':
        import time
        from logger import LogHandler
     
        app = MyApp('logger')
        # creation d'une log view...
        logView = LogView(app)
        # le 'slot' a la Qt
        cb = getattr(logView, 'add_line')
        # le 'logHandler' qui expedie les messages de log
        lh = LogHandler()
        # ajout du 'slot'...
        lh.add_callback(cb)
        # et maintenant on demarre le thread...
        t1 = threading.Thread(target=threading_callable, args=(10,))
        t1.start()
     
        app.mainloop()
    Jusqu'ici j'espère que tout va bien...
    Nous avons crée une application graphique... avec une LogView qui recevra les messages de log émis par les threads.
    Note: Si on veut afficher du texte dans la logView, il suffit d'appeler le callback logView.add_line('texte...') mais on veut aussi hacker logging!!!

    Côté logging, c'est pas compliqué mais un peu bavard.
    A part l'initialisation du module, il contient "LogHandler" qui va permettre de rediriger les logs dans les text box ou autres qui se seront déclarées via .add_callback. C'est une construction "classique".

    file: logger.py
    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
    import logging
     
    logfile = 'test.log'
    ch_format = "[%(name)s] %(levelname)s - %(message)s"
    fh_format = "%(asctime)s [%(name)s] %(levelname)s, pid: %(process)d - %(message)s"
    # create logger
    logger = logging.getLogger('')
    logger.setLevel(logging.DEBUG)
     
    # create console handler and set level to debug
    ch = logging.StreamHandler()
    ch.setLevel(logging.DEBUG)
     
    # create formatter
    formatter = logging.Formatter(ch_format)
     
    # add formatter to ch
    ch.setFormatter(formatter)
     
    # add ch to logger
    logger.addHandler(ch)
     
    # create file handler which logs even debug messages
    fh = logging.FileHandler(logfile)
    fh.setLevel(logging.DEBUG)
    # create formatter and add it to the handlers
    formatter = logging.Formatter(fh_format)
    fh.setFormatter(formatter)
    # add the handlers to logger
    logger.addHandler(fh)
     
    class LogHandler(logging.Handler):
        '''a logging handler implementing observer pattern'''
        _format = "%(asctime)s [%(name)s] %(levelname)s %(message)s"
     
        def __init__(self):
    #        super(LogHandler, self).__init__()
            logging.Handler.__init__(self)
            self.setFormatter(logging.Formatter(self._format))
            logger = logging.getLogger('') # root logger
            logger.addHandler(self)
     
            self._callbacks = []
     
        def emit(self, record):
            self.notify((self.format(record),), {})
     
        def add_callback(self, method):
            self._callbacks.append(method)
     
        def notify(self, args, kwds):
            for cb in self._callbacks:
                cb(*args, **kwds)
     
     
    if __name__ == '__main__':
        logger.debug('foo')
    Reste la partie GUI... une application/MainWindow et une LogView qui va afficher les messages de log.
    En fait, logView affiche tout ce qu'on lui envoie via .add_line.

    Côté threading, c'est pas compliqué!
    Si .add_line est appelé depuis le thread courant et s'exécute, sinon l'appel est sérialisé via la mainloop du GUI.
    Note: c'est simple, car l'appelant n'attend pas le 'retour' ou la fin de l'appel
    à .add_line.

    file: tk_application.py
    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
    from Tkinter import *
    import sys
    import threading
    # c'est pas plus simple, juste mnemotechnique
    get_current_tid = lambda: threading.current_thread().ident
     
    class Command(object):
        '''wraps call into message'''
        def __init__(self, method, *args, **kwds):
            self._method = method
            self._args = args
            self._kwds = kwds
     
        def do(self):
            try:
                rs = self._method(*self._args, **self._kwds)
            except Exception as e:
                print 'exception ', e
                return
            return rs
     
     
    class LogView(Frame):
        _max_lines=10
        _remove_chunk = 5
     
        def __init__(self, master=None):
            Frame.__init__(self, master)
     
            self._scrollbar = Scrollbar(self)
            self._scrollbar.pack(side=RIGHT, fill=Y)
            self._text = Text(self)
            self._text.pack()
     
            self.pack()
            self._line_count = 0
            self._delete_args = ("1.0", "%d.0" % self._remove_chunk)
     
        def add_line(self, line):
            '''if called from a different thread, submit as a message'''
     
            if get_current_tid() != self.master._tid: #assume master is root!
                self.after_idle(Command(self.add_line, line).do)
                return
     
            self._line_count += 1
            self._text.insert(INSERT, line + '\n')
            if self._line_count > self._max_lines:
                self._text.delete(*self._delete_args)
                self._line_count -= (self._remove_chunk - 1)
     
    class TkApplication(Tk):
        _height = 200
        _width = 200
     
        def __init__(self, title):
            Tk.__init__(self)
            self.title(title)
            self.geometry('%dx%d' % (self._height, self._width))
            self.protocol("WM_DELETE_WINDOW", self.on_delete_window)
            self._quit_event = False
            self._tid = get_current_tid()
     
        def on_delete_window(self):
            self._quit_event = True
            sys.exit()
    Et voilà...
    - W
    PS: C'est un code que j'ai repris d'un développement. A la relecture, le nettoyage n'est pas si bien fait: il reste des trucs qui ne servent à rien côté exemple.
    Architectures post-modernes.
    Python sur DVP c'est aussi des FAQs, des cours et tutoriels

Discussions similaires

  1. Avis sur des Log Multi-thread
    Par jmjmjm dans le forum Windows Forms
    Réponses: 6
    Dernier message: 25/02/2010, 11h11
  2. Thread/Mise a jour d'IHM
    Par davids75014 dans le forum Interfaces Graphiques en Java
    Réponses: 7
    Dernier message: 04/06/2007, 14h44
  3. [VB.NET][VS2003] Threads et IHM
    Par toniolol dans le forum Windows Forms
    Réponses: 7
    Dernier message: 27/04/2006, 13h54
  4. [threading][logging] erreur de loggage dans un thread
    Par Guigui_ dans le forum Général Python
    Réponses: 5
    Dernier message: 12/10/2005, 15h01
  5. [SWT]mise a jour ihm SWT par un thread
    Par will82 dans le forum SWT/JFace
    Réponses: 1
    Dernier message: 06/08/2004, 11h37

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