今天我遇到了一个有趣的情况,下面的一行在行前行(上面一行)之前执行。这种情况的简单版本发布在下面。简要说明:有三个QGroupBox()
连接到同一窗口:groupboxA
,groupboxB
和groupboxC
。
Groupbox B被隐藏。单击“确定”按钮将取消隐藏第二个组框B并隐藏第一个组框A.重复单击将继续此序列的隐藏/取消隐藏事件。
最后一行是调用calcA()
方法处理,大约需要2-3秒才能进行竞争。
问题:即使在最后一行调用calcA()
时,它也会在更改QGroupBoxes可见性之前执行,上面编写的代码负责这些可见性。我想知道它为什么会发生以及如何解决它。
from PyQt4 import QtGui, QtCore
class MyApp(object):
def __init__(self):
super(MyApp, self).__init__()
app = QtGui.QApplication(sys.argv)
self.mainWidget = QtGui.QWidget()
self.mainLayout = QtGui.QVBoxLayout()
self.mainWidget.setLayout(self.mainLayout)
# A
self.groupboxA = QtGui.QGroupBox()
self.layoutA = QtGui.QVBoxLayout()
self.groupboxA.setLayout(self.layoutA)
lineA = QtGui.QTextEdit('This is QGroupBox A')
self.layoutA.addWidget(lineA)
# B
self.groupboxB = QtGui.QGroupBox()
self.layoutB = QtGui.QVBoxLayout()
self.groupboxB.setLayout(self.layoutB)
self.groupboxB.setHidden(True)
labelB = QtGui.QLabel('This is QGroupBox B')
self.layoutB.addWidget(labelB)
# C
self.groupboxC = QtGui.QGroupBox()
self.layoutC = QtGui.QVBoxLayout()
self.groupboxC.setLayout(self.layoutC)
okButton = QtGui.QPushButton('OK')
okButton.clicked.connect(self.OK)
self.layoutC.addWidget(okButton)
self.mainLayout.addWidget(self.groupboxA)
self.mainLayout.addWidget(self.groupboxB)
self.mainLayout.addWidget(self.groupboxC)
self.mainWidget.show()
sys.exit(app.exec_())
def calcA(arg=None):
print "Task started..."
for i in range(5000000):
pass
print '...task completed.'
def OK(self):
if self.groupboxA.isHidden(): self.groupboxA.setHidden(False)
else: self.groupboxA.setHidden(True)
if self.groupboxB.isHidden(): self.groupboxB.setHidden(False)
else: self.groupboxB.setHidden(True)
self.calcA()
if __name__ == '__main__':
MyApp()
答案 0 :(得分:2)
在将控件返回到Qt事件循环(一旦您的插槽OK
完成)之后,不会更新GUI。
当您有一个长时间运行的插槽并希望在插槽结束之前可以看到GUI更改时,这是一个常见问题。这个问题有一些答案,详细介绍了一些解决方法: Label in PyQt4 GUI not updating with every loop of FOR loop
基本上,将长时间运行的工作卸载到QThread通常是最好的想法,但是如果长时间运行的代码不直接与GUI交互(您无法通过线程与GUI交互),则只能轻松地执行此操作,所以它变得棘手)。线程很容易出错,并且会严重破坏程序的稳定性,所以你可能不想走这条路。
另一种选择是在更新后的QApplication.instance().processEvents()
方法中调用app
(或将self.app
分配给__init__
,然后立即调用self.app.processEvents()
) GUI(如setHidden()
),但我通常会尽量避免这种情况,因为我认为这是不好的做法。
最终解决方案以及可能在您的情况下效果最佳的解决方案是拨打QTimer.singleShot(1,self.calcA)
而不是self.calcA
。这会将控制权返回给事件循环,这将更新您的GUI,然后调用self.calcA
。
答案 1 :(得分:1)
问题是你有这个,伪代码:
def OK(self):
modify GUI
self.long_running_function()
微妙之处在于,只有在Qt事件循环恢复后,即在OK()返回后,对GUI的修改才会被“处理”。这就是为什么看起来你的长时间运行功能在它上面的行之前执行。它不是,但是一旦OK()返回,它上面的行只会“激活”,这是在你的长时间运行功能完成之后。
因此,如果您希望之前进行的GUI更新可见,则无法在OK中运行长时间运行功能。解决方案取决于您的长时间运行功能:如果它是一个循环,您可以这样写:
def long_running_function(self):
while condition:
self.doQuickStuff()
然后你可以用这个代替:
def doQuickStuff(self):
if condition:
.... do stuff ....
# repeat:
# 10 ms should give time for some event processing
QTimer.singleShot(10, self.doQuickStuff)
def OK(self):
modify GUI
doQuickStuff()
否则只需将长时间运行的函数移动到QThread即可。你必须考虑以下问题: