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

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre chevronné
    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
    Par défaut Comment ne pas bloquer la GUI pendant le travail ?
    Bonsoir,

    je voudrais lancer des commandes python et autre (os.system, subprocess...) sans bloquer l'interface graphique.

    Je m'explique, une fois le travail en cours, impossible de cliquer ou que ce soit.

    En bash je lançais ces commandes en fond via &.

    Comment peut-on faire en python ?

    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
    cmd = ["mkvextract", "tracks", "/home/hizoka/Fichier.mkv", '0:"/home/hizoka/0_Video.mkv"', '1:"/home/hizoka/1_Audio_dts_eng.dts"']
     
    try:
        reply = subprocess.Popen(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    except (IOError, OSError) as exc:
        self.print_(u"\nSubprocess ERROR: {0}".format(exc))
        return
     
    old_text = ""
    while 1:
        text = reply.stdout.readline()[:-1]
        if type(text) != str or text == '' and reply.poll() != None: # Terminé !
            break
        elif old_text == text:
            pass
        else:
            old_text = text
            if "Progression" in text:
                chains = text.split(":")
                try:
                    value = int(chains[1].strip().replace('%', ''))
                except:
                    pass
                else:
                    self.ui.progressBar.setValue(value)
    Dans le cas présent, l'avancement va bien fonctionner mais impossible de cliquer tant que ce n'est pas fini car cela occupe le script.
    Il faudrait pouvoir lancer cette boucle infinie en fond...

    Autre exemple :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    def MKValidator(self):
        self.ui.progressBar.setMaximum(0) # Permet de faire progresser automatiquement la progressbar
        os.system('mkclean "{}"'.format(Configs["MKVLink"]))
        self.ui.progressBar.setMaximum(100) # Arrête la progression auto
    Il ne se passe rien dans le cas présent, pas de progression dans la progressbar...
    j'ai testé également avec subprocess.Popen, subprocess.check_out.
    La, le soucis est le meme, si je surveille la commande pour savoir quand arreter la progression automatique, je bloque mon logiciel.

    Une idée de comment surveiller ces commandes pour envoyer la progression ou non sans bloquer la gui (qui permettrait le clic sur un bouton pour stopper le travail) ?

    Voilou

    Merci.

  2. #2
    Membre éclairé
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    89
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Mai 2013
    Messages : 89
    Par défaut


    Je précise d'abord que je suis loin d'être un expert en python, il y a plus de chance que ce soit faux que vrai. mais je :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
     self.print_(u"\nSubprocess ERROR: {0}".format(exc))
    Ne mettrais pas de u puisque tu codes en Python 3.3.Par contre si c'était en 2.7.x ce serait le cas.

    Ferais comme ça (à la fin):

    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
    cmd = ["mkvextract", "tracks", "/home/hizoka/Multimedia/Videos/Films/HD/World War Z.mkv", '0:"/home/hizoka/Download/0_Video_World.War.Z.2013.MULTi.UNRATED.1080p.BluRay.x264.AC3.DTS-SALEM.mkv"', '1:"/home/hizoka/Download/1_Audio_dts_eng.dts"']
     
    try:
        reply = subprocess.Popen(cmd, universal_newlines=True, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
    except (IOError, OSError) as exc:
        self.print_(u"\nSubprocess ERROR: {0}".format(exc))
        return
     
    old_text = ""
    while 1:
        text = reply.stdout.readline()[:-1]
        if type(text) != str or text == '' and reply.poll() != None: # Terminé !
            break
        elif old_text == text:
            pass
        else:
            old_text = text
            if "Progression" in text:
                chains = text.split(":")
                try:
                    value = int(chains[1].strip().replace('%', ''))
                    self.ui.progressBar.setValue(value)
                except:
                    pass
    Et je rajouterai un break pour casser la boucle While. Cela peut expliquer pourquoi cela ne s'arrete pas.

  3. #3
    Membre éclairé
    Homme Profil pro
    Inscrit en
    Mai 2013
    Messages
    89
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Secteur : Industrie

    Informations forums :
    Inscription : Mai 2013
    Messages : 89
    Par défaut
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    ef MKValidator(self):
        self.ui.progressBar.setMaximum(0) # Permet de faire progresser automatiquement la progressbar
        os.system('mkclean "{}"'.format(Configs["MKVLink"]))
        self.ui.progressBar.setMaximum(100) # Arrête la progression auto
    Une erreur dans ton code cad tu mets
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    self.ui.progressBar.setMaximum(0) # Permet de faire progresser automatiquement la progressbar
    Ne serait-ce pas plutot setMinimum(0)

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

    Citation Envoyé par hizoka Voir le message
    En bash je lançais ces commandes en fond via &.
    Ca fonctionnait parce que le GUI (GTK-server) était aussi dans un thread sépare.

    Comment peut-on faire en python ?
    Par défaut, Python est un langage de scripting mais le GUI démarre dans le thread principal comme avec n'importe quel autre langage de programmation "normal". Si vous faites autre chose, le GUI se gèle.

    Si vous ne voulez pas trop cassez la logique, ça serait pas mal de pousser le GUI dans un thread a lui et garder la main sur le thread principal. Comme ce n'est pas le "standard", ce sera peut être "plus complique", voir ici par exemple.

    Avec le mode de fonctionnement "normal" (GUI dans thread principal), il faut revoir toutes les interfaces et la logique qui va avec.

    Ceci dit, comme c'est quand même "basique". Il y a plein d'exemples qui traînent dans ce forum ou qui sont accessibles via google ou autre.

    A vous de voir ce qui est le moins pire cote changements.

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

    Pour lancer une opération longue à partir d'un GUI sans le bloquer, j'utilise d'habitude un thread (QThread). Le principe est le suivant:

    - le GUI lance le thread, mais rend tout de suite la main au GUI. Ainsi, à notre échelle au moins, le GUI et le thread semblent s'exécuter en même temps ( le GUI n'est pas bloqué).

    - mais le thread ne peut pas toucher directement aux éléments graphiques! Si on veut que le thread renseigne le GUI pendant son exécution, par exemple d'un avancement, il lui envoie un message avec emit.

    - à la fin de son exécution, le thread peut aussi envoyer un message pour signaler sa fin.

    Dans les 2 cas, le message peut encapsuler des données.

    Dans les 2 cas, on crée des signaux spécifiques.

    - Du côté du GUI, la réception des messages d'avancement et de la fin du thread est préparée avec connect. Donc, à chaque réception d'un message, la méthode prévue s'exécute pour agir sur le graphique par exemple..

    Si tu veux un exemple, je peux t'en monter un.

    Mais avec un processus, je ne sais pas si on peut faire la même chose.

  6. #6
    Membre chevronné
    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
    Par défaut
    tyrtamos => Ta solution me parait être la meilleure et j'arrive à en comprendre le principe mais je vais avoir besoin d'un bon exemple

  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
    Voilà un petit exemple:

    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
    67
    68
    69
    70
    71
    72
    73
    74
    75
    76
    77
    78
    79
    80
    81
    82
    83
    84
    85
    86
    87
    88
    89
    90
    91
    92
    93
    94
    #!/usr/bin/python
    # -*- coding: utf-8 -*-
    from __future__ import division
    # Python 2.7
     
    import sys, os, time
     
    from PyQt4 import QtCore, QtGui
     
    #############################################################################
    class Operationlongue(QtCore.QThread):
     
        # création des nouveaux signaux
        info = QtCore.pyqtSignal(int)
        fini = QtCore.pyqtSignal()
     
        #========================================================================
        def __init__(self, parent=None):
            super(Operationlongue, self).__init__(parent)
     
        #========================================================================
        def run(self):
            """partie qui s'exécute en tâche de fond"""
            for i in range(0, 101):
                time.sleep(0.05)
                self.info.emit(i)
            # fin du thread
            self.fini.emit()
     
    #############################################################################
    class Fenetre(QtGui.QWidget):
     
        #========================================================================
        def __init__(self, parent=None):
            super(Fenetre,self).__init__(parent)
     
            self.depart = QtGui.QPushButton(u"Départ", self)
            self.depart.clicked.connect(self.lancement)
     
            self.barre = QtGui.QProgressBar(self)
            self.barre.setRange(0, 100)
     
            self.barre.setValue(0)
     
            posit = QtGui.QGridLayout()
            posit.addWidget(self.depart, 0, 0)
            posit.addWidget(self.barre, 1, 0)
            self.setLayout(posit)        
     
            self.operationlongue = None
     
        #========================================================================
        def lancement(self, ok):
     
            if self.operationlongue==None or not self.operationlongue.isRunning():
                # initialisation de la barre de progression
                self.barre.reset()
                self.barre.setRange(0, 100)
                self.barre.setValue(0)
                # démarre l'opération longue dans le thread
                self.operationlongue = Operationlongue()
                self.operationlongue.info.connect(self.progression)
                self.operationlongue.fini.connect(self.stop)
                self.operationlongue.start()
     
        #========================================================================
        @QtCore.pyqtSlot(int)
        def progression(self, i):
            """lancé à chaque réception d'info émis par le thread"""
            self.barre.setValue(i)
            QtCore.QCoreApplication.processEvents() # force le rafraichissement
     
        #========================================================================
        @QtCore.pyqtSlot()
        def stop(self):
            """Lancé quand le thread se termine"""
            self.barre.setValue(100)
            QtGui.QMessageBox.information(self,
                u"Téléchargement",
                u"Opération terminée!")
     
        #========================================================================
        def closeEvent(self, event):
            """lancé à la fermeture de la fenêtre quelqu'en soit la méthode"""
            if self.operationlongue.isRunning():
                self.operationlongue.terminate()
            event.accept()
     
    #############################################################################
    if __name__ == "__main__":
        app = QtGui.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())

  8. #8
    Membre chevronné
    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
    Par défaut
    Bien qu'un peu complexe j'ai plutot bien compris ton exemple

    et dans le cas ou l'on voudrait un bouton annulant ce qui se passe sur quoi faudrait-il agir ?
    Sur les connexions ? en envoyant le signal de fin ou un autre signal ?
    en ajoutant une vérification (de la présence d'un fichier ou d'une variable par ex) dans le run de Operationlongue ?

    @ Cenwen
    => pour le u"" en effet ce n'est pas necessaire mais j'ai copié collé la proposition de vins vite fait, je ne l'utilise pas encore.

    => Normalement apres tous mes tests, la boucle s'arrete nickel a chaque fois.

    => Et c'est min setMinimum qu'il faut mettre à 0 (teste sur qtdesigner tu verras le resultat directement )


    @ wiztricks
    Merci pour le lien je regarderai ca plus en detail demain

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