在 Qt 中禁用小部件上的阻塞信号

Posted

技术标签:

【中文标题】在 Qt 中禁用小部件上的阻塞信号【英文标题】:Blocking signals on disabled widgets in Qt 【发布时间】:2014-06-27 06:52:55 【问题描述】:

我有一个禁用的组合,但向其中添加一个元素会发出currentIndexChanged(int) 信号。

我希望在禁用小部件时信号会自然关闭,但事实并非如此。我知道有blockSignals(bool),但如果有许多小部件的信号必须“在禁用时被阻止”,blockSignals 将需要每个小部件的布尔状态。

如何禁用小部件在被禁用时发送的信号(而不改变其blockSignals 状态)?

编辑 澄清一下:由于这是一个小部件,当它被禁用时,用户无法与之交互,但是在以编程方式更改小部件时会发出一些信号。就我而言,有两个有趣的信号: currentIndexChanged(int)activated(int)

我的代码中的问题是我有时会以编程方式更改组合并且我希望它发出信号,有时是用户通过交互来更改组合。这就是我使用currentIndexChanged 而不是activated 的原因。

无论如何,在这两种情况下,我都不希望在小部件被禁用时发出信号。

【问题讨论】:

但也许你仍然可以发出所有信号,如果未启用发射小部件,则在插槽中忽略它们? @vahancho 是的,可能是一个解决方案,但我希望在 Qt 中有更多的结构,因为这个解决方案复制了很多用于检查的代码。 好吧,另一方面,如果你禁用了一个小部件,为什么你不能也阻止它的信号 (blockSignals(true))? 可以,但是要保存之前的阻塞状态。需要一个额外的变量(这没什么大不了的,但如果我不必这样做会更好)。 另外值得注意的是,如果父母被禁用怎么办? blockSignals(true) 在这种情况下可能会很痛苦。 【参考方案1】:

QComboBox 信号是基于最终用户角度的用户交互,前提是您只有一个 QComboBox,而您的问题似乎暗示没有其他任何东西。

我根本无法重现该问题。我刚刚做了一个简短的程序,因为我不能简单地与小部件交互,所以我无法发出任何 QComboBox 信号。

编辑:为临时读者提供更多背景信息可能是个好主意,但根据 cmets 中的进一步说明,是的,以编程方式可能是这种情况,但随后发出信号使用相应的插槽以编程方式处理也可能很有用,因此如果 Qt 自动阻止它们,这并不是一个重大改进。

幸运的是,您希望拥有的功能已经可用:

bool QObject::blockSignals(bool block)

如果 block 为真,则此对象发出的信号将被阻塞(即,发出信号不会调用任何与之连接的东西)。如果block为false,则不会发生这种阻塞。

返回值是signalsBlocked()的前一个值。

请注意,即使该对象的信号已被阻塞,也会发出 destroy() 信号。

如果您想为许多小部件执行此操作,请创建一个您调用的简单函数,而不是 myWidget->setDisabled(true);

inline bool disableAndBlockSignals(QWidget *widget)
 
    widget->setDisabled(true);
    return widget->blockSignals(true);

如果您只想禁用其中的一些,比如currentIndexChanged,那么您可以手动使用disconnect。

【讨论】:

感谢您的回答,但正如我在帖子中提到的,我已经知道blockSignals(目前我正在使用此功能来实现我的需要)。这篇文章的目的是了解是否有一种方法可以在禁用小部件时自动阻止信号。当(像我的情况)有很多小部件要阻止时,使用 blockSignals 会变得有点乏味。 我真的希望QWidgets 中出现像blockSignalsWhenDisabled(bool) 这样的标志,但似乎没有:P @AkiRoss: inline bool disableAndBlockSignals(QWidget *widget) widget->setDisabled(true); return widget->blockSignals(true); 嗯,是的,但我的意思不一样:使用这个函数,我仍然必须存储布尔值,而且它不是一个通用标志,我可以在构造时应用于小部件......我必须在我的小部件上调用此函数而不是 setEnabled,这需要一些额外的代码。无论如何,我现在正在做与您的建议类似的事情。 @AkiRoss:子类化小部件并向构造函数添加标志。 Qt API 不会被每一个细微的细微差别弄得一团糟,因为在通常情况下使用它会变得更加困难。您的案例可能有效,但不够普遍。【参考方案2】:

您可以在要阻止信号时通过QObject::disconnect(); 断开信号,然后在要解除阻止时重新连接它们。

【讨论】:

这不是很方便的选择,因为你必须记住所有的连接,才能恢复它们。 确实可以编写一个包罗万象的连接/断开功能,但随后您重新发明了 blockSignals,除非您只想阻止其中的一些,在这种情况下,这在技术上是一个有效的解决方案。 【参考方案3】:

在您的情况下,信号是传递给其他对象的状态信息的唯一来源。如果禁用它们,其他对象将永远不会收到任何状态更改的通知。这可能会导致依赖于您的小部件状态的对象出现错误。

至少有两种解决方案:

    不要更改小部件的状态。您当然可以将小部件内容的更新推迟到重新启用之后。

    创建一个代理来监控原始小部件的状态,并将信号(通过压缩)排队,直到小部件重新启用。

由于这些变通方法,您的设计可能需要返工。如果您可以处理来自那些禁用的小部件的信号,也许会更好。您还应该评估禁用小部件是否不会破坏用户体验。如果用户想要查看小部件的内容,但并不意味着要更改当前设置怎么办?在这种情况下,禁用的小部件太过分了。您可以使您拥有,也许是子类化的小部件,以使控件不被禁用,但当前元素保持固定。这甚至可以是一个单独的对象,适用于任何控制——通过明智地利用用户属性。

【讨论】:

以上是关于在 Qt 中禁用小部件上的阻塞信号的主要内容,如果未能解决你的问题,请参考以下文章

Qt:动态小部件信号和槽连接

Qt小部件析构函数通过连接信号间接调用小部件方法,并崩溃

如何隐藏在 qt 中生成信号的小部件

如何关闭在提升的小部件中单击的按钮上的 qt 小部件 ui?

小部件关闭后 Qt 发出信号

Qt 在另一个小部件中调整小部件的大小