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 :

[Pyside6] QProcess dans un QThread


Sujet :

PyQt Python

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre éclairé Avatar de FadeToBlack
    Homme Profil pro
    ...
    Inscrit en
    Août 2010
    Messages
    321
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : ...
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Août 2010
    Messages : 321
    Par défaut [Pyside6] QProcess dans un QThread
    Bonjour à tous,

    J'ai une question piège pour vous. Malheureusement, je ne pourrais pas vous montrer de code car je n'ai pas accès à mon ordi de bureau aujourd'hui. Mais je pense que la théorie suffit.

    Voici la situation :

    J'ai, actuellement, une mainWindow qui lance un Qprocess. Ce QProcess lance la commande Windows X-COPY pour faire des copies de fichiers d'un répertoire à un autre.

    J'ai mis une progressBar et un compteur afin de visualiser l'avancée de la copie.

    Tout fonctionne bien si je copie plusieurs dizaines de milliers de fichiers de petites tailles. Mais si les fichiers dépassent les 100Mo, cela ne va plus. Mon compteur et la progressBar se figent, tout comme l'app, jusqu'à la fin de la copie.

    J'ai donc pensé créer un QthreadPool à partir de ma mainWindow. Et dans ce second Qthread, je voulais lancer un QProcess pour la copie.

    Le tout étant relié via des Signaux et des Slots pour gérer la QProgressBar et le compteur qui reste dans la mainWindow.

    Est-ce que cela vous parait envisageable ou faut-il que je procède autrement.


    JE vous remercie pour vos réflexions et votre aide.

    Bonne journée à tous

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

    Le problème est que pour faire marcher une barre de progression, il faut un indicateur d'avancement. Pour l'instant, il n'y a que le passage d'une copie de fichier au suivant. Et à ma connaissance, xcopy ne renvoie pas d'indicateur d'avancement en court de copie (à vérifier, mais s'il existait, il faudrait voir comment le récupérer dans le programme graphique).

    La seule solution est donc d'utiliser un autre programme de copie qui serait capable de renvoyer un indicateur de progression.
    On peut même le créer sous Python! J'avais fait un petit code qui faisait cela justement pour avoir une progression:

    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
    def copiefichier(ficsource, ficdestin, lgbuf=8192, callback=None):
        """Copie le contenu du fichier source vers le fichier destination
        """
        lgcop = 0  # portera le nb d'octets copiés pendant la copie
        lgtot = os.path.getsize(ficsource)  # nb total d'octets à copier
     
        if os.path.isdir(ficdestin):
            # la destination étant un répertoire, on lui ajoute le nom source
            ficdestin = os.path.join(ficdestin, os.path.basename(ficsource))
     
        with open(ficsource, 'rb') as fs:
            with open(ficdestin, 'wb') as fd:
                while True:
                    buf = fs.read(lgbuf)
                    if len(buf) == 0:
                        break  # plus rien à copier: on a fini
                    fd.write(buf)
                    if callback != None:
                        lgcop += len(buf)
                        callback(lgcop, lgtot)  # envoi des infos au callback si demandé
    On voit que à chaque bloc binaire copié, un appel est fait au callback avec le nb d'octets copiés et le nb d'octets à copier.

    Peut-être faut-il adapter la taille du buffer à la taille du fichier?

    Il faudrait mettre ça dans un QThread pour ne pas figer le graphique, et permettre de renseigner la barre de progression par un envoi de signaux.

    On pourrait même avoir 2 barres de progression comme ça existe dans certains programmes:
    - une barre de progression qui indique les fichiers déjà copiés par rapport au nb total de fichiers à copier
    - une barre de progression qui indique la progression de la copie de chaque fichier.

    Ce serait intéressant de voir avant tout ça si la performance de ce programme de copie Python est suffisante par rapport à xcopy.

  3. #3
    Membre éclairé Avatar de FadeToBlack
    Homme Profil pro
    ...
    Inscrit en
    Août 2010
    Messages
    321
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : ...
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Août 2010
    Messages : 321
    Par défaut
    Bonjour Tyrtamos,

    X-copy renvoie bien les éléments copiés au fur et à mesure que les copies se font. Question performance, j'arrive à copiés 100 Go de fichiers de 50Mo en moins de 30 mn. Ce qui me suffit.

    Quoiqu'il en soit, via mon Qprocess je récupère les éléments de retour de x-copy et à chaque retour j'incrémente un label.

    Comme, je le disais, cela fonctionne parfaitement avec des fichiers de petites tailles mais à partir de quelques méga cela freeze.

    Je poserai mon code lundi, si cela vous dit de voir ce que j'ai pu faire.

  4. #4
    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
    Citation Envoyé par FadeToBlack Voir le message
    Je poserai mon code lundi, si cela vous dit de voir ce que j'ai pu faire.
    J'ai regardé la doc de xcopy, et je ne vois pas comment avoir une progression de la copie d'un fichier. Donc, oui, ça m'intéresse!

    Par ailleurs, robocopy est considéré comme plus performant que xcopy.

  5. #5
    Membre éclairé Avatar de FadeToBlack
    Homme Profil pro
    ...
    Inscrit en
    Août 2010
    Messages
    321
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Seine Saint Denis (Île de France)

    Informations professionnelles :
    Activité : ...
    Secteur : Associations - ONG

    Informations forums :
    Inscription : Août 2010
    Messages : 321
    Par défaut
    Salut Tyrtamos,

    Voici la partie Qprocess de mon script.

    Ce qui gère le retour du powerShell, et donc de la méthode Xcopy se trouve dans la méthode.

    Si tu as des remarques, sujections pour mon petit problème.

    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
     
    import sys
    from PySide6.QtWidgets import Qdonlication, QLabel, QWidget, QPushButton, \
        QHBoxLayout,QVBoxLayout, QProgressBar, QMessageBox, QLineEdit, QMenuBar
    from PySide6.QtGui import QAction
     
    from PySide6.QtCore import QDirIterator, QDir, QProcess, Signal, Qt, QCoredonlication
    from don.services.tools  import change_path
     
    class copy_data(QWidget):
     
        copyFinished = Signal(object)
        def __init__(self, don):
            super().__init__()
            self.setWindowTitle("Tree Copy")
            self.resize(500,400)
            self.don = don
            self.data = {
                "nbDir":    0,
                "nbFiles":  0,
                "size":     0
            }
     
            self.script_xcopy = "xcopy '" + change_path(self.don['origin']) +"\*' " + change_path(self.don["working"]) + " /s"
            print(self.don['origin'])
            print(self.don['working'])
            print(self.script_xcopy)
            #-----------
     
            self.copiedFiles = 0
     
            self.init_ui()
     
        def init_ui(self):
            self.initialiseData()
            self.repLabel = QLabel(f'Dir to copy: {self.data["nbDir"]}')
            self.fileLabel = QLabel(f'File to copy: {self.data["nbFiles"]}')
            self.sizeLabel = QLabel()
            if self.data['size'] >= 10**9 :
                self.sizeLabel.setText(f'Taille de la copie: {self.data["size"]/1024/1024/1024:.2f} Go')
            else:
                self.sizeLabel.setText(f'Taille de la copie: {self.data["size"]/1024/1024:.2f} Mo')
     
            self.copiedFilesLEdit = QLabel()
     
            self.pb = QProgressBar()
            self.confirm = QPushButton('Confirm')
            self.annul   = QPushButton('Stop')
            self.annul.setEnabled(0)
     
            hb = QHBoxLayout()
            hb.addStretch(1)
            hb.addWidget(self.confirm)
            hb.addWidget(self.annul)
     
     
            vb = QVBoxLayout()
            vb.addWidget(self.repLabel)
            vb.addWidget(self.fileLabel)
            vb.addWidget(self.sizeLabel)
            vb.addStretch()
            vb.addWidget(self.pb)
            vb.addWidget(self.copiedFilesLEdit)
            vb.addLayout(hb)
     
            self.setLayout(vb)
            self.confirm.clicked.connect(self.ps_process)
            self.setWindowFlags(Qt.FramelessWindowHint)
     
     
        def initialiseData(self):
     
            rep = QDirIterator(self.don['origin'], QDir.NoDotAndDotDot | QDir.AllDirs, QDirIterator.Subdirectories )
            files = QDirIterator(self.don['origin'],  QDir.NoDotAndDotDot | QDir.Files, QDirIterator.Subdirectories )
     
     
            while files.hasNext():
                files.next()
                info = files.fileInfo()
                nom = info.fileName()
                chemin = info.filePath()
                self.data["size"] += info.size()
                self.data["nbFiles"] += 1
     
            while rep.hasNext():
                rep.next()
                self.data["nbDir"] += 1
     
     
        def ps_process(self):
            self.confirm.setEnabled(0)
            self.annul.setEnabled(1)
     
            self.powershell_process = QProcess()
     
            self.powershell_process.readyReadStandardOutput.connect(self.powershell_process_stdout)
            self.powershell_process.readyReadStandardError.connect(self.powershell_process_stderr)
            self.powershell_process.finished.connect(self.cleanup_powershell_process)
            self.powershell_process.start("powershell.exe", [self.script_xcopy])
            self.powershell_process.waitForFinished(-1)
     
     
        def powershell_process_stderr(self):
            err = bytes(self.powershell_process.readAllStandardError()).decode("cp858")
            pass
     
        def powershell_process_stdout(self):
            self.result = bytes(self.powershell_process.readAllStandardOutput()).decode("cp858")
            print(self.result)
            countFiles = self.result.split("\r\n")[:-1]
            for countFile in countFiles:
                self.copiedFiles +=1
            self.pb.setValue(self.copiedFiles/self.data["nbFiles"]*100)
            self.copiedFilesLEdit.setText(f'{self.copiedFiles} for {self.data["nbFiles"]}')
     
        def cleanup_powershell_process(self):
            self.powershell_process = None
            self.copyFinished.emit(self.data)
            self.close()

    Bonne journée à toi

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

    Si vous écrivez "self.powershell_process.waitForFinished(-1)", le GUI se gèle en attendant que le Process se termine.

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

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

Discussions similaires

  1. PySide6 - StyleSheet dans fichier .qrc
    Par FadeToBlack dans le forum PyQt
    Réponses: 0
    Dernier message: 21/06/2021, 05h27
  2. Réponses: 1
    Dernier message: 19/02/2014, 23h19
  3. Problème d'appel d'un slot dans un QThread
    Par petitclem dans le forum Multithreading
    Réponses: 9
    Dernier message: 20/03/2012, 09h51
  4. [QThread] Interception de std::signal dans un QThread
    Par uriotcea dans le forum Multithreading
    Réponses: 0
    Dernier message: 01/08/2010, 11h55
  5. [QThread] QProcess et QSemaphore
    Par slymira dans le forum Multithreading
    Réponses: 11
    Dernier message: 29/11/2007, 11h40

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