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.

La finestra di dialogo Barra di avanzamento non risponde

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.

Barra di avanzamento di QThread

Dovrebbe anche sentirsi molto più reattivo e non si bloccherà.



Modified text is an extract of the original Stack Overflow Documentation
Autorizzato sotto CC BY-SA 3.0
Non affiliato con Stack Overflow