Recherche…


Introduction

Les barres de progression font partie intégrante de l'expérience utilisateur et aident les utilisateurs à se faire une idée du temps qu'il reste pour un processus donné exécuté sur l'interface graphique. Cette rubrique passera en revue les bases de la mise en œuvre d’une barre de progression dans votre propre application.

Ce sujet abordera légèrement QThread et le nouveau mécanisme de signaux / slots. Des connaissances de base des widgets PyQt5 sont également attendues des lecteurs.

Lors de l'ajout d'exemples, utilisez uniquement les fonctions intégrées PyQt5 et Python pour démontrer la fonctionnalité.

PyQt5 seulement

Remarques

Expérimenter avec ces exemples est la meilleure façon de commencer à apprendre.

Barre de progression de base PyQt

C'est une barre de progression très basique qui n'utilise que ce qui est nécessaire au strict minimum.

Il serait sage de lire cet exemple jusqu'à la fin.

import sys
import time

from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar, QPushButton)

TIME_LIMIT = 100

class Actions(QDialog):
    """
    Simple dialog that consists of a Progress Bar and a Button.
    Clicking on the button results in the start of a timer and
    updates the progress bar.
    """
    def __init__(self):
        super().__init__()
        self.initUI()
        
    def initUI(self):
        self.setWindowTitle('Progress Bar')
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.progress.setMaximum(100)
        self.button = QPushButton('Start', self)
        self.button.move(0, 30)
        self.show()

        self.button.clicked.connect(self.onButtonClick)

    def onButtonClick(self):
        count = 0
        while count < TIME_LIMIT:
            count += 1
            time.sleep(1)
            self.progress.setValue(count)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    sys.exit(app.exec_())

La barre de progression est d'abord importée comme telle à from PyQt5.QtWidgets import QProgressBar

Ensuite, il est initialisé comme tout autre widget dans QtWidgets

La self.progress.setGeometry(0, 0, 300, 25) définit les positions x,y dans la boîte de dialogue et la largeur et la hauteur de la barre de progression.

Nous déplaçons ensuite le bouton en utilisant .move() de 30px vers le bas pour qu'il y ait un écart de 5px entre les deux widgets.

Ici, self.progress.setValue(count) est utilisé pour mettre à jour la progression. Définir une valeur maximale à l'aide de .setMaximum() calculera également automatiquement les valeurs pour vous. Par exemple, si la valeur maximale est définie sur 50, puisque TIME_LIMIT vaut 100, elle passera de 0 à 2 à 4% au lieu de 0 à 1 à 2 par seconde. Vous pouvez également définir une valeur minimale à l'aide de .setMinimum() ce qui oblige la barre de progression à partir d'une valeur donnée.

L'exécution de ce programme produira une interface graphique similaire à celle-ci.

La boîte de dialogue de la barre de progression ne répond pas

Comme vous pouvez le constater, l’interface graphique TIME_LIMIT définitivement et ne répond plus jusqu’à ce que le compteur réponde à la condition TIME_LIMIT . En effet, time.sleep fait croire au système d'exploitation que ce programme est resté bloqué dans une boucle infinie.

QThread

Alors, comment pouvons-nous surmonter ce problème? Nous pouvons utiliser la classe de threading fournie par PyQt5.

import sys
import time

from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5.QtWidgets import (QApplication, QDialog,
                             QProgressBar, QPushButton)

TIME_LIMIT = 100

class External(QThread):
    """
    Runs a counter thread.
    """
    countChanged = pyqtSignal(int)

    def run(self):
        count = 0
        while count < TIME_LIMIT:
            count +=1
            time.sleep(1)
            self.countChanged.emit(count)

class Actions(QDialog):
    """
    Simple dialog that consists of a Progress Bar and a Button.
    Clicking on the button results in the start of a timer and
    updates the progress bar.
    """
    def __init__(self):
        super().__init__()
        self.initUI()
        
    def initUI(self):
        self.setWindowTitle('Progress Bar')
        self.progress = QProgressBar(self)
        self.progress.setGeometry(0, 0, 300, 25)
        self.progress.setMaximum(100)
        self.button = QPushButton('Start', self)
        self.button.move(0, 30)
        self.show()

        self.button.clicked.connect(self.onButtonClick)

    def onButtonClick(self):
        self.calc = External()
        self.calc.countChanged.connect(self.onCountChanged)
        self.calc.start()

    def onCountChanged(self, value):
        self.progress.setValue(value)

if __name__ == "__main__":
    app = QApplication(sys.argv)
    window = Actions()
    sys.exit(app.exec_())

Décomposons ces modifications.

from PyQt5.QtCore import QThread, pyqtSignal

Cette ligne importe Qthread qui est une implémentation PyQt5 pour diviser et exécuter certaines parties (par exemple: fonctions, classes) d'un programme en arrière-plan (également appelé multi-threading). Ces parties sont également appelées threads. Tous les programmes PyQt5 par défaut ont un thread principal et les autres (threads de travail) sont utilisés pour décharger en arrière-plan des tâches intensives et traiter des tâches intensives tout en conservant le fonctionnement du programme principal.

Le second import pyqtSignal est utilisé pour envoyer des données (signaux) entre les threads de travail et principaux. Dans cette instance, nous l'utilisons pour indiquer au thread principal de mettre à jour la barre de progression.

Nous avons maintenant déplacé la boucle while pour le compteur dans une classe distincte appelée External .

class External(QThread):
    """
    Runs a counter thread.
    """
    countChanged = pyqtSignal(int)

    def run(self):
        count = 0
        while count < TIME_LIMIT:
            count +=1
            time.sleep(1)
            self.countChanged.emit(count)

En sous-classant QThread nous convertissons essentiellement External en une classe qui peut être exécutée dans un thread distinct. Les threads peuvent également être démarrés ou arrêtés à tout moment, ce qui ajoute à ses avantages.

Ici countChanged est la progression en cours et pyqtSignal(int) indique au thread de travail que le signal envoyé est de type int . Bien que self.countChanged.emit(count) envoie simplement le signal à toutes les connexions du thread principal (normalement, il peut également être utilisé pour communiquer avec d'autres threads de travail).

def onButtonClick(self):
        self.calc = External()
        self.calc.countChanged.connect(self.onCountChanged)
        self.calc.start()

def onCountChanged(self, value):
    self.progress.setValue(value)

Lorsque l'utilisateur clique sur le bouton, self.onButtonClick s'exécute et démarre également le thread. Le thread est démarré avec .start() . Il convient également de noter que nous avons connecté le signal self.calc.countChanged créé précédemment à la méthode utilisée pour mettre à jour la valeur de la barre de progression. À chaque mise à jour de External::run::count la valeur int est également envoyée à onCountChanged .

Voici comment l’interface graphique peut s’occuper de ces modifications.

Barre de progression QThread

Il devrait également se sentir beaucoup plus réactif et ne gèlera pas.



Modified text is an extract of the original Stack Overflow Documentation
Sous licence CC BY-SA 3.0
Non affilié à Stack Overflow