pyqt5
Wprowadzenie do pasków postępu
Szukaj…
Wprowadzenie
Paski postępu są integralną częścią doświadczenia użytkownika i pomagają użytkownikom zorientować się, ile czasu pozostało do wykonania danego procesu uruchomionego w interfejsie GUI. W tym temacie omówiono podstawy wdrażania paska postępu we własnej aplikacji.
Temat ten delikatnie dotknie QThread i nowego mechanizmu sygnałów / gniazd. Od czytelników oczekuje się także podstawowej wiedzy na temat widżetów PyQt5.
Podczas dodawania przykładów używaj tylko wbudowanych PyQt5 i Python, aby zademonstrować funkcjonalność.
Tylko PyQt5
Uwagi
Eksperymentowanie z tymi przykładami jest najlepszym sposobem na rozpoczęcie nauki.
Podstawowy pasek postępu PyQt
Jest to bardzo podstawowy pasek postępu, który wykorzystuje tylko to, co jest potrzebne, na minimum.
Mądrze byłoby przeczytać cały ten przykład do końca.
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_())
Pasek postępu jest najpierw importowany tak jak from PyQt5.QtWidgets import QProgressBar
Następnie jest inicjowany jak każdy inny widget w QtWidgets
Metoda liniowa self.progress.setGeometry(0, 0, 300, 25)
definiuje pozycje x,y
w oknie dialogowym oraz szerokość i wysokość paska postępu.
Następnie przesuwamy przycisk za pomocą .move()
o 30 30px
dół, aby między dwoma 5px
była przerwa 5 5px
.
Tutaj self.progress.setValue(count)
służy do aktualizacji postępu. Ustawienie maksymalnej wartości za pomocą .setMaximum()
spowoduje również automatyczne obliczenie wartości. Na przykład, jeśli maksymalna wartość jest ustawiona na 50, to ponieważ TIME_LIMIT
wynosi 100, przeskakuje od 0 do 2 do 4 procent zamiast 0 do 1 do 2 co sekundę. Możesz także ustawić minimalną wartość za pomocą .setMinimum()
zmuszając pasek postępu do uruchomienia od określonej wartości.
Uruchomienie tego programu spowoduje utworzenie GUI podobnego do tego.
Jak widać, GUI z całą pewnością zawiesi się i nie będzie odpowiadać, dopóki licznik nie spełni warunku TIME_LIMIT
. Wynika to z faktu, że time.sleep
powoduje, że system operacyjny wierzy, że program utknął w nieskończonej pętli.
QThread
Jak więc rozwiązać ten problem? Możemy użyć klasy wątków zapewnianej przez 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_())
Podzielmy te modyfikacje.
from PyQt5.QtCore import QThread, pyqtSignal
Ta linia importuje Qthread
który jest implementacją PyQt5
celu podzielenia i uruchomienia niektórych części (np. Funkcji, klas) programu w tle (znanego również jako wielowątkowość). Te części są również nazywane wątkami. Wszystkie programy PyQt5
domyślnie mają główny wątek, a pozostałe (wątki PyQt5
) są wykorzystywane do odciążania dodatkowego czasu i przetwarzania zadań w tle, przy jednoczesnym utrzymaniu działania głównego programu.
Drugi import pyqtSignal
służy do wysyłania danych (sygnałów) między pyqtSignal
a głównym. W tym przypadku użyjemy go, aby poinformować główny wątek o aktualizacji paska postępu.
Teraz przenieśliśmy pętlę while licznika do osobnej klasy o nazwie 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
zasadniczo konwertujemy External
na klasę, którą można uruchomić w osobnym wątku. Wątki można również uruchamiać i zatrzymywać w dowolnym momencie, zwiększając ich zalety.
Tutaj countChanged
to bieżący postęp, a pyqtSignal(int)
informuje wątek roboczy, że wysyłany sygnał jest typu int
. Chociaż self.countChanged.emit(count)
po prostu wysyła sygnał do dowolnych połączeń w głównym wątku (zwykle może być używany do komunikacji z innymi wątkami self.countChanged.emit(count)
).
def onButtonClick(self):
self.calc = External()
self.calc.countChanged.connect(self.onCountChanged)
self.calc.start()
def onCountChanged(self, value):
self.progress.setValue(value)
Po kliknięciu przycisku uruchomi się self.onButtonClick
, a także rozpocznie się wątek. Wątek jest uruchamiany za pomocą .start()
. Należy również zauważyć, że podłączyliśmy utworzony wcześniej sygnał self.calc.countChanged
do metody użytej do aktualizacji wartości paska postępu. Za każdym razem, gdy External::run::count
jest aktualizowany, wartość int
jest również wysyłana do onCountChanged
.
Tak może wyglądać GUI po wprowadzeniu tych zmian.
Powinien także czuć się bardziej responsywny i nie zawiesza się.