2017-07-18 3 views
1

Veuillez répondre à ma question car je suis débutant. J'ai eu des problèmes de mise en œuvre de la barre de progression dans pyqt et tout l'exemple que j'ai vu n'explique pas vraiment comment l'implémenter correctement et à partir de ce example et je l'ai partiellement fait fonctionner mais ça reste bloqué. J'ai ce code:Python pyqt barre de progression pulsée avec multithread

class Window(QtGui.QMainWindow): 
    def __init__(self): 
     super(Window, self).__init__() 
     self.setGeometry(750, 450, 400, 200) 
     self.setFixedSize(self.size()) 
     btn1 = QtGui.QPushButton("Convert", self) 
     btn1.move(210,171) 
     btn1.clicked.connect(self.progbar) 

    def progbar (self): 
     self.prog_win = QDialog() 
     self.prog_win.resize(400, 100) 
     self.prog_win.setFixedSize(self.prog_win.size()) 
     self.prog_win.setWindowTitle("Processing request") 
     self.lbl = QLabel(self.prog_win) 
     self.lbl.setText("Please Wait. . .") 
     self.lbl.move(15,18) 
     self.progressBar = QtGui.QProgressBar(self.prog_win) 
     self.progressBar.resize(410, 25) 
     self.progressBar.move(15, 40) 
     self.progressBar.setRange(0,1) 
     self.myLongTask = TaskThread() 

     #I think this is where I am wrong 
     #because all of the answers here is very specific 
     #or just not for beginners 
     self.prog_win.show() 
     self.myLongTask.taskFinished.connect(self.onStart) 
     self.output_settings() 

    def onStart(self): 
     self.progressBar.setRange(0,0) 
     self.myLongTask.start() 

    def output_convert(self): 
     #very long process to convert a txt file to excel 

#My Thread 
class TaskThread(QtCore.QThread): 
    taskFinished = QtCore.pyqtSignal() 
    def run(self): 
     time.sleep(3) 
     self.taskFinished.emit() 

def run(): 
    app = QtGui.QApplication(sys.argv) 
    GUI = Window() 
    app.exec_() 
run() 

Tous les exemples et les messages ici ont été très utiles à la compréhension la mise en œuvre de la barre de progression, mais avec tout l'exemple ayant des réponses spécifiques à un problème spécifique, je ne peux pas comprendre la mise en œuvre de la barre de progression dans une application pyqt standard. Peux-tu au moins me pointer dans la bonne direction? Serait apprécié.

Répondre

0

Il s'agit d'une barre de progression très basique qui n'utilise que ce qui est nécessaire au strict minimum.

Il serait sage de lire cet exemple jusqu'à la fin.

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 barre de progression est d'abord importées comme si from PyQt5.QtWidgets import QProgressBar

Ensuite, il est initialisé comme tout autre widget dans QtWidgets

La ligne méthode self.progress.setGeometry(0, 0, 300, 25) définit les x,y positions sur le dialogue et la largeur et la hauteur de la barre de progression.

Nous déplaçons ensuite le bouton en utilisant .move() par 30px vers le bas de sorte qu'il y aura un écart de 5px entre les deux widgets.

Ici self.progress.setValue(count) est utilisé pour mettre à jour la progression. La définition d'une valeur maximale à l'aide de .setMaximum() calcule également automatiquement les valeurs pour vous. Par exemple, si la valeur maximale est définie sur 50, alors que TIME_LIMIT vaut 100, elle sautera de 0 à 2 à 4% au lieu de 0 à 1 à 2 toutes les secondes. Vous pouvez également définir une valeur minimale en utilisant .setMinimum() pour forcer la barre de progression à démarrer à partir d'une valeur donnée. L'exécution de ce programme produira une interface graphique similaire à celle-ci.

Progress Bar Dialog Not Responding

Comme vous pouvez le voir, l'interface graphique sera certainement geler et cesse de répondre jusqu'à ce que le compteur satisfait à la condition TIME_LIMIT. En effet, time.sleep fait croire au système d'exploitation que le programme s'est bloqué dans une boucle infinie.

QThread

Alors, comment pouvons-nous surmonter ce problème? Nous pouvons utiliser la classe de threading fournie par 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_()) 

Décrivons ces modifications.

from PyQt5.QtCore import QThread, pyqtSignal 

Cette importation de ligne Qthread qui est une mise en œuvre PyQt5 de diviser et d'exécuter certaines parties (par exemple: fonctions, classes) d'un programme en arrière-plan (aussi connu sous le multi-threading). Ces parties sont également appelées threads.Tous les programmes PyQt5 ont par défaut un thread principal et les autres (threads de travail) sont utilisés pour décharger des tâches très longues et fastidieuses en arrière-plan tout en maintenant le fonctionnement du programme principal.

La deuxième importation pyqtSignal est utilisée pour envoyer des données (signaux) entre les threads de travail et principaux. Dans ce cas, nous l'utiliserons pour indiquer au thread principal de mettre à jour la barre de progression.

Maintenant, nous avons déplacé la boucle while du compteur dans une classe distincte appelée 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) 

Par sous-classing QThread nous convertissons essentiellement External dans une classe qui peut être exécuté dans un thread séparé. Threads peut également être démarré ou arrêté à tout moment en ajoutant à ses avantages.

Ici countChanged est la progression en cours et pyqtSignal(int) indique au thread de travail que le signal envoyé est de type int. Bien que, self.countChanged.emit(count) envoie simplement le signal à toutes les connexions dans le thread principal (normalement il peut aussi être utilisé pour communiquer avec d'autres threads de travail).

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

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

Lorsque le bouton est cliqué sur le self.onButtonClick courront et aussi démarrer le fil. Le fil est démarré avec .start(). Il convient également de noter que nous avons connecté le signal self.calc.countChanged que nous avons créé précédemment à la méthode utilisée pour mettre à jour la valeur de la barre de progression. Chaque fois que External::run::count est mis à jour, la valeur int est également envoyée à onCountChanged.

Voici comment l'interface graphique peut prendre en charge ces modifications.

QThread Progress Bar

Il doit aussi se sentir beaucoup plus réactif et ne gèle pas.

+0

Cela m'a vraiment énormément aidé bien que j'utilise PyQt4 –

+0

@JuanCarlosAsuncion^_ ^ – daegontaven

1

La réponse à ma propre question. Ce n'est pas si difficile si vous pouvez comprendre le concept de threading et de passer des variables à travers les classes. Ma première erreur était vraiment le manque de connaissances sur les threads Worker, en second lieu, je pensais qu'une fois que vous déclarez le Thread cela signifie que vous avez juste à l'appeler de sorte qu'il exécuterait la fonction dans la classe principale, donc je cherchais comment vous mettrait en œuvre cela et tout ce que je pensais était faux.

Solution

Tous les disques/processus long DEVRAIENT être dans le sous-classé QThread sous def run et doivent être appelés dans votre boucle class Window(QtGui.QMainWindow): ou principale, ce qui est ce que mon regard de code comme maintenant

class Window(QtGui.QMainWindow): 
    def __init__(self): 
     super(Window, self).__init__() 
     self.setGeometry(750, 450, 400, 200) 
     self.setFixedSize(self.size()) 
     btn1 = QtGui.QPushButton("Convert", self) 
     btn1.move(210,171) 
     btn1.clicked.connect(self.progbar) 

    def progbar (self): 
     self.prog_win = QDialog() 
     self.prog_win.resize(400, 100) 
     self.prog_win.setFixedSize(self.prog_win.size()) 
     self.prog_win.setWindowTitle("Processing request") 
     self.lbl = QLabel(self.prog_win) 
     self.lbl.setText("Please Wait. . .") 
     self.lbl.move(15,18) 
     self.progressBar = QtGui.QProgressBar(self.prog_win) 
     self.progressBar.resize(410, 25) 
     self.progressBar.move(15, 40) 
     self.progressBar.setRange(0,1) 

     self.myLongTask = TaskThread(var = DataYouWantToPass) #initializing and passing data to QThread 
     self.prog_win.show() 
     self.onStart() #Start your very very long computation/process 
     self.myLongTask.taskFinished.connect(self.onFinished) #this won't be read until QThread send a signal i think 

    def onStart(self): 
     self.progressBar.setRange(0,0) 
     self.myLongTask.start() 

    #added this function to close the progress bar 
    def onFinished(self): 
     self.progressBar.setRange(0,1) 
     self.prog_win.close() 


#My Thread 
class TaskThread(QtCore.QThread): 
    taskFinished = QtCore.pyqtSignal() 

    #I also added this so that I can pass data between classes 
    def __init__(self, var, parent=None): 
     QThread.__init__(self, parent) 
     self.var = var 

    def run(self): 
     #very long process to convert a txt file to excel 

def run(): 
    app = QtGui.QApplication(sys.argv) 
    GUI = Window() 
    app.exec_() 
run() 

Si quelque chose dans cette réponse est faux alors s'il vous plaît corrigez-moi, car ce serait une aide précieuse pour le comprendre plus ou peut-être pas et