PyQt4 GUI中的标签在每个FOR循环中都没有更新

9
我有一个问题,我希望从一个使用GUI的Python程序中运行几个命令行函数。我不知道我的问题是特定于PyQt4还是与我对Python代码的误用有关。
我想做的是在我的GUI上有一个标签可以更改其文本值,以通知用户正在执行哪个命令。然而,当我使用for循环运行多个命令时,我的问题就出现了。我希望标签在每次循环时更新自己,但是程序却没有更新GUI标签,而是只在完成整个循环后更新自己,并显示最后一个被执行的命令。
我正在使用PyQt4作为我的GUI环境。我已经确认标签的文本变量确实在每次循环时得到更新,但是它并没有在GUI中可视化地显示出来。
有没有办法强制标签更新自己?我已经尝试在循环内部使用update()和repaint()方法,但它们没有产生任何区别。
我真的很感激任何帮助。谢谢。
这是我正在使用的代码:
# -*- coding: utf-8 -*-
import sys, os
from PyQt4 import QtGui, QtCore
Gui = QtGui
Core = QtCore

# ================================================== CREATE WINDOW OBJECT CLASS
class Win(Gui.QWidget):
    def __init__(self, parent = None):
        Gui.QWidget.__init__(self, parent)

        # --------------------------------------------------- SETUP PLAY BUTTON
        self.but1 = Gui.QPushButton("Run Commands",self)
        self.but1.setGeometry(10,10, 200, 100)

        # -------------------------------------------------------- SETUP LABELS
        self.label1 = Gui.QLabel("No Commands running", self)
        self.label1.move(10, 120)

        # ------------------------------------------------------- SETUP ACTIONS
        self.connect(self.but1, Core.SIGNAL("clicked()"), runCommands)


# =======================================================  RUN COMMAND FUNCTION
def runCommands():
    for i in commands:
        win.label1.setText(i)       # Make label display the command being run
        print win.label1.text()     # This shows that the value is actually
                                    # changing with every loop, but its just not
                                    # being reflected in the GUI label
        os.system(i)

# ======================================================================== MAIN

# ------------------------------------------------------  THE TERMINAL COMMANDS
com1 = "espeak 'senntence 1'"
com2 = "espeak 'senntence 2'"
com3 = "espeak 'senntence 3'"
com4 = "espeak 'senntence 4'"
com5 = "espeak 'senntence 5'"
commands = (com1, com2, com3, com4, com5)

# --------------------------------------------------- SETUP THE GUI ENVIRONMENT
app = Gui.QApplication(sys.argv)
win = Win()
win.show()

sys.exit(app.exec_())
3个回答

14

以下是您可以采取的措施:

  • 将长时间运行的循环移到次要线程中,绘制GUI在主线程中进行。

  • 在您的循环中调用 app.processEvents()。这样可以让Qt处理事件并重绘GUI。

  • 将您的循环分解,并使用超时为0的QTimer来运行它。

使用线程是最好的选择,但涉及到比仅仅调用processEvents更多的工作。使用计时器的方式是老式的方法,不再推荐使用。(请参阅文档)


1
非常感谢! 我发现app.processEvents()只有在每个命令已经完成后才更改标签,这太晚了,而且还跳过了一些命令。有效的方法是创建一个带有run方法的新QThread对象,并在按下按钮时调用run方法。这就是你的意思吗?这是我使用的代码。我以前从未学过线程,请告诉我是否不明智地应用了它。class RunCommands(Core.QThread):
def run(self):
for i in commands:
win.label1.setText(i) os.system(i)
- Ronny
哦,天啊,我的注释没有按照我输入的换行和制表符打印出来 :( - Ronny
@Ronny:听起来差不多,但你应该调用start()而不是run() - Georg Schölly

2

您对GUI的工作原理有一个基本误解。Qt GUI必须在其自己的事件循环中运行。而您的循环正在运行,GUI不能在您的循环执行之间完成其工作。也就是说,在您的for循环运行时,GUI代码不会获得CPU时间并且不会更新。

您可以设置一个带有事件的计时器,并在该事件的处理程序中执行一定量的时间 - 这将解决您的问题。


1
感谢您在回复的第一段中的解释。现在我对正在发生的事情有了更多的理解,也更好地理解了Georg答案背后的原因。然而,我不理解您提出的实际解决方案。我是一个编程的初学者。也许一个简单的代码概述草图会很有帮助。尽管我现在已经找到了一个似乎可行的解决方案,但我仍然很想了解您提出的替代方案。 - Ronny
@Ronny:学习一下QTimer,并查看一些使用示例-我认为它会变得清晰明了。 - Eli Bendersky

1
或者您可以直接调用repaint()来立即更新GUI。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接