pyqt5
Introduction aux barres de progression
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.
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.
Il devrait également se sentir beaucoup plus réactif et ne gèlera pas.