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

PyQt Python Discussion :

Comment ne pas bloquer la GUI pendant le travail ?


Sujet :

PyQt Python

  1. #41
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    Pas de soucis voila l'idée vers laquelle je m'oriente :
    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
    def WorkStart(self):
        """Fonction appellée par celles ayant besoin de lancer du travail en fond."""
        Configs["CmdInProgress"] = Configs["CmdWorkInProgress"].split(" ")[0]
     
        if Configs["CmdInProgress"] == "mkvalidator": # Dans le cas de mkvalidator
            self.ui.progressBar.setMaximum(0) # Mode pulsation de la barre de progression
     
        ... # Modifications graphiques
     
        ... # Envoie d'informations
     
        self.process.start(Configs["CmdWorkInProgress"]) # Lancement du processus
     
     
    def travail(self):
        # Creation des variables des différentes commandes
        mkvextract_track = ""
        mkvextract_joint = ""
        mkvextract_chap = ""
        mkvextract_tag = ""
     
        ..... # En fonction de nombreux critères, les variables sont configurées ou non
     
        # Lancement des commandes les unes après les autres
        if mkvextract_track:
            Configs["CmdWorkInProgress"] = mkvextract_track
            self.WorkStart()
     
        # Pause, attend que le qprocess soit libre
     
        if mkvextract_tag:
            Configs["CmdWorkInProgress"] = mkvextract_tag
            self.WorkStart()
     
        # Pause, attend que le qprocess soit libre
     
        if mkvextract_chap:
            Configs["CmdWorkInProgress"] = mkvextract_chap
            self.WorkStart()
     
        # Pause, attend que le qprocess soit libre
     
        if mkvextract_joint:
            Configs["CmdWorkInProgress"] = mkvextract_joint
            self.WorkStart()
    Cette logique me semble être la plus simple mais je peux modifier sans trop de soucis le fonctionnement si je vais pas dans la bonne direction.

  2. #42
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 334
    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 334
    Points : 36 855
    Points
    36 855
    Par défaut
    Un seul QProcess a qui on appliquerait plusieurs .start a la suite sans attendre que la commande en cours se termine plantera.
    Et si on n'attend pas, pas de raison que le GUI "freeze".

    Avec plusieurs QProcess, sans attente, pas de GUI "freeze".

    Pas facile de comprendre la cohérence de tout çà
    Pourriez vous clarifier (un peu)?

    A moins que le commentaire:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    # Pause, attend que le qprocess soit libre
    fasse simplement ce qu'il dit.
    Auquel cas ca expliquerait le "freeze"

    - W

  3. #43
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    Je cherche un moyen d’exécuter plusieurs commandes qui attendraient à chaque fois que la commande précédente ait terminée pour se lancer.

    Je trouve plus simple de passer par un seul process (inutile d'en avoir plusieurs si ils ne sont pas lancés en même temps, non ?).

    Je ne veux pas les lancer en même temps car je dois surveiller leurs retours pour afficher la progression (en autre).

    J'ai testé pas mal de truc mais je n'ai pas réussis à y parvenir.

    car quelque soit mon approche, je bloque ma GUI en attendant que la commande précédente soit terminée...

    Je ne sais pas si je suis clair...

  4. #44
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 334
    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 334
    Points : 36 855
    Points
    36 855
    Par défaut
    Pour le faire marcher ce truc, il faudrait que le GUI attende (en faisant autre chose) d'avoir recu le SIGNAL "finished(int)" emit par QProcess.
    Si la méthode qui attrape ca se charge de lancer le .start suivant en fonction du reste a faire. Personne n'attend et voila.

    Est ce clair? ou faut-il que je raconte ca avec du code?
    (demain)

    - W

  5. #45
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    J'ai surement trouvé une solution
    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
    def WorkStart(self):
        """Fonction appellée par celles ayant besoin de lancer du travail en fond."""
        Configs["CmdWorkInProgress"] = Configs["CmdList"][0]
        del Configs["CmdList"][0]
     
        ... # Modifications graphiques
     
        ... # Envoie d'informations
     
        self.process.start(Configs["CmdWorkInProgress"]) # Lancement du processus
     
     
    def finished(self):
        if Configs["CmdList"]: # S'il reste des commandes on lance la suite du travail
            self.WorkStart()
     
     
    def travail(self):
        # Creation des variables des différentes commandes
        mkvextract_track = ""
        mkvextract_joint = ""
        Configs["CmdList"] = []
     
        ..... # En fonction de nombreux critères, les variables sont configurées ou non
     
        # Lancement des commandes les unes après les autres
        if mkvextract_track:
            Configs["CmdList"].append(mkvextract_track)
     
        if mkvextract_joint:
            Configs["CmdList"].append(mkvextract_joint)
     
        self.WorkStart()
    Dis moi si ca te parait assez propre.

    Merci !


    EDIT : Il semble que j'ai eu la meme idée vis a vis du message ci-dessus (si j'ai bien compris)

    EDIT2 : j'ai fait quelques corrections du code (collé trop vite)

    EDIT3 : Petite question annexe : Est-il possible d’exécuter une commande indiquant un fichier de redirection ? Ex basique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    self.process.start(echo "ceci est un test" > /home/hizoka/test)

  6. #46
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 334
    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 334
    Points : 36 855
    Points
    36 855
    Par défaut
    Citation Envoyé par hizoka Voir le message
    Dis moi si ca te parait assez propre.
    Je ferais un exemple minimal pour que d'autres puissent en profiter.
    Mais pas maintenant.

    EDIT3 : Petite question annexe : Est-il possible d’exécuter une commande indiquant un fichier de redirection ? Ex basique :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    self.process.start(echo "ceci est un test" > /home/hizoka/test)
    Ce genre de construction n'a de sens que pour un shell.
    QProcess n'execute qu'un process... qui pourrait etre shell suivi d'un string. Subtil?
    - W

  7. #47
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 334
    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 334
    Points : 36 855
    Points
    36 855
    Par défaut
    Re...

    Si on dit "en francais", je veux une sorte de QProcess qui lance les commandes qu'on lui envoie les unes après les autres...

    Difficile de ne pas traduire cela par une s/classe de QProcess dans lequel on surcharge la méthode .start pour qu'elle "empile" si c'est "busy". Puis lorsque la commande en cours se termine, on ajoute un peu de logique pour traiter celles en attente.

    Comme initialement, on voulait aussi récupérer ce qui arrivait sur stdout de ces commande dans un "widget" on va profiter de s/classer pour faire cela un peu plus "souple".

    Si on se contente d’écrire le fonctionnel qui fait ca avec un minimum pour la "demo", ca pourrait donner:
    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
    import collections
    from PyQt4.QtCore import QProcess, pyqtSignal, pyqtSlot
     
    class Process(QProcess):
        read_line = pyqtSignal(str)
     
        def __init__(self):
            super().__init__()
            self._queue = collections.deque()
            self.readyReadStandardOutput.connect(self._has_data)
            self.finished[int].connect(self._start_next)
     
        @pyqtSlot()    
        def _has_data(self):
            data = self.readAllStandardOutput()
            for line in  bytes(data).decode('utf-8').splitlines():
                if line:
                    self.read_line.emit(line)
     
        def start(self, command):
            queue = self._queue
            if not queue:
                self.read_line.emit('start: %s' % command)
                super().start(command)
            queue.append(command)
     
        @pyqtSlot(int)
        def _start_next(self, exit_code):
            queue = self._queue
            queue.pop()
            self.read_line.emit('*done*')
            if queue:
                self.read_line.emit('start: %s' % queue[0])
                super().start(queue[0])
     
    if __name__ == '__main__':
        import sys
        from PyQt4.QtGui import QApplication, QTextEdit
     
        app = QApplication(sys.argv)
        display = QTextEdit()
        display.show()
     
        process = Process()
        process.read_line.connect(display.append)
        for _ in range(3):
            process.start('py -3.3 -c "import time; time.sleep(1)"')
     
        app.exec_()
    note: c'est ce que j'appelle un "brouillon", l'intention est de montrer la mise en œuvre du fonctionnel attendu. Les aspects non fonctionnels (traiter les erreurs, les petites impasses ici et la...) ne sont pas traites.

    - W

  8. #48
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    Merci pour ce code.

    Je suis assez content de moi car on en arrive à peu près à la même chose

    à la différence que ma liste de commande est créée avant l'appel au process.

  9. #49
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    Bonsoir,

    je me permets de relancer un peu ce sujet car je rencontre un soucis.

    En effet, j'utilise toujours le systeme ci-dessus qui marche bien mais je suis confronté à un logiciel qui renvoie pas loin de 30 lignes d'un coup puis plus rien pendant 10s et à nouveau plein de ligne.

    Ce logiciel (pbuilder) permet la creation de paquet debian depuis des fichiers sources.

    Y aurait-il moyen d'améliorer ce systeme pour eviter ce probleme ?

    merci !

  10. #50
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 334
    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 334
    Points : 36 855
    Points
    36 855
    Par défaut
    Citation Envoyé par hizoka Voir le message
    En effet, j'utilise toujours le systeme ci-dessus qui marche bien mais je suis confronté à un logiciel qui renvoie pas loin de 30 lignes d'un coup puis plus rien pendant 10s et à nouveau plein de ligne.

    Ce logiciel (pbuilder) permet la creation de paquet debian depuis des fichiers sources.
    Dois je comprendre que lancé à la console, pbuilder n'affiche pas par accoups?
    Et qu'ils ne sont visibles que redirigés avec par la mécanique QProcess etc...

    - W

  11. #51
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    Si je lance la commande dans ma console linux, tout marche bien meme s'il y a un tout petit ralentissement de temps en temps mais rien de vraiment visible.

    Mais dans le cas actuel, je lance la commande depuis un QProcess qui renvoie les infos via readyReadStandardOutput et co.

    C'est en effet exactement le meme systeme que celui que tu m'avais proposé.

  12. #52
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 334
    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 334
    Points : 36 855
    Points
    36 855
    Par défaut
    Le truc est que changer stdout d'un tty à un pipe, c'est aussi changer le "buffering".
    i.e, les bytes s'accumulent jusqu'à 8192 avant que readyReadStandardOutput signale "has_data".
    Dans le code que je vous ai donné, remplacez:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
        for _ in range(3):
            process.start('py -3.3 -c "import time; time.sleep(1)"')
    par:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
        process.start('python3 test.py')
    Puis dans test.py:
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    #! /usr/bin/env python
    #! -*- coding:utf-8 -*-
    import time
    import sys
     
    for x in range(20):
        print ('count:', x)
        #sys.stdout.flush()
        time.sleep(0.1)
    Pour faire ce que vous voulez, il suffirait de ne pas avoir le .flush en commentaire (essayez, vous verrez la différence)
    Hélas, pas toutes les programmes regardent si stdout isatty pour faire des flush "automatiques".

    Une autre méthode serait de passer le pipe en mode "unbuffered" (on peut le faire côté QProcess, sans changer le programme appelé).
    Ca devrait être possible mais il faut arriver à décoder la documentation Qt et arriver à faire marcher ce truc là.
    Et çà, c'est la taxe à payer à vouloir utiliser des bibliothèques Qt plutôt que leurs équivalents Python.
    Mais une bonne âme voudra peut être partager le bout de code qui le fait.

    - W

  13. #53
    Membre confirmé
    Homme Profil pro
    Inscrit en
    Novembre 2013
    Messages
    563
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations forums :
    Inscription : Novembre 2013
    Messages : 563
    Points : 460
    Points
    460
    Par défaut
    Salut,

    merci pour ta réponse.

    Donc si je comprends bien, le soucis vient du logiciel qui ne flush pas et du coup readyReadStandardOutput ne signale les infos tous les 8192 bytes ?

    De ce fait il n'est pas possible d'agir là dessus ?

    Je présume que l'on ne peut pas augmenter la sensibilité du nombre de byte pour déclencher has_data ?

    Il y aurait moyen de contourner le probleme en utilisant python plutot que qt ? des process au lieu de QProcess ?

    D'apres ce que je comprends on peut essayé de faire une boucle lançant un waitForReadyRead qui permettrait de charger les retours plus souvent, c'est bien ça ?

  14. #54
    Expert éminent sénior
    Homme Profil pro
    Architecte technique retraité
    Inscrit en
    Juin 2008
    Messages
    21 334
    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 334
    Points : 36 855
    Points
    36 855
    Par défaut
    Salut,

    Citation Envoyé par hizoka Voir le message
    Il y aurait moyen de contourner le probleme en utilisant python plutot que qt ? des process au lieu de QProcess ?
    Le problème n'est pas "contournable", il faut le gérer.
    Il existe des solutions côté programme appelé, côté Qt, ou Python subprocess.

    Avec python subprocess, on écrit Popen(..., bufsize=0,...) et voilà (pas testé: je préfère modifier le pipe).

    Pour QProcess, il faut touiller.

    - W

Discussions similaires

  1. Réponses: 7
    Dernier message: 01/09/2010, 13h27
  2. Wpf/C# Bloquer interaction GUI pendant animation
    Par gomezmic dans le forum Windows Presentation Foundation
    Réponses: 7
    Dernier message: 17/02/2010, 17h04
  3. [XL-97] UserForm : comment ne pas bloquer le code appelant?
    Par Penegal dans le forum Macros et VBA Excel
    Réponses: 31
    Dernier message: 17/04/2009, 15h35
  4. [Conception] Comment faire pour bloquer une valeur pendant 24H
    Par lolodelp dans le forum PHP & Base de données
    Réponses: 4
    Dernier message: 07/07/2006, 15h46
  5. [API]Comment ne pas bloquer la fenêtre principal...
    Par X-K4l1 dans le forum Windows
    Réponses: 1
    Dernier message: 16/08/2005, 14h10

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