PyQt的进度(PyQt ProgressBar)

2019-07-03 15:40发布

如果使用下面的代码我的应用程序几秒钟后档。 和摊点我的意思是挂起。 我收到从Windows话说等待或强制关闭的窗口。

我想补充一点,当我单击进度条窗口内或当我点击它外,使它失去焦点,这只是发生。 如果我启动例子,不碰任何东西它像它应该。

from PyQt4 import QtCore
from PyQt4 import QtGui


class ProgressBar(QtGui.QWidget):
    def __init__(self, parent=None, total=20):
        super(ProgressBar, self).__init__(parent)
        self.name_line = QtGui.QLineEdit()

        self.progressbar = QtGui.QProgressBar()
        self.progressbar.setMinimum(1)
        self.progressbar.setMaximum(total)

        main_layout = QtGui.QGridLayout()
        main_layout.addWidget(self.progressbar, 0, 0)

        self.setLayout(main_layout)
        self.setWindowTitle("Progress")

    def update_progressbar(self, val):
        self.progressbar.setValue(val)   

使用这种像这样:

app = QtGui.QApplication(sys.argv)
bar = ProgressBar(total=101)
bar.show()

for i in range(2,100):
    bar.update_progressbar(i)
    time.sleep(1)

谢谢你的帮助。

Answer 1:

您需要允许而循环运行,这样应用程序可以保持响应处理的事件。

更重要的是,对于长时间运行的任务,您需要提供用户一旦它开始停止循环的方式。

一个简单的方法做,这是开始一个计时器循环,然后定期调用qApp.processEvents而循环运行。

这里有一个演示脚本,它是:

import sys, time
from PyQt4 import QtGui, QtCore

class ProgressBar(QtGui.QWidget):
    def __init__(self, parent=None, total=20):
        super(ProgressBar, self).__init__(parent)
        self.progressbar = QtGui.QProgressBar()
        self.progressbar.setMinimum(1)
        self.progressbar.setMaximum(total)
        self.button = QtGui.QPushButton('Start')
        self.button.clicked.connect(self.handleButton)
        main_layout = QtGui.QGridLayout()
        main_layout.addWidget(self.button, 0, 0)
        main_layout.addWidget(self.progressbar, 0, 1)
        self.setLayout(main_layout)
        self.setWindowTitle('Progress')
        self._active = False

    def handleButton(self):
        if not self._active:
            self._active = True
            self.button.setText('Stop')
            if self.progressbar.value() == self.progressbar.maximum():
                self.progressbar.reset()
            QtCore.QTimer.singleShot(0, self.startLoop)
        else:
            self._active = False

    def closeEvent(self, event):
        self._active = False

    def startLoop(self):
        while True:
            time.sleep(0.05)
            value = self.progressbar.value() + 1
            self.progressbar.setValue(value)
            QtGui.qApp.processEvents()
            if (not self._active or
                value >= self.progressbar.maximum()):
                break
        self.button.setText('Start')
        self._active = False

app = QtGui.QApplication(sys.argv)
bar = ProgressBar(total=101)
bar.show()
sys.exit(app.exec_())

UPDATE

假设你正在使用的C语言实现的Python(即CPython的),解决这个问题完全取决于必须与GUI同时运行的任务(一个或多个)的性质。 更重要的是,它是由具有确定的CPython 全局解释器锁(GIL) 。

我不打算尝试的CPython的GIL的任何解释;相反,我会简单地推荐看这部优秀PYCON视频由Dave比兹利,并留在这一点。


一般情况下,试图用一个后台任务同时运行GUI的时候,第一个要问的问题是:是任务IO的限制,或CPU绑定?

如果是IO的限制(例如,访问本地文件系统,从互联网上下载,等等),那么解决方案通常是非常简单的,因为CPython的总释放GIL的I / O操作。 后台任务可以简单地实现异步 ,或由执行工作线程 ,并做什么特别的需求,保证GUI作出响应。

的主要困难与发生CPU密集型任务,当有第二个问题要问:可以将任务分解成一系列小步骤?

如果可以,那么解决的办法是定期发送请求到GUI线程来处理目前的未决事件的堆栈。 上述演示脚本是该技术的粗例子。 更通常,所述任务将在一个单独的工作线程,作为任务的各步骤完成,其将发射的GUI更新信号来进行。 (注:它确保了工作线程不会尝试任何GUI相关的操作本身是很重要的)。

但是,如果任务不能被分解成小的步骤,那么没有通常线程型的解决方案会奏效。 该GUI将只是冻结,直到任务已经完成,线程是否使用与否。

对于这最后的情况下,唯一的解决办法是使用一个单独的进程 ,而不是一个单独的线程-即利用的多模块。 当然,如果目标系统具有多个可用的CPU内核,该解决方案才会有效。 如果只有一个CPU核心一起玩,还有基本上没有什么可以做,以帮助(而不是切换到不同的Python实现,或以不同的语言完全)。



Answer 2:

当你GUI编程需要使用某种形式的多线程的,你将有一个工作线程和一个更新GUI和响应鼠标和键盘事件。 有许多方法可以做到这一点。 我建议你看看这个QProgressBar教程有如何使用QProgressBar一个很好的工作示例。

在本教程中他们使用QBasicTimer是办法得到控制回主线程,以便它可以以GUI事件。 通过使用time.sleep在你的代码是阻止一秒钟运行的唯一线索。



文章来源: PyQt ProgressBar