当前位置: 首页 > 面试题库 >

PyQt ProgressBar

斜俊
2023-03-14
问题内容

使用以下代码时,我的应用程序会在几秒钟后停顿。我指的是档位。我从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并行运行的任务的性质。从根本上讲,它是由具有全局解释器锁(GIL)的CPython确定的。

我不会尝试对CPython的GIL进行任何解释:相反,我只建议您观看Dave
Beazley的这段出色的PyCon视频,然后就此结束。

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

如果是IO绑定的(例如,访问本地文件系统,从Internet下载等),则该解决方案通常非常简单,因为CPython始终为I /
O操作发布GIL。后台任务可以简单地以异步方式完成,也可以由工作线程执行,而无需执行任何特殊操作即可使GUI保持响应。

当遇到第二个问题要问时,主要的困难出现在与CPU绑定的任务上:可以将任务分解为一系列小步骤吗?

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

但是,如果 不能 将任务分解成小步,那么所有常规的线程类型解决方案都将无法正常工作。无论是否使用线程,GUI都将冻结直到任务完成。

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



 类似资料:

相关阅读

相关文章

相关问答