PyQt ProgressBar [英] PyQt ProgressBar

查看:214
本文介绍了PyQt ProgressBar的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

使用以下代码时,我的应用程序在几秒钟后停顿. 我指的是档位.我从Windows看到一个窗口,说等待或强制关闭.

When using the following code my application stalls after a couple of seconds. And by stalls I mean hangs. I get a window from Windows saying wait or force close.

我可能会补充说,只有当我在进度条窗口中单击或在其外部单击时,才会发生这种情况,从而失去焦点.如果我以该示例为起点,并且不进行任何操作,那么它应该可以正常工作.

I might add that this only happens when I click either inside the progress bar window or when I click outside of it so it loses focus. If I start the example and do not touch anything it works like it should.

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)

感谢您的帮助.

推荐答案

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

You need to allow events to be processed whilst the loop is running so that the application can remain responsive.

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

Even more importantly, for long-running tasks, you need to provide a way for the user to stop the loop once it's started.

一种简单的方法是使用计时器启动循环,然后定期调用 qApp.processEvents .

One simple way to do this is to start the loop with a timer, and then periodically call qApp.processEvents whilst the loop is running.

这是一个演示脚本,可以执行以下操作:

Here's a demo script that does that:

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同时运行的任务的性质. .从根本上说,它是由CPython使用 Global Interpreter Lock( GIL).

Assuming that you're using the C implementation of python (i.e. CPython), the solution to this issue depends entirely on the nature of the task(s) that have to run concurrently with the GUI. More fundamentally, it is determined by CPython having a Global Interpreter Lock (GIL).

我不会尝试对CPython的GIL进行任何解释:相反,我只建议您观看此出色的

I am not going to attempt any explanation of CPython's GIL: instead, I will simply recommend watching this excellent PyCon video by Dave Beazley, and leave it at that.

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

Generally, when trying to run a GUI concurrently with a background task, the first question to ask is: Is the task IO-bound, or CPU-bound?

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

If it's IO-bound (e.g. accessing the local file-system, downloading from the internet, etc), then the solution is usually quite straightforward, because CPython always releases the GIL for I/O operations. The background task can simply be done asynchronously, or performed by a worker thread, and nothing special needs to be done to keep the GUI responsive.

与CPU绑定的任务遇到的主要困难是,还有第二个问题要问:可以将任务分解为一系列小步骤吗?

The main difficulties occur with CPU-bound tasks, when there is a second question to ask: Can the task be broken down into a series of small steps?

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

If it can, then the solution is to periodically send requests to the GUI thread to process its current stack of pending events. The demo script above is a crude example of this technique. More usually, the task would be carried out in a separate worker thread, which would emit a gui-update signal as each step of the task is completed. (NB: it's important to ensure that the worker thread never attempts any GUI-related operations itself).

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

But if the task can't be broken down into small steps, then none of the usual threading-type solutions will work. The GUI will just freeze until the task has been completed, whether threads are used or not.

对于此最终方案,唯一的解决方案是使用单独的进程而不是单独的线程-即,使用

For this final scenario, the only solution is to use a separate process, rather than a separate thread - i.e. make use of the multiprocessing module. Of course, this solution will only be effective if the target system has multiple CPU cores available. If there's only one CPU core to play with, there's basically nothing that can be done to help (other than switching to a different implementation of Python, or to a different language altogether).

这篇关于PyQt ProgressBar的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆