编辑表时关闭 PyQt 事件循环

Posted

技术标签:

【中文标题】编辑表时关闭 PyQt 事件循环【英文标题】:Turn off PyQt Event Loop While Editing Table 【发布时间】:2012-11-26 03:20:47 【问题描述】:

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

So, when a list item is selected -> update the table with the item's properties (from a dictionary variable), highlight the item on the plot

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

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

在以编程方式更新表时,是否有禁用 GUI 事件循环的典型方法? (我有使用 Excel VBA 的经验,在该上下文设置 Application.EnableEvents=False 将阻止每次以编程方式更新单元格时触发 WorksheetChange 事件。) 我是否应该有一个“正在进行表更新”变量以防止在更新表时采取行动? 有没有办法一次更新表格小部件而不是逐项更新? (我承认我暂时有意避免使用 Model-View 框架,因此使用 qListWIdget 和 qTableWidget)。

有什么建议吗?

我是第一次发帖,但 *** 的长期用户,所以我只想提前感谢您成为如此棒的社区!

【问题讨论】:

万一其他人落在这里:PyQt 为所有 QObjects(所有小部件和任何可以发送信号的东西)提供 blockSignals() 方法。 doc.qt.digia.com/stable/qobject.html#blockSignals 为了防止小部件发送信号,widget.blockSignals(True) 可以解决问题。您可以使用 widget.signalsBlocked() 检查阻塞状态。使用 widget.blockSignals(False) 重新打开信号。这在填充可能不需要 itemChanged 信号的小部件时特别有用。 【参考方案1】:

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 的情况下禁用布局元素或整个选项卡。【参考方案2】:

对于继承QObject的任何对象,信号可以暂时为blocked:

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

【讨论】:

【参考方案3】:

如果您禁用整个事件循环,应用将变得无响应。而且,即使用户没有注意到,操作系统也可能会发出某种“挂起”通知(就像 OS X 的色彩鲜艳的旋转沙滩球,用户永远不会错过)。

您可能希望在不完全禁用事件循环的情况下禁用 repaints。但即使这样也可能太激烈了。

您真正要做的就是确保表格停止重绘本身(不改变您实现表格视图的方式,您承认这并不理想,但您有理由这样做)。

所以,只需禁用 ItemChanged 更新。几乎在所有情况下,最简单的方法是在小部件上调用blockSignals(True)

在极少数情况下这不起作用(或者当您处理旨在用于基于 Qt4 和早期项目的古老代码时),您仍然可以获得信号的处理程序,把它们藏起来,然后移除它们,然后做你的工作,然后恢复以前的处理程序。

您可以改为创建处理程序可以访问的标志,并更改它们,以便在设置标志时它们不执行任何操作。这是传统的 C 做事方式,但通常不是您想要在 Python 中做的事情。

【讨论】:

谢谢!没想到只是禁用信号本身(仍然习惯于信号槽结构)。您的回复将我引向 Google “pyqt disable signal”,这导致了一个相关的 *** 问题:***.com/questions/7323295/…,它揭示了 blockSignals() 方法。在更新表之前使用 table.blockSignals(True) 并在之后使用 table.blockSignals(False) 会产生预期的效果! 是的,blockSignals 实际上比隐藏他们的处理程序更好(并且 +1 给 ekhumoro 的建议),除非你正在编写非常奇怪的代码(你在系统中有对象不是' t real QObjects,或者你想在 Qt3 和 Qt4 中使用相同代码的地方,或者……)。很抱歉没有先提出这个建议,很高兴你还是设法找到了它。

以上是关于编辑表时关闭 PyQt 事件循环的主要内容,如果未能解决你的问题,请参考以下文章

PyQT5 与 matplotlib 图,事件循环已经在运行

python和PyQt5中的Quamash QventLoop“RuntimeError:没有运行事件循环”错误

PyQt 事件循环和 ipython 中的异常

Pyside / Pyqt 从窗口打开新窗口(事件循环已在运行)

QCoreApplication::exec: 事件循环已经在运行 - 调用另一个 PyQt5 文件

PyQt5的事件机制