在编辑表格时关闭 PyQt 事件循环

11

我正在使用PyQt开发一个图形用户界面(GUI)。GUI中包含一个qListWidget、一个qTableWidget和一个用Mayavi实现的绘图。列表引用了被绘制的形状(例如圆柱体和锥体)。当在列表中选择一个形状时,我希望该形状的属性被加载到表格中(从一个字典变量中),并且在绘图中突出显示该形状。我的Mayavi绘图已经正常工作。此外,如果表格被编辑,我需要重新绘制形状,以反映新的属性值(例如对于圆柱体,如果半径改变)。

所以,当选择列表项时 -> 使用该项的属性更新表格(从字典变量中),在绘图中突出显示该项

当表格被编辑时 -> 更新字典变量并重新绘制该项

问题是:当我选择一个列表项并将数据加载到表格中时,每次更新单元格时都会触发qTableWidget ItemChanged信号,这会导致以不完整的数据多次重新绘制形状。

在程序更新表格时,是否有一种典型的方法可以禁用GUI事件循环?(我有Excel VBA的经验,在那种情况下,设置Application.EnableEvents=False将防止每次单元格被程序更新时触发WorksheetChange事件。) 我应该有一个“正在更新表格”变量来防止在表格更新时采取行动吗? 是否有一种一次性更新表格小部件而不是逐个项目更新的方法?(我承认我正在故意避开Model-View框架,因此使用qListWIdget和qTableWidget)。

有什么建议吗?

我是第一次张贴者,但是我长期使用StackOverflow,所以提前感谢您成为这样一个令人敬畏的社区!


1
如果其他人来到这里:PyQt为所有QObjects(所有小部件和任何可以发送信号的东西)提供了blockSignals()方法。http://doc.qt.digia.com/stable/qobject.html#blockSignals要防止小部件发送信号,widget.blockSignals(True)就可以解决问题。您可以使用widget.signalsBlocked()检查阻塞状态。使用widget.blockSignals(False)重新启用信号。在填充小部件时,这特别有用,此时可能不需要itemChanged信号。 - flutefreak7
3个回答

18

blockSignals(bool) 旨在禁止 QObjects 及其子类发出信号,从而防止其他对象在槽中接收这些信号。但这是一个 QObject 方法。如果您特别想防止一个对象在响应您正在进行的更改时发出信号,并可能触发槽中的计算或其他昂贵的处理,则这就是您想要的。

但如果您的情况是重复更改导致昂贵的绘制操作一遍又一遍地发生(或在小部件上生成其他昂贵的事件),则您可以使用 updatesEnabled(bool) 禁用更新。这种方法的好处是它递归地禁用目标小部件的子级,也就是说,直到您再次启用为止,层次结构中的任何东西都不会接收到更新。

mainWidget.setUpdatesEnabled(False)
# do a bunch of operations that would trigger expensive events
# like repaints
mainWidget.setUpdatesEnabled(True)

最终取决于您的问题来源是触发信号还是触发小部件事件。阻止信号仍将允许小部件处理其事件,但不会通知任何其他侦听器。 updatesEnabled 是一种常见的方法来包装多个列表/表格/树更新。之后重新启用它时,将执行单个后更新。


感谢关于updatesEnabled()的额外信息。对其他操作来说,这可能会很有用。分层结构很好用,因为我可以禁用布局元素或整个选项卡而不必禁用整个GUI。 - flutefreak7

14

任何继承自 QObject 的对象都可以暂时阻止其信号的发出:

self.tableWidget.blockSignals(True)
# perform updates, etc
self.tableWidget.blockSignals(False)

0
如果你禁用了整个事件循环,应用程序就会失去响应。即使用户没有注意到,操作系统也可能会发出某种“挂起”通知(例如OS X的醒目旋转彩色海滩球,任何用户都不会错过)。
您可能希望在不完全禁用事件循环的情况下禁用重绘。但即使这样也可能太过严厉。
您真正想做的只是确保表格停止重新绘制自身(而不更改您实现表格视图的方式,虽然您承认这并不理想,但您有理由这样做)。
因此,只需禁用ItemChanged更新即可。在几乎所有情况下,最简单的方法是在小部件上调用blockSignals(True)
在极少数情况下,这种方法行不通(或者在处理旨在用于基于Qt4和早期项目的古老代码时),仍然可以获取信号的处理程序,将其隐藏,并将其删除,然后进行工作,最后恢复以前的处理程序。
您可以创建一个标志,让处理程序可以访问它,并更改它们,以便在设置了标志时它们什么都不做。这是传统的C语言做事情的方式,但通常不是Python中所需的。

1
谢谢!我没有想到可以禁用信号本身(仍在适应信号槽架构)。你的回答让我搜索“pyqt禁用信号”,然后找到了一个相关的StackOverflow问题:https://dev59.com/WVvUa4cB1Zd3GeqPpAH5,其中介绍了blockSignals()方法。在更新表格之前使用table.blockSignals(True),在更新后使用table.blockSignals(False)就可以产生所需的效果! - flutefreak7
是的,blockSignals比隐藏它们的处理程序更好(ekhumoro建议+1),除非你正在编写非常奇怪的代码(在系统中有不是真正的QObject对象,或者你想在Qt3和Qt4中使用相同的代码等)。很抱歉我没有首先提出这个建议,很高兴你还是找到了它。 - abarnert

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