Zoeken…


Invoering

Voortgangsbalken zijn een integraal onderdeel van de gebruikerservaring en helpen gebruikers een idee te krijgen van de resterende tijd voor een bepaald proces dat op de GUI draait. Dit onderwerp behandelt de basisprincipes van het implementeren van een voortgangsbalk in uw eigen toepassing.

Dit onderwerp raakt licht aan QThread en het nieuwe signaal / slots-mechanisme. Enige basiskennis van PyQt5-widgets wordt ook van lezers verwacht.

Gebruik bij het toevoegen van voorbeelden alleen ingebouwde PyQt5 en Python om de functionaliteit te demonstreren.

Alleen PyQt5

Opmerkingen

Experimenteren met deze voorbeelden is de beste manier om te beginnen met leren.

Basic PyQt voortgangsbalk

Dit is een zeer eenvoudige voortgangsbalk die alleen gebruikt wat nodig is op het absolute minimum.

Het is verstandig dit hele voorbeeld tot het einde te lezen.

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

De voortgangsbalk wordt eerst zo geïmporteerd from PyQt5.QtWidgets import QProgressBar

Vervolgens wordt het geïnitialiseerd zoals elke andere widget in QtWidgets

De self.progress.setGeometry(0, 0, 300, 25) definieert de x,y posities in het dialoogvenster en de breedte en hoogte van de voortgangsbalk.

Vervolgens verplaatsen we de knop met .move() met 30 30px omlaag zodat er een opening van 5 5px tussen de twee widgets is.

Hier wordt self.progress.setValue(count) gebruikt om de voortgang bij te werken. Als u een maximale waarde .setMaximum() met .setMaximum() worden de waarden ook automatisch voor u berekend. Als de maximale waarde bijvoorbeeld is ingesteld op 50, TIME_LIMIT aangezien TIME_LIMIT 100 is, van 0 tot 2 tot 4 procent in plaats van 0 tot 1 tot 2 elke seconde. U kunt ook een minimumwaarde instellen met .setMinimum() waardoor de voortgangsbalk wordt gedwongen te starten vanaf een bepaalde waarde.

Het uitvoeren van dit programma zal een vergelijkbare GUI produceren.

Dialoogvenster Voortgangsbalk reageert niet

Zoals u kunt zien, zal de GUI zeker vastlopen en niet reageren totdat de teller voldoet aan de voorwaarde TIME_LIMIT . Dit komt omdat time.sleep ervoor zorgt dat het besturingssysteem denkt dat het programma vastloopt in een oneindige lus.

QThread

Dus hoe kunnen we dit probleem oplossen? We kunnen de threading-klasse gebruiken die PyQt5 biedt.

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

Laten we deze wijzigingen afbreken.

from PyQt5.QtCore import QThread, pyqtSignal

Deze regel importeert Qthread , een PyQt5 implementatie om sommige delen (bijvoorbeeld: functies, klassen) van een programma op de achtergrond te verdelen en uit te voeren (ook bekend als multi-threading). Deze delen worden ook threads genoemd. Alle PyQt5 programma's hebben standaard een PyQt5 en de andere (werkthreads) worden gebruikt om extra tijdrovende en intensieve taken naar de achtergrond te PyQt5 terwijl het hoofdprogramma nog steeds functioneert.

Het tweede pyqtSignal wordt gebruikt om gegevens (signalen) te verzenden tussen de werk- en pyqtSignal . In dit geval zullen we het gebruiken om de hoofdthread te vertellen de voortgangsbalk bij te werken.

Nu hebben we de while-lus voor de teller verplaatst naar een aparte klasse genaamd 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)

Door QThread onder te classificeren, converteren we in wezen External naar een klasse die in een afzonderlijke thread kan worden uitgevoerd. Threads kunnen ook op elk moment worden gestart of gestopt, wat bijdraagt aan de voordelen ervan.

Hier is countChanged de huidige voortgang en pyqtSignal(int) vertelt de werkthread dat het signaal dat wordt verzonden van het type int . Terwijl self.countChanged.emit(count) het signaal eenvoudigweg naar alle verbindingen in de self.countChanged.emit(count) verzendt (normaal kan dit ook worden gebruikt om met andere werkthreads te communiceren).

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

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

Wanneer op de knop wordt geklikt, wordt self.onButtonClick uitgevoerd en wordt de thread gestart. De thread wordt gestart met .start() . Het moet ook worden opgemerkt dat we het signaal self.calc.countChanged we eerder hebben gemaakt, hebben verbonden met de methode die wordt gebruikt om de voortgangsbalkwaarde bij te werken. Telkens wanneer External::run::count wordt bijgewerkt, wordt de int waarde ook verzonden naar onCountChanged .

Dit is hoe de GUI eruit zou kunnen zien na het aanbrengen van deze wijzigingen.

QThread-voortgangsbalk

Het zou ook veel sneller moeten reageren en niet bevriezen.



Modified text is an extract of the original Stack Overflow Documentation
Licentie onder CC BY-SA 3.0
Niet aangesloten bij Stack Overflow