Suche…


Einführung

Fortschrittsbalken sind ein wesentlicher Bestandteil der Benutzererfahrung und helfen Benutzern, einen Überblick über die verbleibende Zeit für einen bestimmten Prozess zu erhalten, der auf der GUI ausgeführt wird. Dieses Thema behandelt die Grundlagen der Implementierung einer Fortschrittsleiste in Ihrer eigenen Anwendung.

Dieses Thema wird leicht auf QThread und den neuen Mechanismus für Signale / Slots eingehen. Einige Grundkenntnisse der PyQt5-Widgets werden auch von Lesern erwartet.

Verwenden Sie beim Hinzufügen von Beispielen nur die integrierten PyQt5- und Python-Ins, um die Funktionalität zu demonstrieren.

Nur PyQt5

Bemerkungen

Das Experimentieren mit diesen Beispielen ist der beste Weg, um mit dem Lernen zu beginnen.

Grundlegende PyQt-Fortschrittsleiste

Dies ist eine sehr grundlegende Fortschrittsanzeige, die nur das Nötigste verwendet.

Es wäre klug, dieses ganze Beispiel bis zum Ende zu lesen.

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_())

Die Fortschrittsleiste wird zuerst so importiert wie from PyQt5.QtWidgets import QProgressBar

Dann wird es wie jedes andere Widget in QtWidgets

Die self.progress.setGeometry(0, 0, 300, 25) definiert die self.progress.setGeometry(0, 0, 300, 25) x,y Positionen im Dialogfeld sowie Breite und Höhe der Fortschrittsleiste.

Dann bewegen wir den Button mit .move() um 30px nach unten, so dass zwischen den beiden Widgets ein Abstand von 5px .

Hier wird self.progress.setValue(count) verwendet, um den Fortschritt zu aktualisieren. Wenn Sie mit .setMaximum() einen Maximalwert .setMaximum() werden die Werte automatisch für Sie berechnet. Wenn der Maximalwert beispielsweise auf 50 TIME_LIMIT ist, TIME_LIMIT 100 und TIME_LIMIT von 0 auf 2 bis 4 Prozent statt von 0 auf 1 bis 2 pro Sekunde. Sie können auch einen Mindestwert .setMinimum() indem Sie .setMinimum() , um den Fortschrittsbalken zu zwingen, von einem bestimmten Wert zu starten.

Wenn Sie dieses Programm ausführen, wird eine ähnliche GUI erzeugt.

Fortschrittsbalken-Dialog reagiert nicht

Wie Sie sehen, wird die GUI definitiv einfrieren und reagiert nicht, bis der Zähler die TIME_LIMIT Bedingung erfüllt. Dies liegt daran, dass time.sleep das Betriebssystem zu der Überzeugung bringt, dass das Programm in einer Endlosschleife time.sleep geblieben ist.

QThread

Wie überwinden wir dieses Problem? Wir können die von PyQt5 bereitgestellte Threading-Klasse verwenden.

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_())

Lassen Sie uns diese Modifikationen abbauen.

from PyQt5.QtCore import QThread, pyqtSignal

Diese Zeile importiert Qthread , eine PyQt5 Implementierung, um einige Teile (z. B. Funktionen, Klassen) eines Programms im Hintergrund zu teilen und auszuführen (auch als Multithreading bekannt). Diese Teile werden auch als Threads bezeichnet. Alle PyQt5 Programme verfügen standardmäßig über einen Haupt-Thread und die anderen (Worker-Threads) werden verwendet, um zusätzliche zeitaufwendige und prozessintensive Aufgaben in den Hintergrund zu verlagern, während das Hauptprogramm weiterhin funktioniert.

Das zweite Import- pyqtSignal wird verwendet, um Daten (Signale) zwischen pyqtSignal und Main-Threads zu senden. In diesem Fall werden wir es verwenden, um dem Haupt-Thread mitzuteilen, dass der Fortschrittsbalken aktualisiert werden soll.

Nun haben wir die while-Schleife für den Zähler in eine separate Klasse namens External verschoben.

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)

Durch die Unterklassifizierung von QThread wir im Wesentlichen External in eine Klasse, die in einem separaten Thread ausgeführt werden kann. Darüber hinaus können Threads jederzeit gestartet oder gestoppt werden, was die Vorteile erhöht.

countChanged ist der aktuelle Fortschritt, und pyqtSignal(int) teilt dem Worker-Thread mit, dass das gesendete Signal vom Typ int . self.countChanged.emit(count) sendet das Signal einfach an alle Verbindungen im Haupt-Thread (normalerweise kann es auch mit anderen Worker-Threads kommunizieren).

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

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

Wenn Sie auf die Schaltfläche self.onButtonClick wird self.onButtonClick ausgeführt und der Thread gestartet. Der Thread wird mit .start() gestartet. Es sollte auch beachtet werden, dass wir das self.calc.countChanged erstellte Signal self.calc.countChanged mit der zum Aktualisieren des Fortschrittsbalkenwerts verwendeten Methode verbunden haben. Bei jeder Aktualisierung von External::run::count wird der int Wert auch an onCountChanged gesendet.

So könnte die GUI nach diesen Änderungen aussehen.

QThread-Fortschrittsbalken

Es sollte sich auch viel ansprechender anfühlen und wird nicht einfrieren.



Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow