Python pyqt:如何在外部变量值更改时动态更新小部件属性?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/20873259/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-18 21:26:52  来源:igfitidea点击:

pyqt: How to dynamically update widget property on outer variable value change?

pythonpyqtsignals-slots

提问by Sashko Lykhenko

I have class Ui_MainWindow(object)that creates a window with a progress bar and class OtherClass(object)that contains method in which the local int variable increments in cycle.

class Ui_MainWindow(object)创建了一个带有进度条的窗口,class OtherClass(object)其中包含局部 int 变量循环递增的方法。

How to connect local variable value change to progres bar value change?

如何将局部变量值更改连接到进度条值更改?

mainGUI.py

主界面.py

import sys
from PyQt4.uic.Compiler.qtproxies import QtGui
from PyQt4 import QtGui
from Ui_MainWindow import Ui_MainWindow

def main():

    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())



if __name__ == '__main__':
    main()

Ui_MainWindow.py

ui_MainWindow.py

from PyQt4 import QtCore, QtGui
from MainGui.OtherClass import OtherClass

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    _fromUtf8 = lambda s: s

class Ui_MainWindow(object):

    def myButtonSlot(self):
        objVar=OtherClass()
        objVar.method()

    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(389, 332)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.verticalLayout = QtGui.QVBoxLayout(self.centralwidget)
        self.verticalLayout.setObjectName(_fromUtf8("verticalLayout"))

        self.pushButton = QtGui.QPushButton(self.centralwidget)
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        self.pushButton.clicked.connect(self.myButtonSlot)
        self.verticalLayout.addWidget(self.pushButton)

        self.progressBar = QtGui.QProgressBar(self.centralwidget) 
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName(_fromUtf8("progressBar"))

        self.verticalLayout.addWidget(self.progressBar)
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 389, 21))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(QtGui.QApplication.translate("MainWindow", "MainWindow", None, QtGui.QApplication.UnicodeUTF8))
        self.pushButton.setText(QtGui.QApplication.translate("MainWindow", "PushButton", None, QtGui.QApplication.UnicodeUTF8))  

OtherClass.py

其他类.py

class OtherClass(object):  
    def method(self):
        for i in range(100): # i want to connect variable i to progress bar value
            print i 
            for j in range(100500):
                pass

回答by catwalker333

You have to use a signal and slot...and multiprocessing or multithreading.

您必须使用信号和槽...以及多处理或多线程。

There's a good example here that specifically takes you through the progress bar: ZetCode Progress Bar

这里有一个很好的例子,专门带你浏览进度条: ZetCode Progress Bar

Also, question has been answered here before: SO Progress Bar

另外,问题之前已经在这里回答过: SO Progress Bar

回答by ekhumoro

You need to re-organize your code a little bit.

您需要稍微重新组织一下代码。

Firstly, you should neveredit the code in the UI module generated by pyuic. Instead, import it into your main module and implement all your application logic there.

首先,你永远不应该编辑由pyuic. 相反,将其导入主模块并在那里实现所有应用程序逻辑。

Secondly, you should create a main-window class in your main module, and do all the setup inside its __init__method.

其次,您应该在主模块中创建一个主窗口类,并在其__init__方法中进行所有设置。

One way to solve your problem of connecting the loop variable to the progress bar, is to make OtherClassa subclass of QObjectand emit a custom signal:

解决将循环变量连接到进度条的问题的一种方法是创建OtherClass一个子类QObject并发出自定义信号:

from PyQt4 import QtCore

class OtherClass(QtCore.QObject):
    valueUpdated = QtCore.pyqtSignal(int)

    def method(self):
        # i want to connect variable i to progress bar value
        for i in range(100):
            print i
            self.valueUpdated.emit(i)
            for j in range(100500):
                pass

With that in place, you would then move the setup for pushButtonand its slot to "mainGUI.py", and re-generate "Ui_MainWindow.py" with pyuic. A slot would then be added to handle the custom valueChangedsignal, which would update the progress bar and also process any pending GUI events.

到位后,您可以将设置pushButton及其插槽移动到“mainGUI.py”,并使用pyuic. 然后将添加一个槽来处理自定义valueChanged信号,这将更新进度条并处理任何挂起的 GUI 事件。

So "mainGUI.py" would end up looking something like this:

所以“mainGUI.py”最终看起来像这样:

import sys
from PyQt4 import QtGui
from Ui_MainWindow import Ui_MainWindow
from OtherClass import OtherClass

class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
    def __init__(self):
        QtGui.QMainWindow.__init__(self)
        self.setupUi(self)
        self.pushButton.clicked.connect(self.myButtonSlot)
        self.otherclass = OtherClass(self)
        self.otherclass.valueUpdated.connect(self.handleValueUpdated)

    def myButtonSlot(self):
        self.otherclass.method()

    def handleValueUpdated(self, value):
        self.progressBar.setValue(value)
        QtGui.qApp.processEvents()

def main():
    app = QtGui.QApplication(sys.argv)
    window = MainWindow()
    window.show()
    sys.exit(app.exec_())

if __name__ == '__main__':

    main()

回答by Ninga

The following post has a version that increments the progress bar 10% each time you press the button. And a version which uses a timer to increment the progress bar. (I'm just in the process of learning this myself)

下面的帖子有一个版本,每次按下按钮时进度条都会增加 10%。还有一个使用计时器来增加进度条的版本。(我只是在自己学习这个过程中)

In Qt Designer, add a progress bar and a button. Click on 'Edit Signals/Slots', drag a line from the button to somewhere in the window and when the button is 'pressed()' add a slot(or signal??) called 'button_pressed()' (use the + button to make this). When you have done this, the OK button is greyed out - select the slot you made, and press OK.

在 Qt Designer 中,添加一个进度条和一个按钮。单击“Edit Signals/Slots”,将一条线从按钮拖到窗口中的某个位置,当按钮被“pressed()”时,添加一个名为“button_pressed()”的插槽(或信号??)(使用 + 按钮做这个)。完成此操作后,确定按钮变灰 - 选择您制作的插槽,然后按确定。

Save the file as ui_MainWindow.ui (note the capitals carefully). Convert to a py file using the batch file >

将文件另存为 ui_MainWindow.ui(注意大写)。使用批处理文件转换为 py 文件 >

pyuic4 -x ui_MainWindow.ui -o ui_MainWindow.py

This file should look something like....(you don't need to edit this).

这个文件应该看起来像....(你不需要编辑这个)。

from PyQt4 import QtCore, QtGui

try:
    _fromUtf8 = QtCore.QString.fromUtf8
except AttributeError:
    def _fromUtf8(s):
        return s

try:
    _encoding = QtGui.QApplication.UnicodeUTF8
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig, _encoding)
except AttributeError:
    def _translate(context, text, disambig):
        return QtGui.QApplication.translate(context, text, disambig)

class Ui_MainWindow(object):
    def setupUi(self, MainWindow):
        MainWindow.setObjectName(_fromUtf8("MainWindow"))
        MainWindow.resize(800, 600)
        self.centralwidget = QtGui.QWidget(MainWindow)
        self.centralwidget.setObjectName(_fromUtf8("centralwidget"))
        self.progressBar = QtGui.QProgressBar(self.centralwidget)
        self.progressBar.setGeometry(QtCore.QRect(110, 90, 118, 23))
        self.progressBar.setProperty("value", 24)
        self.progressBar.setObjectName(_fromUtf8("progressBar"))
        self.pushButton = QtGui.QPushButton(self.centralwidget)
        self.pushButton.setGeometry(QtCore.QRect(120, 200, 75, 23))
        self.pushButton.setObjectName(_fromUtf8("pushButton"))
        MainWindow.setCentralWidget(self.centralwidget)
        self.menubar = QtGui.QMenuBar(MainWindow)
        self.menubar.setGeometry(QtCore.QRect(0, 0, 800, 21))
        self.menubar.setObjectName(_fromUtf8("menubar"))
        MainWindow.setMenuBar(self.menubar)
        self.statusbar = QtGui.QStatusBar(MainWindow)
        self.statusbar.setObjectName(_fromUtf8("statusbar"))
        MainWindow.setStatusBar(self.statusbar)

        self.retranslateUi(MainWindow)
        QtCore.QObject.connect(self.pushButton, QtCore.SIGNAL(_fromUtf8("pressed()")), MainWindow.button_pressed)
        QtCore.QMetaObject.connectSlotsByName(MainWindow)

    def retranslateUi(self, MainWindow):
        MainWindow.setWindowTitle(_translate("MainWindow", "MainWindow", None))
        self.pushButton.setText(_translate("MainWindow", "PushButton", None))


if __name__ == "__main__":
    import sys
    app = QtGui.QApplication(sys.argv)
    MainWindow = QtGui.QMainWindow()
    ui = Ui_MainWindow()
    ui.setupUi(MainWindow)
    MainWindow.show()
    sys.exit(app.exec_())

Create a 'program.py' file. This is the file you will run...

创建一个“program.py”文件。这是您将运行的文件...

import sys
from PyQt4 import QtGui
#from PyQt5 import QtCore, QtGui, QtWidgets #works for pyqt5
from mainWindow import MainWindow

def main():
    #app = QtWidgets.QApplication (sys.argv) #works for pyqt5
    app = QtGui.QApplication (sys.argv) #works for pyqt4
    m = MainWindow ()
    m.show ()
    sys.exit (app.exec_ () )

if __name__ == '__main__':
    main ()

Now this is where the good stuff happens when you subclass the mainwindow. Call this file 'mainWindow.py'. Careful with the capitalizations.

现在,当您对主窗口进行子类化时,这就是好事发生的地方。将此文件称为“mainWindow.py”。小心大小写。

from PyQt4 import QtCore, QtGui
from ui_MainWindow import Ui_MainWindow #note the capitalization

class MainWindow (QtGui.QMainWindow):
    def __init__ (self, parent = None):
        super (MainWindow, self).__init__ ()
        self.ui = Ui_MainWindow ()
        self.ui.setupUi (self)
        #------------do your custom stuff from here on-----------
        self.progress = 0 #Start value of progress bar
        self.ui.progressBar.setValue(self.progress)

    def button_pressed(self):
        print('button pressed')
        self.ui.statusbar.showMessage(str(self.progress)) #this is at bottom left of window. Discovered this accidentially when doing this!
        self.ui.progressBar.setValue(self.progress)
        self.progress+=10

There is a good tutorial herewhich I used to create an alternate 'mainWindow.py' which uses a timer to increment the progress bar. It does not block the code with a loop using sleep or by doing a CPU intensive loop. I don't understand multithreading, multi-processor options yet to comment on using these.

这是一个很好的教程在这里,我用来创建使用定时器递增进度条备用“mainWindow.py”。它不会通过使用睡眠的循环或执行 CPU 密集型循环来阻塞代码。我不了解多线程、多处理器选项,还没有评论使用这些选项。

#from PyQt5 import QtCore, QtGui, QtWidgets #works for PyQt5
from PyQt4 import QtCore, QtGui
from ui_MainWindow import Ui_MainWindow #note the capitalization

class MainWindow (QtGui.QMainWindow):
    def __init__ (self, parent = None):
        super (MainWindow, self).__init__ ()
        self.ui = Ui_MainWindow () #same name as appears in mainWindowUi.py
        self.ui.setupUi (self)
        self.progress = 0 #Start value of progress bar
        self.ui.progressBar.setValue(self.progress)

        self.timer = QtCore.QBasicTimer()

    def button_pressed(self):
        self.timerEvent(64) #this needs an argument to work but I'm not sure what is is yet so I just put in some random number

    def timerEvent(self, e):
        self.ui.progressBar.setValue(self.progress)
        if self.progress >=100:
            self.timer.stop()
       else:
            if self.timer.isActive():
                pass
            else:
                self.timer.start(10,self) #10 milliseconds
        self.progress+=1