python - PyQt 进度条

标签 python multithreading qt pyqt qprogressbar

当使用以下代码时,我的应用程序会在几秒钟后停止。 摊位我的意思是挂起。我从 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)

感谢您的帮助。

最佳答案

您需要允许在循环运行时处理事件,以便应用程序可以保持响应。

更重要的是,对于长时间运行的任务,您需要为用户提供一种在循环开始后停止循环的方法。

一个简单的方法是用计时器启动循环,然后定期调用 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_())

更新

假设您正在使用 python 的 C 实现(即 CPython),此问题的解决方案完全取决于必须与 GUI 同时运行的任务的性质.更根本的是,它由具有 Global Interpreter Lock (GIL) 的 CPython 决定。 .

我不会尝试对 CPython 的 GIL 进行任何解释:相反,我只会推荐观看这个优秀的 PyCon video戴夫·比兹利 (Dave Beazley) 的作品,就此打住。


通常,当尝试同时运行 GUI 和后台任务时,要问的第一个问题是:任务是 IO 绑定(bind)还是 CPU 绑定(bind)?

如果它是 IO 绑定(bind)的(例如访问本地文件系统、从互联网下载等),那么解决方案通常非常简单,因为 CPython 总是为 I/O 操作释放 GIL。后台任务可以简单搞定asynchronously ,或由 worker thread 执行,并且无需执行任何特殊操作即可保持 GUI 响应。

主要困难出现在 CPU 密集型任务上,当有第二个问题要问时:任务能否分解为一系列小步骤?

如果可以,那么解决方案就是定期向 GUI 线程发送请求以处理其当前的未决事件堆栈。上面的演示脚本是这种技术的粗略示例。更常见的是,该任务将在一个单独的工作线程中执行,该工作线程将在任务的每个步骤完成时发出一个 gui-update 信号。 (注意:重要的是要确保工作线程本身从不尝试任何与 GUI 相关的操作)。

但是如果任务不能被分解成小步骤,那么通常的线程类型解决方案都将不起作用。在任务完成之前,无论是否使用线程,GUI 都会卡住。

对于最后一个场景,唯一的解决方案是使用一个单独的进程,而不是一个单独的线程——即使用multiprocessing模块。当然,只有当目标系统有多个可用的 CPU 内核时,此解决方案才会有效。如果只有一个 CPU 核心可以使用,基本上没有什么可以帮助的(除了切换到不同的 Python 实现,或者完全切换到不同的语言)。

关于python - PyQt 进度条,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/13269936/

相关文章:

javascript - html 在单击按钮时从 json 数据加载更多

Python:编写接受参数的装饰器的快捷方式?

python - 从命令行强制 TensorFlow-GPU 使用 CPU

c++ - 比 std::atomic<bool> 更便宜的替代品?

c++ - 包含对象和容器的容器

python - 当第一个子字符串后有空格时,在 Python 中查找两个子字符串之间的字符串

c# - 我可以将 Async/Await 与 dbContext 一起使用吗?如果是这样,我应该什么时候使用 ConfigureAwait(false)?

java - 多线程文件处理和报告

c++ - 在两个 QWidget 实例之间共享数据

sqlite - Qt 和 sqlite 错误 26