pyqt5
Introduzione alle barre di avanzamento
Ricerca…
introduzione
Le barre di avanzamento sono parte integrante dell'esperienza utente e aiutano gli utenti a farsi un'idea sul tempo rimanente per un determinato processo eseguito sulla GUI. Questo argomento tratterà delle nozioni di base sull'implementazione di una barra di avanzamento nella propria applicazione.
Questo argomento toccherà leggermente su QThread e il nuovo meccanismo segnali / slot. Ci si aspetta anche una certa conoscenza di base dei widget PyQt5 dei lettori.
Quando si aggiungono solo esempi, si utilizzano Python e Python per dimostrarne la funzionalità.
Solo PyQt5
Osservazioni
Sperimentare con questi esempi è il modo migliore per iniziare a imparare.
Barra di avanzamento PyQt di base
Questa è una barra di avanzamento molto semplice che utilizza solo ciò che è necessario al minimo indispensabile.
Sarebbe saggio leggere questo intero esempio fino alla fine.
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 barra di avanzamento viene prima importata in questo modo from PyQt5.QtWidgets import QProgressBar
Quindi viene inizializzato come qualsiasi altro widget in QtWidgets
La linea self.progress.setGeometry(0, 0, 300, 25)
metodo definisce le posizioni x,y
sulla finestra di dialogo e la larghezza e l'altezza della barra di avanzamento.
Quindi spostiamo il pulsante usando .move()
di 30px
verso il basso in modo che ci sia uno spazio di 5px
tra i due widget.
Qui self.progress.setValue(count)
viene utilizzato per aggiornare lo stato di avanzamento. L'impostazione di un valore massimo utilizzando .setMaximum()
calcolerà automaticamente anche i valori per te. Ad esempio, se il valore massimo è impostato su 50, poiché TIME_LIMIT
è 100, esso salterà da 0 a 2 al 4 percento anziché da 0 a 1 a 2 ogni secondo. È anche possibile impostare un valore minimo usando .setMinimum()
forzando la barra di avanzamento a partire da un dato valore.
L'esecuzione di questo programma produrrà una GUI simile a questa.
Come puoi vedere, la GUI si bloccherà definitivamente e non risponderà finché il contatore non raggiungerà la condizione TIME_LIMIT
. Questo perché time.sleep
fa credere al sistema operativo che il programma si sia bloccato in un ciclo infinito.
QThread
Quindi, come possiamo superare questo problema? Possiamo usare la classe di threading fornita da 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_())
Analizziamo queste modifiche.
from PyQt5.QtCore import QThread, pyqtSignal
Questa riga importa Qthread
che è un'implementazione PyQt5
per dividere ed eseguire alcune parti (es .: funzioni, classi) di un programma in background (noto anche come multi-threading). Queste parti sono anche chiamate thread. Tutti i programmi PyQt5
hanno per impostazione predefinita un thread principale e gli altri (thread di lavoro) sono utilizzati per scaricare più tempo e attività di elaborazione intensiva in background mantenendo il funzionamento del programma principale.
Il secondo import pyqtSignal
viene utilizzato per inviare dati (segnali) tra worker e thread principali. In questo caso lo useremo per dire al thread principale di aggiornare la barra di avanzamento.
Ora abbiamo spostato il ciclo while per il contatore in una classe separata chiamata 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)
QThread
stiamo essenzialmente convertendo External
in una classe che può essere eseguita in un thread separato. I thread possono anche essere avviati o fermati in qualsiasi momento aggiungendo vantaggi.
Qui countChanged
è l'avanzamento corrente e pyqtSignal(int)
dice al thread worker che il segnale inviato è di tipo int
. Mentre self.countChanged.emit(count)
invia semplicemente il segnale a qualsiasi connessione nel thread principale (normalmente può anche essere usato per comunicare con altri thread worker).
def onButtonClick(self):
self.calc = External()
self.calc.countChanged.connect(self.onCountChanged)
self.calc.start()
def onCountChanged(self, value):
self.progress.setValue(value)
Quando si fa clic sul self.onButtonClick
, self.onButtonClick
verrà eseguito e inizierà anche il thread. Il thread è avviato con .start()
. Va anche notato che abbiamo collegato il segnale self.calc.countChanged
creato in precedenza al metodo utilizzato per aggiornare il valore della barra di avanzamento. Ogni volta che External::run::count
viene aggiornato, il valore int
viene anche inviato a onCountChanged
.
Ecco come potrebbe essere la GUI dopo aver apportato queste modifiche.
Dovrebbe anche sentirsi molto più reattivo e non si bloccherà.