Python pyqt脉冲进度条多线程

时间:2017-07-18 03:31:21

标签: python python-2.7 pyqt progress-bar pyqt4

因为我是初学者,请忍受我的问题。我在pyqt中实现进度条时遇到了问题,我所看到的所有示例都没有真正解释如何正确实现它,并且从exampleexample我有点部分地做了它工作,但它仍然挂起。我有这段代码:

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

这里的所有示例和帖子对于理解进度条实现非常有帮助,但是所有示例都针对特定问题具有特定答案,我无法理解标准pyqt应用程序中进度条的实现。你们这些人至少能指出我正确的方向吗?将不胜感激。

2 个答案:

答案 0 :(得分:1)

这是一个非常基本的进度条,只使用最低限度的需要。

将这整个例子读到最后是明智的。

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

首先导入进度条,如from PyQt5.QtWidgets import QProgressBar

然后它会像QtWidgets

中的任何其他窗口小部件一样初始化

self.progress.setGeometry(0, 0, 300, 25)方法定义对话框上的x,y位置以及进度条的宽度和高度。

然后我们使用.move()按下30px向下移动按钮,这样两个小部件之间就会有5px的差距。

此处self.progress.setValue(count)用于更新进度。使用.setMaximum()设置最大值也会自动为您计算值。例如,如果最大值设置为50,那么由于TIME_LIMIT为100,它将从0跳到2到4%而不是每秒从0跳到1到2。您还可以使用.setMinimum()强制进度条从给定值开始设置最小值。

执行此程序将生成类似于此的GUI。

Progress Bar Dialog Not Responding

正如您所看到的,在计数器满足TIME_LIMIT条件之前,GUI肯定会冻结并且无响应。这是因为time.sleep导致操作系统认为程序已陷入无限循环。

<强>的QThread

那么我们如何克服这个问题呢?我们可以使用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_())

让我们分解这些修改。

from PyQt5.QtCore import QThread, pyqtSignal

此行导入Qthread这是一个PyQt5实现,用于在后台划分和运行程序的某些部分(例如:函数,类)(也称为多线程)。这些部分也称为线程。默认情况下,所有PyQt5程序都有一个主线程,其他(工作线程)用于将额外的时间和过程密集型任务卸载到后台,同时仍然保持主程序的正常运行。

第二个导入pyqtSignal用于在工作线程和主线程之间发送数据(信号)。在这个例子中,我们将使用它来告诉主线程更新进度条。

现在我们已将计数器的while循环移动到一个名为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进行子类化,我们实际上将External转换为可以在单独的线程中运行的类。线程也可以在任何时候启动或停止,从而增加它的好处。

此处countChanged是当前进度,pyqtSignal(int)告诉工作线程发送的信号类型为int。而self.countChanged.emit(count)只是将信号发送到主线程中的任何连接(通常它也可用于与其他工作线程通信)。

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

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

单击该按钮时,self.onButtonClick将运行并启动该线程。该线程以.start()启动。还应该注意,我们将之前创建的信号self.calc.countChanged连接到用于更新进度条值的方法。每次更新External::run::count时,int值也会发送到onCountChanged

这是GUI在进行这些更改后的外观。

QThread Progress Bar

它也应该感觉更敏感,不会冻结。

答案 1 :(得分:1)

我自己的问题的答案。如果你能理解线程的概念并通过类传递变量,那就不难了。我的第一个错误实际上是缺乏关于Worker线程的知识,其次是我认为一旦你声明Thread它意味着你只需要调用它以便它将在主要内部运行函数所以我正在寻找你将如何实现这一点以及我认为错误的一切。

<强>解决方案

所有硬/长进程应该位于QThread下的子类def run中,应该在class Window(QtGui.QMainWindow):或主循环中调用,这就是我的代码现在看起来像

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

如果这个答案中的某些内容是错误的,那么请纠正我,因为这样可以更好地理解它或者某些东西,但不是