PyQt ProgressBar

发布于 2021-01-29 18:15:27

使用以下代码时,我的应用程序会在几秒钟后停顿。我指的是档位。我从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)

谢谢你的帮助。

关注者
0
被浏览
52
1 个回答
  • 面试哥
    面试哥 2021-01-29
    为面试而生,有面试问题,就找面试哥。

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

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

    一种简单的方法是使用计时器启动循环,然后在循环运行时定期调用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的不同实现,或者完全切换到另一种语言)。



知识点
面圈网VIP题库

面圈网VIP题库全新上线,海量真题题库资源。 90大类考试,超10万份考试真题开放下载啦

去下载看看