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 :

Affichage ligne par ligne en temps réel d'un traitement long en console dans un processus [Python 3.X]


Sujet :

Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    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 Affichage ligne par ligne en temps réel d'un traitement long en console dans un processus
    Bonjour,

    J'aimerais automatiser avec un programme Python un traitement long qui se fait d'habitude en console. Il s'agit en fait d'une conversion de format video avec l'excellent ffmpeg, dont l'exécution peut demander plus de 10 minutes.

    J'essaie alors de lancer le programme et ses arguments dans un processus (avec subprocess.Popen), mais je dois attendre que le processus soit terminé pour avoir les lignes d'affichage, alors que je voudrais l'affichage progressif pendant le traitement. comme je les ai dans la console.

    J'ai cherché sur le web pendant des heures, et aucun exemple trouvé n'a permis cela: j'ai donc la console avec un écran noir pendant toute la durée du traitement.

    Voilà un petit exemple de code qui affiche l'aide de python (affichage rapide ici) avec Windows 10 et Python 3.4:

    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, os
    import subprocess
     
    console = 'cmd /k'
     
    with subprocess.Popen(console, 
                          stdin=subprocess.PIPE,
                          stdout=subprocess.PIPE, 
                          stderr=subprocess.STDOUT, 
                          shell=True,
                          bufsize=10000) as proc:
     
        commande = br'E:\Python34\python.exe -h'
        lignes = str(proc.communicate(commande + b'\n')[0], 'cp850').splitlines()
        for ligne in lignes:
            print(ligne)
     
        x = input("?") # pour garder la console ouverte en fin du traitement
    Quelqu'un aurait-il une idée?

  2. #2
    Expert confirmé

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

    Informations forums :
    Inscription : Octobre 2008
    Messages : 4 307
    Par défaut
    Salut,

    Pour traiter les retours de commande ligne par ligne je procède comme ceci:

    (Je copie tel quel)
    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
     
                self.proc = subprocess.Popen(["gphoto2", "--get-all-files"], 
                                            universal_newlines=True, 
                                            stdout=subprocess.PIPE)
     
                while 1:
                    text = self.proc.stdout.readline()[:-1]
                    if type(text) != str or text == '' and self.proc.poll() != None: 
                        break
     
                    elif type(text) == str and len(text) > 6:
                        self.fileDownloaded.emit(text)
     
                self.proc = None
                self.downloadingFinished.emit(r)
    Donc, ici, chaque étape du processus est contenue dans text moi je l'affiche dans une interface mais tu peux simplement le printer.

  3. #3
    Membre éclairé
    Homme Profil pro
    Développeur informatique
    Inscrit en
    Janvier 2010
    Messages
    553
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 45
    Localisation : France

    Informations professionnelles :
    Activité : Développeur informatique
    Secteur : Industrie

    Informations forums :
    Inscription : Janvier 2010
    Messages : 553
    Par défaut
    Salut,

    une solution qui pourrait te convenir, c'est de faire en sorte que ton processus ouvert avec Popen écrive directement sur la sortie standard plutôt que dans un pipe. ainsi, tu lances juste tes commandes et leurs sorties arrivent toutes seules sur la console sans traitement spécifique.

    exemple testé en python 2.7, mais ça doit être transposable sans problème en python 3.4:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    import sys
    import subprocess
    
    console = 'cmd /k'
    proc = subprocess.Popen(console, stdin=subprocess.PIPE,
                            stdout=sys.stdout, stderr=sys.stderr,
                            shell=True, bufsize=10000)
    
    commande = r'C:\Python27\python.exe stub.py'
    proc.communicate(commande + '\n')
    le fichier stup.py que j'utilise sert juste à m'afficher des lignes avec un petit délai entre chaque affichage, pour être plus proche de ton besoin:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    import time
     
    for percent in xrange(101):
        print("avancement: {}%".format(percent))
        time.sleep(0.1)
    au final, mon test m'affiche bien les lignes en temps réel plutôt que d'attendre la fin de la commande.

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

    "python -u" écrira dans le pipe en unbuffered.

    - edit -
    Cependant, si la question est:
    Citation Envoyé par tyrtamos Voir le message
    J'aimerais automatiser avec un programme Python un traitement long qui se fait d'habitude en console. Il s'agit en fait d'une conversion de format video avec l'excellent ffmpeg, dont l'exécution peut demander plus de 10 minutes.

    J'essaie alors de lancer le programme et ses arguments dans un processus (avec subprocess.Popen), mais je dois attendre que le processus soit terminé pour avoir les lignes d'affichage, alors que je voudrais l'affichage progressif pendant le traitement. comme je les ai dans la console.
    ben, on ne sait pas passer ffmpeg en mode unbuffered (comme on peut le faire pour Python ou d'autres commandes).
    Sur unix on pourrait utiliser pexpect.spawn. Ca remplace le PIPE par un pseudo terminal (pty) mais sur Windows cette feature n'existant pas en standard...

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

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

    Merci pour les réponses! J'ai finalement réussi avec stdout=sys.stdout (merci Tryph!), ce qui donne un code particulièrement simple:

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    with subprocess.Popen(commande,
                          stdout=sys.stdout,
                          stderr=sys.stdout) as proc:
        pass
    Et c'est tout!

    La commande est ici la chaine de caractères qui lancerait le programme souhaité avec ses options. Comme il s'agit d'un exécutable, on laisse l'option par défaut shell=False (shell=True n'est nécessaire que quand on veut lancer une commande shell comme "dir *.*").

    Quand on lance le programme, il vient la fenêtre de la console à l'écran, et toutes les lignes s'affichent au fur et à mesure du traitement comme souhaité. En plus, les messages d'alertes et d'erreurs s'affichent avec leur couleur comme si on avait lancé directement en console. En fait, c'est identique au lancement en console, à part qu'on peut enchainer les traitements grâce au pilotage par le code Python.

    Comme mon problème était de lancer la conversion de plusieurs fichiers vidéo ".avi" en "mp4", voilà comment je fais:

    - je trouve la liste des fichiers vidéos ".avi" à convertir dans un répertoire avec glob.glob.

    - pour chaque nom de fichier avi trouvé (=> srce), je fabrique le même nom de fichier avec l'extension .mp4 (=> dest).

    voilà le code pour la suite à placer dans la boucle des fichiers à convertir (le logiciel ffmpeg est trouvé ici: https://www.ffmpeg.org/):

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    listecde = ['ffmpeg.exe', # suppose que c'est dans le PATH. Sinon: ajouter l'adresse absolue
                        '-nostdin',  # option pour empêcher les questions pendant le traitement
                        '-y', # option pour permettre de remplacer un fichier existant sans demander
                        '-i',  # précède l'adresse du fichier à convertir
                        '"' + srce + '"', # fichier à convertir (les guillemets permettent les espaces)
                        '"' + dest + '"' # nouveau fichier à écrire (les guillemets permettent les espaces)
                        ]
    commande = " ".join(listecde)
     
    with subprocess.Popen(commande,
                          stdout=sys.stdout,
                          stderr=sys.stdout) as proc:
        print("Conversion du fichier: %s\n" % (os.path.split(srce)[1],))
    Avec ça, on lance le programme le soir, et le matin, tout est fini! Pendant la journée, on peut vérifier à tous moments que le traitement continue grâce aux lignes qui s'affichent, on peut voir les messages d'erreurs éventuels, etc...

    J'aurais bien aimé avoir un plus grand contrôle sur l'affichage pour afficher les lignes dans une fenêtre PyQt mais je n'ai pas réussi. Dans un autre programme, j'avais déjà utilisé StringIO comme "file-like" pour détourner le sys.stdout", mais là ça ne marche pas, parce que subprocess utilise pour afficher la méthode fileno() qui n'existe pas avec StringIO.

    Je n'ai pas trouvé une autre solution que "stdout=sys.stdout" qui marche. Dès que l'on utilise les PIPE, on a seulement la totalité des lignes à la fin du traitement. Même d'ailleurs si on lance le code Python avec "python -u".

    Problème résolu!

  6. #6
    Expert éminent
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 752
    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 752
    Par défaut
    Citation Envoyé par tyrtamos Voir le message
    Je n'ai pas trouvé une autre solution que "stdout=sys.stdout" qui marche. Dès que l'on utilise les PIPE, on a seulement la totalité des lignes à la fin du traitement. Même d'ailleurs si on lance le code Python avec "python -u".
    Il faut quand même préciser que tous ces tracas sont dus aux PIPE de Windows.
    Sinon "python -u" fonctionne même sous Windows mais ce n'est pas un script Python qui est lancé mais ffmpeg (qui n'a pas d'équivalent pour "-u").

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

  7. #7
    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 wiztricks et merci,

    Concernant le détournement de sys.stdout, connais-tu une astuce qui permettrait d'éviter l'erreur concernant fileno() générée par subprocess? J'ai essayé plusieurs solutions avec des classes mais je n'ai rien trouvé qui fonctionne. Bien sûr, ça marche avec un "vrai" fichier disque, mais pas pour l'affichage.

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

Discussions similaires

  1. Réponses: 4
    Dernier message: 11/06/2009, 09h57
  2. Réponses: 11
    Dernier message: 11/05/2009, 20h29
  3. affichage ligne par ligne du fichier ascII
    Par khayate dans le forum VB.NET
    Réponses: 15
    Dernier message: 06/06/2007, 14h14
  4. [ Problème d'affichage de données ligne par ligne ]
    Par Arkoze dans le forum VB 6 et antérieur
    Réponses: 6
    Dernier message: 05/06/2007, 09h45
  5. Probleme affichage avec un GtkTextView (affichage ligne par ligne)
    Par Marmoccelle dans le forum GTK+ avec C & C++
    Réponses: 9
    Dernier message: 22/03/2007, 14h42

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