Buscar..


Introducción

Las barras de progreso son una parte integral de la experiencia del usuario y ayudan a los usuarios a tener una idea del tiempo que queda para un proceso determinado que se ejecuta en la GUI. Este tema tratará los aspectos básicos de la implementación de una barra de progreso en su propia aplicación.

Este tema tratará ligeramente sobre QThread y el nuevo mecanismo de señales / ranuras. También se espera un cierto conocimiento básico de los widgets de PyQt5 de los lectores.

Cuando se agregan ejemplos, solo se utilizan los complementos PyQt5 y Python para demostrar la funcionalidad.

Sólo PyQt5

Observaciones

Experimentar con estos ejemplos es la mejor manera de comenzar a aprender.

Barra de progreso de PyQt básica

Esta es una barra de progreso muy básica que solo usa lo que se necesita como mínimo.

Sería prudente leer todo este ejemplo hasta el final.

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 de progreso se importa primero from PyQt5.QtWidgets import QProgressBar

Luego se inicializa como cualquier otro widget en QtWidgets

El self.progress.setGeometry(0, 0, 300, 25) línea self.progress.setGeometry(0, 0, 300, 25) define las posiciones x,y en el cuadro de diálogo y el ancho y alto de la barra de progreso.

Luego movemos el botón usando .move() en 30px hacia abajo para que haya un espacio de 5px entre los dos widgets.

Aquí self.progress.setValue(count) se usa para actualizar el progreso. Establecer un valor máximo utilizando .setMaximum() también calculará automáticamente los valores para usted. Por ejemplo, si el valor máximo se establece en 50, dado que TIME_LIMIT es 100 TIME_LIMIT de 0 a 2 a 4 por ciento en lugar de 0 a 1 a 2 cada segundo. También puede establecer un valor mínimo usando .setMinimum() forzando que la barra de progreso comience a partir de un valor dado.

La ejecución de este programa producirá una GUI similar a esta.

El diálogo de la barra de progreso no responde

Como puede ver, la GUI definitivamente se congelará y no responderá hasta que el contador cumpla con la condición TIME_LIMIT . Esto se debe a que time.sleep hace que el sistema operativo crea que el programa se ha atascado en un bucle infinito.

Qhilo

Entonces, ¿cómo superamos este problema? Podemos usar la clase de subprocesos que proporciona 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_())

Vamos a desglosar estas modificaciones.

from PyQt5.QtCore import QThread, pyqtSignal

Esta línea importa Qthread que es una implementación de PyQt5 para dividir y ejecutar algunas partes (por ejemplo: funciones, clases) de un programa en segundo plano (también conocido como multihilo). Estas partes también se llaman hilos. PyQt5 defecto, todos los programas de PyQt5 tienen un subproceso principal y los otros (subprocesos de trabajo) se utilizan para descargar más tiempo y procesar tareas intensivas en segundo plano mientras se mantiene el funcionamiento del programa principal.

La segunda importación pyqtSignal se utiliza para enviar datos (señales) entre los procesos de trabajo y los subprocesos principales. En este caso, lo utilizaremos para indicar al subproceso principal que actualice la barra de progreso.

Ahora hemos movido el bucle while para el contador a una clase separada llamada 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 esencialmente estamos convirtiendo External en una clase que puede ejecutarse en un hilo separado. Los hilos también pueden iniciarse o detenerse en cualquier momento, lo que se suma a sus beneficios.

Aquí countChanged es el progreso actual y pyqtSignal(int) le dice al subproceso de trabajo que la señal que se envía es de tipo int . Mientras que self.countChanged.emit(count) simplemente envía la señal a cualquier conexión en el subproceso principal (normalmente, también se puede usar para comunicarse con otros subprocesos de trabajo).

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

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

Cuando se hace clic en el botón, self.onButtonClick se ejecutará y también iniciará el hilo. El hilo comienza con .start() . También se debe tener en cuenta que conectamos la señal self.calc.countChanged que creamos anteriormente al método utilizado para actualizar el valor de la barra de progreso. Cada vez que External::run::count se actualiza, el valor int también se envía a onCountChanged .

Así es como podría verse la GUI después de realizar estos cambios.

QThread barra de progreso

También debe sentirse mucho más sensible y no se congelará.



Modified text is an extract of the original Stack Overflow Documentation
Licenciado bajo CC BY-SA 3.0
No afiliado a Stack Overflow