首页 > 解决方案 > 从发出的信号更新 progressBar 值

问题描述

从 psutil 获取 CPU 百分比并将其显示在进度条上。我能够显示 CPU 百分比的值,但它没有更新。我有一个来自 QT 设计器的名为 Progress 的文件我使用 pyuic5 将其转换为 py 文件并将其导入名为 cpuprogress.py 的主文件中,我还创建了一个名为 sysinfo 的 python 文件以获取 CPU 百分比的值并将其导入主文件文件。

import sys
from PyQt5 import QtGui, QtCore
from PyQt5.QtCore import QThread, pyqtSignal
from PyQt5 import QtWidgets
from PyQt5.QtWidgets import  (QWidget, QApplication)
from progress import Ui_Form
import sysinfo


class MyTest(QWidget, Ui_Form):
    def __init__(self, parent = None):
        super(MyTest, self).__init__(parent)
        self.setupUi(self)
        self.threadclass = ThreadCLass()
        self.threadclass.start()
        self.threadclass.change_value.connect(self.updateProgressBar)
    def updateProgressBar(self, val):
        self.progressBar.setValue(val)

class ThreadCLass(QThread):
    change_value = pyqtSignal(int)

    def __init__(self, parent = None):
        super(ThreadCLass, self).__init__(parent)
    def run(self):
        while 1:
            val = int(sysinfo.getCPU())
            self.change_value.emit(val)

a =QApplication(sys.argv)
window = QWidget()
app = MyTest()
app.setupUi(window)
app.show()
sys.exit(a.exec_())

在此处输入图像描述

标签: pythonpyqt5signals-slotsqthreadqtimer

解决方案


tl;博士

问题是您调用setupUi了两次,第二次使用另一个小部件作为参数。

为什么它不起作用?

当您使用 pyuic 生成文件时,它实际上会根据 Python 的object类型创建一个类。该类本身不做任何事情,只是一个以“pythonic方式”加载ui元素的“接口”。

如果您尝试打开其中一个文件(永远不应编辑!),您将看到如下内容:

class Ui_Form(object):
    def setupUi(self, Form):
        Form.setObjectName("Form")
        self.progressBar = QtWidgets.QProgressBar(Form)
        self.progressBar.setProperty("value", 24)
        # ...

    def retranslateUi(self, Form):
        # things related to the localization of the ui

使用 Designer报告的官方指南作为第一个示例,除了显示窗口之外实际上什么都不做。

from PyQt5.QtWidgets import QApplication, QWidget
from ui_form import Ui_Form

# create an instance of the base class
window = QWidget()
# create an instance of the Ui "builder"
ui = Ui_Form()
# apply the ui to the base class created before
ui.setupUi(window)

最后一步是最重要的理解:仔细查看该setupUi方法属于什么对象,以及它的参数,然后回到使用 pyuic 创建的文件的内容:

    def setupUi(self, Form):
        Form.setObjectName("Form")
        self.progressBar = QtWidgets.QProgressBar(Form)

Form是之前创建的QWidget实例(“window”),而显然self是指的实例Ui_Form;让我们用它们所代表的实例的名称来翻译变量:

    def setupUi(ui, window):
        window.setObjectName("Form")
        ui.progressBar = QtWidgets.QProgressBar(window)

在开头的示例中,这意味着,虽然window与它的子小部件一起显示,但它们不是实例的属性:没有window.progressBar.

文档中的第二个示例显示了“单一继承”方法,该方法允许实现小部件之间的交互(“逻辑”)。我将像上面一样使用类名:

from PyQt5.QtWidgets import QApplication, QWidget
from ui_form import Ui_Form

class MyWidget(QWidget):
    def __init__(self):
        super(MyWidget, self).__init__()

        self.ui = Ui_Form()
        self.ui.setupUi(self)

现在uiwindow实例的一个属性;让我们再次“翻译” setupUi,假设现在“self”是正在创建的 MyWidget 的实例:

class Ui_Form(object):
    def setupUi(window.ui, window):
        window.setObjectName("Form")
        window.ui.progressBar = QtWidgets.QProgressBar(window)

这意味着现在您可以从窗口实例访问小部件,但只能通过self.ui(如window.ui)。

现在,让我们看看多重继承方法,它非常相似:

class MyWidget(QWidget, Ui_Form):
    def __init__(self):
        super(MyWidget, self).__init__()

        self.setupUi(self)

在这种情况下,MyWidget 继承自 Qwidget 和 Ui_Form 的方法属性。让我们再翻译一遍(注意类):

class MyWidget(object):
    def setupUi(window, window):
        window.setObjectName("Form")
        window.progressBar = QtWidgets.QProgressBar(window)

这种方法使所有小部件成为实例的直接属性(self.progressBar等),我通常建议它,因为它更直接和简单(经常发生您尝试访问小部件并忘记该ui前缀,使用这种方法它不会发生)。


现在,最后,你的问题。

window = QWidget()
app = MyTest()

setupUi在 MyTest 中被调用__init__,这意味着它app有一个名为 的属性progressBar,此时它应该是“可见”的,只要我们调用app.show().
您还创建了另一个小部件(window),并将setupUi用作参数:

app.setupUi(window)

让我们最后一次翻译它(为避免混淆,让我们更改您错误创建的“窗口”QWidget的名称):

    def setupUi(app, otherWindow):
        otherWindow.setObjectName("Form")
        app.progressBar = QtWidgets.QProgressBar(otherWindow)

如您所见,现在您已经“覆盖”了该app.progressBar属性,但该进度条实际上是一个进度条,并且它是子项window(您从未显示)。

然后 updateProgressBar 函数将成功修改进度条的值,但不是您看到的那个。

最后,还有另外一种使用ui文件的方式,就是通过uic模块,可以直接加载ui文件。

from PyQt5 import QtWidgets, uic

class MyTest(QtWidgets.QWidget):
    def __init__(self, parent=None)
        super().__init__(parent)
        uic.loadUi('mywindow.ui', self)

这种方法的另一大优势是:您不必再使用 pyuic 创建文件,它的行为与多重继承方法完全相同,因此您可以self.progressBar像以前一样使用。


推荐阅读