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 :

Suivi de la progression lors de l'extraction d'un ZIP et mise en lignne


Sujet :

PyQt Python

  1. #1
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut Suivi de la progression lors de l'extraction d'un ZIP et mise en lignne
    Bonjour,

    Je voudrais savoir si la lib zipfile permet de faire un suivi de progression avec PyQt/PySide ?

    Lorsque j'extrais un ZIP je voudrais avoir le suivi sur une progress bar le tout dans un thread bien entendu .

    Par la même occasion, si la lib permet "d'emiter" l'extraction, on peut combiner sur la même progress bar, premièrement l'extraction du zip et une fois à 100% relancer un thread qui upload l'extraction sur un FTP et donc repartir de 0 à 100% sur la progress bar ?

    PS : À chaque fois que je me lance dans les thread avec progress bar c'est toujours la partie que j'ai du mal à comprendre, et je ne l'a comprend toujours pas . À chaque fois on dirait que la lib que j'utilise ne permet pas de faire un thread + progress bar, quelque soit le langage que j'utilise (j'utilise toujours des lib qui n'emit rien on dirait lol)

    Merci d'avance.

  2. #2
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Sur le plan du principe, PyQt4-Qt4 n'est pas "thread-safe" (comme beaucoup d'autres bibliothèques graphiques). Cela veut dire qu'un thread ne doit pas toucher directement au graphique. Même si ça marche de temps en temps, c'est une cause courante de plantage difficile à comprendre.

    Par contre, il est facile de faire fonctionner une barre de progression: si on utilise pour le thread un descendant de QThread, ce thread peut envoyer des signaux au programme principal (emit), et c'est lui qui met à jour la barre de progression.

    A part cela, pour qu'une opération puisse mettre à jour une barre de progression, il faut que cette opération renvoie quelque chose grâce à un "callback" pendant toute sa durée. C'est le cas avec les fonctions de téléchargement ftp.

    Mais je n'ai pas vu de callback dans les fonctions d'extraction de zipfile. On peut cependant au moins afficher une barre de progression et la faire fonctionner en "chenille". A la fin de l'extraction zip, rien n'empêche au thread d'envoyer un message de fin de zip pour que la barre de progression soit re-paramétrée pour l'opération de téléchargement ftp, cette fois avec une "vraie" progression 0=>100%.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  3. #3
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Comment peut-on donc faire fonctionner l'extraction de zipfile via une "chenille" ? En gros c'est une progression qui est fausse par rapport à l'extraction ?

    Sinon pour extraire un zip, tu connais une lib qui permet d'avoir un callback pour gérer un thread ?

  4. #4
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    La chenille, c'est une barre de progression qui bouge tout le temps de gauche à droite et de droite à gauche sans avoir un début ni une fin. Au lieu d'avoir un mini de 0 et un maxi de 100, les 2 sont mis à zéro. Cela dit simplement: "prenez patience: on bosse!"

    Je ne connais pas de fonction d'extraction zip qui pourrait alimenter une progression, mais à vrai dire, je n'en ai jamais cherché. Peut-être quelqu'un d'autre en connait?
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  5. #5
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Donc il faudrait que je lance cette barre de progression dans un thread de l'appel de zipfile.ZipFile.extractall(...) ?

    Tu aurais un exemple à tout hasard ? Tout les exemples que je trouve (quelque soit le langage) c'est une boucle for de 0 à 100, je trouve l'exemple pas super explicite lorsque l'on veut l'adapter à notre code.

  6. #6
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Ok: je te fais ça demain matin. Ce sera en Python 3 et PyQt4, mais c'est assez facile à convertir si nécessaire.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  7. #7
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Merci . J'ai déjà hâte :p

  8. #8
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Résultat des courses en 3 points:

    1- modèle de code pour progression en chenille (action longue dans un thread SANS callback)

    2- modèle de code pour progression en % (action longue dans un thread AVEC callback)

    3- une extraction d'une fichier zip AVEC callback

    Je n'ai pas cherché à faire un code subtil pour rester clair, et je l'ai largement commenté.

    ================================================================================
    1- modèle de code pour progression en chenille (action longue dans un thread SANS callback)

    Pour le modèle, la tâche longue sans callback est tout simplement un "sleep(5)" qui simule une tâche de 5 secondes. A remplacer par une vrai tâche.

    Voilà le code proposé:

    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
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    # Python 3, PyQt4
     
    """
    modèle pour progression en chenille d'une tâche longue sans callback qui
    s'exécute dans un thread
    """
     
    import sys
    from PyQt4 import QtCore, QtGui
     
    # pour la simulation d'une tâche longue
    from time import sleep
     
    #############################################################################
    class Monthread(QtCore.QThread):
     
        # création d'un nouveau signal pour informer de la fin du thread
        finduthread = QtCore.pyqtSignal(str)
     
        #========================================================================
        def __init__(self, parent=None):
            QtCore.QThread.__init__(self, parent)
     
        #========================================================================
        def run(self):
            # lancement d'une tâche qui dure 5 secondes
            sleep(5)
            # fin normale du thread
            self.finduthread.emit("Fin de l'action")
     
    #############################################################################
    class Fenetre(QtGui.QWidget):
     
        #========================================================================
        def __init__(self, parent=None):
            super(Fenetre, self).__init__(parent)
            self.resize(300, 200)
     
            # bouton qui exécute la méthode action
            self.bouton = QtGui.QPushButton(u"début", self)
            self.bouton.clicked.connect(self.action)
     
            # position du bouton dans la fenêtre
            posit = QtGui.QGridLayout()
            posit.addWidget(self.bouton, 0, 0)
            self.setLayout(posit)
     
            # création des variables définies plus tard
            self.monthread = None
            self.prog = None
     
        #========================================================================
        def action(self):
            """méthode lancée par le bouton pour démarrer ou stopper une
               opération longue dans un thread
            """
            if self.monthread == None or not self.monthread.isRunning():
                # => démarrage de l'action dans le thread
     
                # création du QProgressDialog
                self.prog = QtGui.QProgressDialog("En cours...", "Annuler", 0, 0, self)
                self.prog.setWindowTitle("Action")
                # branchement du bouton "annuler" à la méthode 'stopaction'
                self.prog.canceled.connect(self.stopaction)
                # affichage de la fenêtre de progression
                self.prog.show()
     
                # lancement du thread avec son opération
                self.monthread = Monthread()
                # se préparer à recevoir le signal de fin du thread
                self.monthread.finduthread.connect(self.finaction)
                # démarrer le thread
                self.monthread.start()
     
                # changement du texte du bouton
                self.bouton.setText("Stop")
     
            else:
                # => arrêt du thread demandé par le bouton "Stop"
                self.stopaction()
     
        #========================================================================
        def stopaction(self):
            """méthode lancée par le bouton "annuler" de la fenêtre de progression
            """
            # arrêt brutal du thread
            self.monthread.terminate()
            # arrêt de la fenêtre de progression
            self.prog.reset()
            # retour au texte initial du bouton
            self.bouton.setText("Début")
     
        #========================================================================
        def finaction(self, msg):
            """méthode lancée à la fin normale du thread
            """
            # arrêt de la fenêtre de progression
            self.prog.reset()
            # retour au texte initial du bouton
            self.bouton.setText("Début")
            # affichage du message de fin
            QtGui.QMessageBox.information(self,
                "Information",
                msg)
     
    #############################################################################
    def main():
        app = QtGui.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
     
    #############################################################################
    if __name__ == "__main__":
        main()
    ================================================================================
    2- modèle de code pour progression en % (action longue dans un thread AVEC callback)

    Pour le modèle, la tâche longue avec callback est une fonction qui dure 5 secondes et qui renvoie son avancement en % au callback. A remplacer par une vrai tâche.

    Voilà le code proposé:

    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
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    # Python 3, PyQt4
     
    """
    modèle pour progression en % d'une tâche longue avec callback qui
    s'exécute dans un thread
    """
     
    import sys
    from PyQt4 import QtCore, QtGui
     
    # pour la simulation d'une tâche longue
    from time import time, sleep
     
    #############################################################################
    def action(callback=None):
        """action qui dure 5 secondes mais qui renvoie un % d'avancement
           en appelant la méthode callback
        """
        delai = 5  # delai de 5 secondes
        t0 = time()  # temps initial
        while True:
            tempspasse = time() - t0
            if callback != None:
                callback(tempspasse / delai)  # envoi du % d'avancement
            if tempspasse > delai:
                break  # délai dépassé
            sleep(0.1)  # simulation d'un temps de traitement
     
    #############################################################################
    class Monthread(QtCore.QThread):
     
        # création d'un nouveau signal pour info du % d'avancement
        progduthread = QtCore.pyqtSignal(float)
        # création d'un nouveau signal pour info de la fin du thread
        finduthread = QtCore.pyqtSignal(str)
     
        #========================================================================
        def __init__(self, parent=None):
            QtCore.QThread.__init__(self, parent)
     
        #========================================================================
        def run(self):
            # lancement d'une tâche qui dure 5 secondes
            action(self.avancement) # on pass le callback en argument
            # fin normale du thread
            self.finduthread.emit("Fin de l'action")
     
        #========================================================================
        def avancement(self, pourc):
            """méthode lancée pendant l'action avec callback
            """
            # envoi du % d'avancement pour mise à jour de la barre de progression
            self.progduthread.emit(pourc)
     
    #############################################################################
    class Fenetre(QtGui.QWidget):
     
        #========================================================================
        def __init__(self, parent=None):
            super(Fenetre, self).__init__(parent)
            self.resize(300, 200)
     
            # bouton qui exécute la méthode action
            self.bouton = QtGui.QPushButton(u"début", self)
            self.bouton.clicked.connect(self.action)
     
            # position du bouton dans la fenêtre
            posit = QtGui.QGridLayout()
            posit.addWidget(self.bouton, 0, 0)
            self.setLayout(posit)
     
            # création des variables définies plus tard
            self.monthread = None
            self.prog = None
     
        #========================================================================
        def action(self):
            """méthode lancée par le bouton pour démarrer ou stopper une
               opération longue dans un thread
            """
            if self.monthread == None or not self.monthread.isRunning():
                # => démarrage de l'action dans le thread
     
                # création du QProgressDialog
                self.prog = QtGui.QProgressDialog("En cours...", "Annuler", 0, 100, self)
                self.prog.setWindowTitle("Action")
                # branchement du bouton "annuler" à la méthode 'stopaction'
                self.prog.canceled.connect(self.stopaction)
                # affichage de la fenêtre de progression
                self.prog.show()
     
                # lancement du thread avec son opération
                self.monthread = Monthread()
                # se préparer à recevoir le % d'avancement
                self.monthread.progduthread.connect(self.progression)
                # se préparer à recevoir le signal de fin du thread
                self.monthread.finduthread.connect(self.finaction)
                # démarrer le thread
                self.monthread.start()
     
                # changement du texte du bouton
                self.bouton.setText("Stop")
     
            else:
                # => arrêt du thread avant la fin, demandé par le bouton "Stop"
                self.stopaction()
     
        #========================================================================
        def progression(self, pourc):
            """mise à jour de la barre de progression
            """
            self.prog.setValue(pourc * 100)
            if pourc >= 100:
                self.prog.reset()
            # forcer la mise à jour en temps réel de l'affichage
            QtCore.QCoreApplication.processEvents()
     
        #========================================================================
        def stopaction(self):
            """méthode lancée par le bouton "annuler" de la fenêtre de progression
            """
            # arrêt brutal du thread
            self.monthread.terminate()
            # arrêt de la fenêtre de progression
            self.prog.reset()
            # retour au texte initial du bouton
            self.bouton.setText("Début")
     
        #========================================================================
        def finaction(self, msg):
            """méthode lancée à la fin normale du thread
            """
            # arrêt de la fenêtre de progression
            self.prog.reset()
            # retour au texte initial du bouton
            self.bouton.setText("Début")
            # affichage du message de fin
            QtGui.QMessageBox.information(self,
                "Information",
                msg)
     
    #############################################################################
    def main():
        app = QtGui.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
     
    #############################################################################
    if __name__ == "__main__":
        main()
    ================================================================================
    3- une extraction d'une fichier zip AVEC callback

    Il est vrai que le module zipfile de Python n'a pas prévu de callback, et c'est dommage. Mais on a la chance d'avoir accès à une grande partie du code: C:\Python34\Lib\zipfile.py.

    On peut donc:
    1- sous-classer la classe ZipFile du module zipfile
    2- surcharger sa méthode extractall pour ajouter le callback qui manque!!!

    Voilà ce que ça donne. La classe Monzip remplace donc la classe ZipFile du module zipfile, et la fonction "extractionzip" pourra être appelée dans le thread du cas 2 précédent, avec le callback du thread pour la mise à jour de la barre de progression.

    Pour faire l'essai, j'ai fabriqué un fichier Lib.zip avec 7-Zip, qui représente tout le répertoire Lib de Python (chez moi: 14915 fichiers, 1440 répertoires).
    A lancer en console: il s'affichera le % d'avancement jusqu'à 100.00 % pour 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
    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
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    # Python 3, PyQt4
     
    import zipfile
     
    #############################################################################
    def afficheprog(pourc):
        print("%.2f %%" % (pourc * 100,))
     
    #############################################################################
    class Monzip(zipfile.ZipFile):
     
        #========================================================================
        def __init__(self, *args, **kwargs):
            super(Monzip, self).__init__(*args, **kwargs)
     
        #========================================================================
        def extractall(self, path=None, members=None, pwd=None, callback=None):
            """méthode de la classe ZipFile simplement recopiée ici avec ajout
               d'une fonction de callback pour envoi du % d'avancement
            """
            if members is None:
                members = self.namelist()
     
            n = len(members)
     
            for i, zipinfo in enumerate(members):
                self.extract(zipinfo, path, pwd)
                if callback != None:
                    callback((i + 1) / n)
     
    #############################################################################
    def extractionzip(fichierzip, repextract, callback=None):
        """"extrait le fichier zip "fichierzip" et place le résultat dans le
            répertoire "repextract". Si callback est donné, il est appelé
            à chaque fichier extrait avec le % d'avancement
        """
        # procéder à l'extraction
        with Monzip(fichierzip, compression=zipfile.ZIP_DEFLATED) as fzip:
            fzip.extractall(repextract, callback=callback)
     
    #############################################################################
    def main():
        # nom du fichier zip à extraire
        fichierzip = "Lib.zip"
        # répertoire où extraire les fichiers
        repextract = "temp"
        # procéder à l'extraction
        extractionzip(fichierzip, repextract, callback=afficheprog)
     
    #############################################################################
    if __name__ == "__main__":
        main()
    Ok?
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  9. #9
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Merci pour toutes ces informations . Je vais tester au fur et à mesure et tenter de comprendre. Déjà si je comprend bien la partie 3, peut s'adapter pour du GUI via la partie 2 ? Donc récupérer l'idée du callback de la partie 3 pour l'utiliser dans la partie 2, en utilisant un thread pour éviter le freeze de la fenêtre et donc pouvoir voir le résultat de la progress bar.

    En tout cas je vais méditer tout ça

  10. #10
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Bon et bien je galère à mélanger la méthode 2 et 3 :/. Pour mettre à jour le pourcentage de l'extraction.

  11. #11
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Ok: je te fais le regroupement demain matin.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  12. #12
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Encore une fois, j'ai encore hâte

    PS : Tu aurais des bonnes ressources à me conseiller (livres, screencast) sur pyqt/pyside bien expliqué ?

  13. #13
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Voilà l'intégration du 3e code dans le 2e.

    Au delà d'un simple regroupement, j'ai amélioré plusieurs choses, en particulier le mécanisme d'arrêt anticipé.

    - puisque le callback est appelé à chaque fichier extrait, on peut l'utiliser pour déclencher une exception. Cela permet d'arrêter le dézippage sans utiliser cet abominable ".terminate()" qui arrête le QThread d'une façon trop brutale.

    - il faut aussi déconnecter (disconnect) les liens entre les signaux du thread avec les méthodes de la fenêtre graphique en cas d'arrêt anticipé

    - pour le fun, j'ai créé une exception personnalisé "Arretanticipe": ce n'est pas indispensable (on peut se contenter d'un "raise Exception")

    J'ai aussi ajouté quelques commentaires.

    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
    95
    96
    97
    98
    99
    100
    101
    102
    103
    104
    105
    106
    107
    108
    109
    110
    111
    112
    113
    114
    115
    116
    117
    118
    119
    120
    121
    122
    123
    124
    125
    126
    127
    128
    129
    130
    131
    132
    133
    134
    135
    136
    137
    138
    139
    140
    141
    142
    143
    144
    145
    146
    147
    148
    149
    150
    151
    152
    153
    154
    155
    156
    157
    158
    159
    160
    161
    162
    163
    164
    165
    166
    167
    168
    169
    170
    171
    172
    173
    174
    175
    176
    177
    178
    179
    180
    181
    182
    183
    184
    185
    186
    187
    188
    189
    190
    191
    192
    193
    194
    195
    196
    197
    198
    199
    200
    201
    202
    203
    204
    205
    206
    #!/usr/bin/python3
    # -*- coding: utf-8 -*-
    # Python 3, PyQt4
     
    """
    Exemple de code pour dézipper un fichier zip avec barre de progression
    et exécution dans un thread pour éviter le gel du graphique
    """
     
    import sys
    import zipfile
     
    from PyQt4 import QtCore, QtGui
     
    #############################################################################
    class Arretanticipe(Exception):
        """création d'une exception spécifique
        """
        pass
     
    #############################################################################
    class Monzip(zipfile.ZipFile):
        """sous classement de ZipFile pour ajouter la fonctionnalité de callback
           permettant de visualiser la progression du traitement
        """
     
        #========================================================================
        def __init__(self, *args, **kwargs):
            super(Monzip, self).__init__(*args, **kwargs)
     
        #========================================================================
        def extractall(self, path=None, members=None, pwd=None, callback=None):
            """simple recopie de la méthode de ZipFile du module zipfile
               avec ajout du callback pour renvoyer le % d'avancement
            """
            if members is None:
                members = self.namelist()
     
            n = len(members)  # <== ajout: nombre total de fichiers à extraire
     
            for i, zipinfo in enumerate(members):
                self.extract(zipinfo, path, pwd)
                if callback != None:  # <== ajout
                    callback((i + 1) / n)  # <== ajout: pourcentage d'avancement
     
    #############################################################################
    class Monthread(QtCore.QThread):
     
        # création d'un nouveau signal pour info du % d'avancement
        progduthread = QtCore.pyqtSignal(float)
        # création d'un nouveau signal pour info de la fin du thread
        finduthread = QtCore.pyqtSignal(str)
     
        #========================================================================
        def __init__(self, fichierzip, repextract, parent=None):
            QtCore.QThread.__init__(self, parent)
     
            self.fichierzip = fichierzip  # fichier zip à dézipper
            self.repextract = repextract  # répertoire pour placer l'extraction
     
            self.stopthread = False  # pour gérer l'arrêt anticipé
     
        #========================================================================
        def run(self):
            """partie de la classe de QThread qui s'exécute en tâche de fond
            """
            try:
                # lancement de l'extraction zip avec callback
                with Monzip(self.fichierzip, compression=zipfile.ZIP_DEFLATED) as fzip:
                    fzip.extractall(self.repextract, callback=self.avancement)
                # fin normale du thread
                self.finduthread.emit("Fin de l'action")
            except Arretanticipe:
                # exception déclenchée par une demande d'arrêt anticipée
                pass
     
        #========================================================================
        def avancement(self, pourc):
            """méthode lancée pendant l'action avec callback
            """
            if self.stopthread:
                # une demande d'arrêt anticipé a été faite
                raise Arretanticipe
            else:
                # envoi du % d'avancement pour mise à jour de la barre de progression
                self.progduthread.emit(pourc)
     
        #========================================================================
        def stop(self):
            """appelé pour demander l'arrêt anticipé du thread
            """
            self.stopthread = True
     
    #############################################################################
    class Fenetre(QtGui.QWidget):
     
        #========================================================================
        def __init__(self, parent=None):
            super(Fenetre, self).__init__(parent)
            self.resize(300, 200)
     
            # bouton qui exécute la méthode action
            self.bouton = QtGui.QPushButton(u"début", self)
            self.bouton.clicked.connect(self.action)
     
            # positionn du bouton dans la fenêtre
            posit = QtGui.QGridLayout()
            posit.addWidget(self.bouton, 0, 0)
            self.setLayout(posit)
     
            # création des variables définies plus tard
            self.monthread = None
            self.prog = None
     
        #========================================================================
        def action(self):
            """méthode lancée par le bouton pour démarrer ou stopper une
               opération longue dans un thread
            """
            if self.monthread == None or not self.monthread.isRunning():
                # aucun thread en cours de travail:
                # => démarrage de l'action dans le thread avec progression
     
                # création du QProgressDialog avec une plage de 0% à 100%
                self.prog = QtGui.QProgressDialog("En cours...", "Annuler", 0, 100, self)
                self.prog.setWindowTitle("Action")
                # branchement du bouton "annuler" à la méthode 'stopaction'
                self.prog.canceled.connect(self.stopaction)
                # affichage de la fenêtre de progression
                self.prog.show()
     
                # fichier zip à dézipper
                fichierzip = "Lib.zip"  # <== à adapter
                # répertoire où mettre les fichiers extraits
                repextract = "temp"  # <== à adapter
                # lancement du thread avec son opération
                self.monthread = Monthread(fichierzip, repextract)
                # se préparer à recevoir le % d'avancement
                self.monthread.progduthread.connect(self.progression)
                # se préparer à recevoir le signal de fin du thread
                self.monthread.finduthread.connect(self.finaction)
                # démarrer le thread
                self.monthread.start()
     
                # changement du texte du bouton
                self.bouton.setText("Stop")
     
            else:
                # il y a déjà un thread en cours de travail
                # => arrêt du thread avant la fin, demandé par le bouton "Stop"
                self.stopaction()
     
        #========================================================================
        def progression(self, pourc):
            """met à jour la barre de progression à chaque fois que le thread
               renvoie le % d'avancement grâce au callback
            """
            # met à jour la barre de progression pour affichage du % d'avancement
            self.prog.setValue(pourc * 100)
     
            # force la mise à jour en temps réel de l'affichage
            QtCore.QCoreApplication.processEvents()
     
        #========================================================================
        def stopaction(self):
            """lancé par le bouton "annuler" de la fenêtre de progression
               pour arrêt anticipé du traitement
            """
            # déconnecter les liens avec les signaux du thread
            self.monthread.progduthread.disconnect()
            self.monthread.finduthread.disconnect()
     
            # arrêt anticipée du thread
            self.monthread.stop()
     
            # arrêt de la fenêtre de progression
            self.prog.reset()
     
            # retour au texte initial du bouton
            self.bouton.setText("Début")
     
        #========================================================================
        def finaction(self, msg):
            """méthode lancée à la fin normale du thread
            """
            # arrêt de la fenêtre de progression
            self.prog.reset()
     
            # retour au texte initial du bouton
            self.bouton.setText("Début")
     
            # affichage du message de fin pour la fin normale
            QtGui.QMessageBox.information(self,
                "Information",
                msg)
     
    #############################################################################
    def main():
        app = QtGui.QApplication(sys.argv)
        fen = Fenetre()
        fen.show()
        sys.exit(app.exec_())
     
    #############################################################################
    if __name__ == "__main__":
        main()
    Pour la documentation concernant PyQt4, il y a au moins le présent site! => http://pyqt.developpez.com/tutoriels/

    J'utilise souvent 2 livres:

    - "Rapid GUI Programming with Python and Qt" (http://www.qtrac.eu/pyqtbook.html et http://www.amazon.com/Programming-Py.../dp/0132354187). Mais il commence à dater (2007) et il est en anglais.Je donne l'adresse d'Amazon parce qu'on le trouve quelquefois moins cher en occasion. On peut aussi le trouver en pdf.

    - "Qt4 et C++ : Programmation d'interfaces GUI" (http://www.amazon.fr/Qt4-Programmati.../dp/2744021407). Il concerne le C++ et non Python, mais il est en français. Et il commence à dater aussi (2007). On peut aussi le trouver en pdf.

    Il y a aussi la doc officielle:

    - de riverbank pour PyQt: http://www.riverbankcomputing.com/news

    - pour Qt: http://qt-project.org/doc/qt-4.8/

    - pour Qt mais partiellement en français: sur le site de developpez! http://qt.developpez.com/doc/4.7/index/

    Et, bien sûr, plusieurs sites trouvés par google ("pyqt4 tutorial"):

    - https://wiki.python.org/moin/PyQt/Tutorials

    - http://zetcode.com/gui/pyqt4/

    - etc...

    Sans oublier, bien sûr, mon propre site...
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  14. #14
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Merci . Au final j'étais pas si loin de trouver la solution . Donc au final quand on veut utiliser un thread, on y place dans la méthode run ce que l'on veut exécuter et quand on veut récupérer une progression, il faut que la méthode de l'action dispose d'un callback pour récupérer le pourcentage pendant le traitement ?

  15. #15
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Citation Envoyé par Rtransat Voir le message
    Merci . Au final j'étais pas si loin de trouver la solution . Donc au final quand on veut utiliser un thread, on y place dans la méthode run ce que l'on veut exécuter et quand on veut récupérer une progression, il faut que la méthode de l'action dispose d'un callback pour récupérer le pourcentage pendant le traitement ?
    Oui: tu as compris!

    Bonne chance pur la suite!
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  16. #16
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Merci à toi . Une autre petite question, si dans le même thread, par exemple après l'extraction je veux copier des fichier dans le dossier compressé, c'est plus logique de faire ça après la méthode d'extraction ou de faire un autre thread ? Vu qu'il s'agit d'une autre opération je penserais notamment à faire un autre thread. Notamment si par la suite je copie les fichiers sur un serveur via FTP là c'est d'autant plus logique pour moi qu'il faut faire un autre thread avec une autre barre de progression.

  17. #17
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Bonjour,

    Les 2 solutions sont possibles: le même thread ou deux threads séparés.

    Pour choisir, il y a à mon avis 2 critères intéressants: la lisibilité de ton code (facilité de déverminage et d'évolution ultérieure), et le fait de coder des choses réutilisables plus tard pour d'autres projets. Pour moi, ces deux critères ferait pencher la balance vers la solution "deux threads".
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  18. #18
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Je pense que je vais faire 2 thread séparé .

    Par contre quand je tente d'exécuter un print("test") après le if self.zip_thread == None or not self.zip_thread.isRunning(): je remarque "test" s'affiche pendant le thread et non après ? Comment faire pour l'exécuter après le thread et non pendant ?

  19. #19
    Expert éminent
    Avatar de tyrtamos
    Homme Profil pro
    Retraité
    Inscrit en
    Décembre 2007
    Messages
    4 462
    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 462
    Points : 9 249
    Points
    9 249
    Billets dans le blog
    6
    Par défaut
    Avant le thread, c'est avant son lancement par .start()
    Après le thread, c'est:
    - à la fin de la méthode "finaction" qui est appelée lorsque le thread a fini son travail normalement
    - à la fin de la méthode "stopaction" qui est appelée en cas de demande d'arrêt anticipé

    Entre les 2, le thread et le programme principal s'exécutent en même temps, et le code actuel n'est pas prévu pour synchroniser des affichages, sauf à utiliser la méthode qui met à jour la barre de progression.
    Un expert est une personne qui a fait toutes les erreurs qui peuvent être faites, dans un domaine étroit... (Niels Bohr)
    Mes recettes python: http://www.jpvweb.com

  20. #20
    Membre à l'essai
    Homme Profil pro
    Développeur Web
    Inscrit en
    Novembre 2014
    Messages
    58
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Maritime (Haute Normandie)

    Informations professionnelles :
    Activité : Développeur Web

    Informations forums :
    Inscription : Novembre 2014
    Messages : 58
    Points : 20
    Points
    20
    Par défaut
    Je vais donc appeler une méthode dans finaction qui fera le traitement suivant . En occurrence, uploader le répertoire extrait ^^. J'arrive à me connecter, faut juste que je trouve comment uploader un répertoire via put. Le truc c'est selon la doc :

    PySide.QtNetwork.QFtp.put(data, file[, type=Binary])

    Parameters:
    data – PySide.QtCore.QByteArray
    file – unicode
    type – PySide.QtNetwork.QFtp.TransferType
    Return type:
    PySide.QtCore.int

    Les data c'est donc mon répertoire sous forme de QByteArray mais file je ne vois pas ce que c'est. data peut être remplacer par dev – PySide.QtCore.QIODevice

    Faut que je regarde tout ça .

+ Répondre à la discussion
Cette discussion est résolue.
Page 1 sur 2 12 DernièreDernière

Discussions similaires

  1. Problème lors de l'extraction d'une sous-chaîne
    Par keul85 dans le forum Débuter
    Réponses: 4
    Dernier message: 31/01/2008, 20h58
  2. Problème de colonne lors de l'extraction en PDF
    Par fantagaro dans le forum Cognos
    Réponses: 6
    Dernier message: 25/10/2007, 09h47
  3. Réponses: 6
    Dernier message: 29/05/2007, 17h41
  4. Afficher les colonnes lors d'une extraction oracle
    Par griese dans le forum Shell et commandes GNU
    Réponses: 3
    Dernier message: 08/12/2006, 16h20
  5. [ADO] Progression lors d'un traitement
    Par portu dans le forum Bases de données
    Réponses: 4
    Dernier message: 17/11/2004, 10h06

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