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 :

Récuperer stdout dans un script.


Sujet :

Python

  1. #1
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 304
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 304
    Par défaut Récuperer stdout dans un script.
    Bonjour,

    Est-il possible et par quel moyen de récupérer les retours d'une ligne de commande lancée par un script python dans ce même script.

    Je m'explique, j'utilise gPhoto2 qui est un front-end de la librairie
    libgphoto2 et qui s'utilise en ligne de commande.

    Dans mon programme python j'ai, donc, ceci

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
     
    reply = subprocess.Popen(["gphoto2", "--get-all-files"],
                                    universal_newlines=True, 
                                    stdout=subprocess.PIPE).communicate()
    Ici, reply me retourne diverses infos mais seulement lorsque l'opération est entièrement terminée, alors que la console, elle, m'affiche l'avancement des
    actions.
    Dans les cas où l'execution est rapide (i.e. lister le contenu d'un appareil photo) cela ne dérange pas mais pour des actions longues (i.e. downloader plus de dix fichiers), j'aimerais pouvoir afficher l'avancement du transfert.

    Merci.
    vincent

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

    soit les deux scripts suivants:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
     
    #!/usr/bin/env python
     
    import time
     
    t = 0
    while t <= 10:
         print t
         time.sleep(2)
         t += 2
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    #!/usr/bin/env python
     
    import subprocess
     
    s = subprocess.Popen(args=['test02.py'])
    s.wait()
    Il me semble que ce que tu veux faire est la façon "standard" de faire avec Popen.

  3. #3
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 304
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 304
    Par défaut
    Oui, bien sur mais je suis toujours tenu à la console.

    Mon script python tourne dans une interface graphique.

    C'est du Qt et j'ai donc, un QPlainTextEdit dans lequel j'aimerais afficher
    l'avance du processus en cours, pour cela il me faudrait un moyen de récupérer les messages de la console.
    Etant en développement, je lance toujours mon apply avec une console,
    mais ce n'est pas fait pour "rester durer".

    La docu dit ceci:
    stdin, stdout ... Valid values are PIPE, ... an existing file object, ...

    Puis-je comprendre que je peux rediriger stdout vers un fichier texte et surveiller dans un thread les nouvelles lignes qui y apparaîtraient,
    ça me paraît du gros bricolage, je l'avoue.

    Cela dit, gphoto2 permet d'utiliser un script "hook" pour surveiller ses tâches, j'ai un exemple en bash, je vais voir si je peux trouver une solution de ce côté.

    merci
    v.

  4. #4
    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
    ok, je comprends mieux

    il "suffit" de créer un objet qui adapte l'interface d'un fichier et de l'objet pyQT que tu manipules.

    malheureusement subprocess passe par les fileno plutôt que par la méthode write. L'adaptation est donc pas immédiate et je ne connais pas trop.

  5. #5
    Membre émérite
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Par défaut
    tu peux te créer vite fait une classe avec un méthode write, et rediriger stdout vers une instance de cette classe :

    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
    import sys
     
    class STDOUT(object):
        def __init__(s, bind):
            '''bind <-- QPlainTextEdit object'''
            s.bind = bind
        def write(s, text):
            s.bind.operationToWrite(text) #à adapter évidemment à la méthode d'écriture du QPlaintTextEdit...
     
    #dérivation de sys.stdout vers une instance de la classe précédement créee
    MEM, sys.stdout= sys.stdout, STDOUT()
     
    ...
    programme
    ...
     
    #remise à l'état d'origine de sys.stdout
    sys.stdout = MEM

  6. #6
    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,

    j'ai essayé N.tox mais malheureusement cette approche ne fonctionne pas. comme je le dis dans mon message subprocess ne passe pas par la méthode write de l'objet qu'on lui donne en stdout. il utilise la méthode fileno pour récupérer le numéro logique du fichier puis fait un os.write sur ce numéro.

    du coup si on passe ton objet en stdout, tout va s'écrire à l'écran et la méthode write ne sera jamais appelée

  7. #7
    Membre émérite
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Par défaut
    j'ai jamais parlé de subprocess... (ceci dit c'est de la mauvaise foi , en vérité j'ai surtout mal lu )

    Alors, au vu de ce que tu me dit, on pourrait utiliser la m^me technique de dérivation temporaire en "remplaçant" la fonction os.write, Non ?

    hmmm... après lecture de la doc de subprocess, j'ai repéré ceci
    Popen.stdout¶
    If the stdout argument was PIPE, this attribute is a file object that provides output from the child process. Otherwise, it is None.
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    subprocess.Popen(["gphoto2", "--get-all-files"],
                                    universal_newlines=True, 
                                    stdout=subprocess.PIPE).stdout

    dès lors, n'est ce pas avec ça qu'il faut joué ? (vu comment c'est compliqué, les anciennes fonctions ont du bon tout de même ...)
    Peut-on lui affecter un autre file object ?

  8. #8
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 304
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 304
    Par défaut
    Merci N.tox, on va vers une solution plus propre là.

    Maintenant, réimplémenter ceci dans mon code n'est pas sans douleur.

    J'ai procéder à divers essais pour arriver à 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
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
     
    class IOcamera(object):
     
        def __init__(self):
            #some arguments
     
        def get_images(self, rep, bind, r=None):
            """Download files from current camera.
     
            keyword arguments:
            rep -- folder to download files.
            bind -- QPlainTextEdit object
            r -- range of files 
            """ 
            old_rep = os.getcwd()
            os.chdir(rep)
            MEM, sys.stdout = sys.stdout, STDOUT(bind)
            if r:
                subprocess.Popen(["gphoto2", "--get-file", r], universal_newlines=True,
                                    stdout=sys.stdout).communicate()
            else:
                subprocess.Popen(["gphoto2", "--get-all-files"], universal_newlines=True, 
                                    stdout=sys.stdout).communicate()
            os.chdir(old_rep)
            sys.stdout = MEM
            return "Done"
     
    class STDOUT(object):
     
        def __init__(self, bind):
            self.bind = bind
     
        def fileno(self):
            print "fileno"
            pass
     
        def write(self, text):
            text = text + "file"
            self.bind.appendPlainText(text)
            QtCore.QCoreApplication.processEvents()
    J'ai rajouté def fileno(), sinon j'avais une erreur

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
     
      File "/usr/lib/python2.6/subprocess.py", line 588, in __init__
        errread, errwrite) = self._get_handles(stdin, stdout, stderr)
      File "/usr/lib/python2.6/subprocess.py", line 945, in _get_handles
        c2pwrite = stdout.fileno()
    AttributeError: 'STDOUT' object has no attribute 'fileno'
    les messages s'affichent toujours dans la console, j'ai ensuite rajouté 'print "fileno"' à la fonction fileno et text = text + "file" à la fonction write.
    Si je lance le download de quatre fichiers, par exemple, 'filenofile' s'affiche dans mon QPlainText ensuite deux lignes vides et encore une fois 'file'
    tout ceci avant que le premier chargement ne démarre et mon curseur qui est normalement "busy" jusqu'à la fin du process redevient normal immédiatement aussi.

    Kango avait parlé de fileno ...

    vincent

  9. #9
    Membre émérite
    Homme Profil pro
    heu...
    Inscrit en
    Octobre 2007
    Messages
    648
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Essonne (Île de France)

    Informations professionnelles :
    Activité : heu...

    Informations forums :
    Inscription : Octobre 2007
    Messages : 648
    Par défaut
    J'y aurais pas pensé

    Merci Kango également, je suis content que nos remarques t'ai fournies l'idée

    Merci pour le post de la soluce, ça pourra être utile à l'avenir

    EDIT : je me permet de te proposer une légère modification de ton code pour éviter les lignes vide (en théorie...):
    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
    class IOcamera(object):
     
        def __init__(self):
            #some arguments
     
        def get_images(self, rep, bind, r=None):
            """Download files from current camera.
     
            keyword arguments:
            rep -- folder to download files.
            bind -- QPlainTextEdit object
            r -- range of files 
            """ 
            old_rep = os.getcwd()
            os.chdir(rep)
            MEM, sys.stdout = sys.stdout, STDOUT(bind)
            if r:
                subprocess.Popen(["gphoto2", "--get-file", r], universal_newlines=True,
                                    stdout=sys.stdout).communicate()
            else:
                subprocess.Popen(["gphoto2", "--get-all-files"], universal_newlines=True, 
                                    stdout=sys.stdout).communicate()
            os.chdir(old_rep)
            sys.stdout = MEM
            return "Done"
     
    class STDOUT(object):
     
        def __init__(self, bind):
            self.bind = bind
     
        def fileno(self): pass
     
        def write(self, text):
            if text.strip():
                self.bind.appendPlainText(text)
                QtCore.QCoreApplication.processEvents()

  10. #10
    Expert confirmé

    Homme Profil pro
    Inscrit en
    Octobre 2008
    Messages
    4 304
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : Belgique

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 304
    Par défaut
    Cette fois ci, c'est bon.

    En fait la solution était plus simple encore:

    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
     
            print "Download images ...", r
            old_rep = os.getcwd()
            os.chdir(rep)
            if r:
                reply = subprocess.Popen(["gphoto2", "--get-file", r],    
                                                   universal_newlines=True,
                                                   stdout=subprocess.PIPE)
                while 1:
                    text = reply.stdout.readline()
                    if text == '' and reply.poll() != None: 
                        break
                    if len(text):
                        bind.appendPlainText(text)[:-2]
                        QtCore.QCoreApplication.processEvents()
    Ici, r = la liste de photos à downloader
    Il fallait supprimer .communicate() qui attend que le process soit terminé avant de transmettre.
    Les deux dernières lignes sont spécifiques à Qt, il faut enlever les deux caractères finaux de la chaîne qui sont "\n" et qui engendraient à chaque fois une ligne vide et QtCore.QCoreApplication.processEvents() sert a forcer l'update du plainTextEdit sinon Qt attend d'être revenu dans la boucle principale du programme pour le faire.

    J'ai trouvé des trucs intéressants ici:

    http://www.doughellmann.com/PyMOTW/subprocess/

    Je met en résolu et encore merci.

    vincent

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

Discussions similaires

  1. Réponses: 0
    Dernier message: 26/08/2011, 17h06
  2. Réponses: 2
    Dernier message: 04/12/2009, 16h27
  3. redéfinir dans un script ses stdout/stderr
    Par _Remy_ dans le forum Applications et environnements graphiques
    Réponses: 2
    Dernier message: 09/10/2008, 23h07
  4. Requête POST dans un script bash
    Par desperado dans le forum Linux
    Réponses: 4
    Dernier message: 11/12/2007, 22h38
  5. Boucle for dans un script cmd
    Par nicolas.ganache dans le forum Développement
    Réponses: 4
    Dernier message: 19/07/2004, 16h07

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