C++ 防止在 Qt 中触发信号

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/3556687/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-28 13:07:44  来源:igfitidea点击:

Prevent Firing Signals in Qt

c++qtcheckboxsignals-slots

提问by metdos

We have a QCheckBoxobject, when user checks it or removes check we want to call a function so we connect our function to stateChanged ( int state )signal. On the other hand, according to some condition we also change the state of QCheckBoxobject inside code, and this causes the unwanted signal.

我们有一个QCheckBox对象,当用户检查它或删除检查时,我们想要调用一个函数,因此我们将我们的函数连接到stateChanged ( int state )信号。另一方面,根据某些情况,我们也会改变QCheckBox代码内部对象的状态,这会导致不需要的信号。

Is there any way to prevent firing signal under some conditions?

在某些情况下有什么办法可以防止发射信号?

回答by Job

You can use the clickedsignal because it is only emitted when the user actually clicked the check box, not when you manually check it using setChecked.

您可以使用该clicked信号,因为它仅在用户实际单击复选框时才会发出,而不是在您使用setChecked.

If you just don't want the signal to be emitted at one specific time, you can use QObject::blockSignalslike this:

如果您只是不想在某个特定时间发出信号,则可以这样使用QObject::blockSignals

bool oldState = checkBox->blockSignals(true);
checkBox->setChecked(true);
checkBox->blockSignals(oldState);

The downside of this approach is that allsignals will be blocked. But I guess that doesn't really matter in case of a QCheckBox.

这种方法的缺点是所有信号都将被阻止。但我想这在 a 的情况下并不重要QCheckBox

回答by Caleb Huitt - cjhuitt

You can always block signal emission on QObjects using QObject::blockSignals(). Note that to be correct about things, you should remember the old state (returned from the function call), and restore it when you are done.

您始终可以使用 阻止 QObjects 上的信号发射QObject::blockSignals()。请注意,要正确处理事情,您应该记住旧状态(从函数调用返回),并在完成后恢复它。

At my job, we prefer RAII for this sort of thing. A simple class to do so might look like this:

在我的工作中,我们更喜欢使用 RAII 来处理这类事情。一个简单的类可能如下所示:

class SignalBlocker
{
public:
    SignalBlocker( QObject *obj ) : m_obj( obj ), m_old( obj->blockSignals( true ) )
    {
    }

    ~SignalBlocker()
    {
        m_obj->blockSignals( m_old );
    }

private:
    QObject *m_obj;
    bool m_old;
};

Edit: Starting with Qt 5.3, see QSignalBlocker(h/t to HappyCactus in comments)

编辑:从 Qt 5.3 开始,请参阅QSignalBlocker(h/t to HappyCactus 在评论中)

回答by liaK

You can QObject::disconnectto remove the corresponding signal-slot connection and can QObject::connectagainonce you are done...

您可以QObject::disconnect删除相应的信号槽连接,完成后可以QObject::connect再次...

回答by Boojum

While learning Qt, I ran into this problem with a set of interconnected widgets that I wanted to update "atomically". I liked @cjhuitt's solution, but found that it goes even better with a bit of syntactic sugar based on proxy objects. Here's the approach that I used...

在学习 Qt 时,我遇到了一组互连小部件的问题,我想“以原子方式”更新这些小部件。我喜欢@cjhuitt 的解决方案,但发现它在使用基于代理对象的一些语法糖时效果更好。这是我使用的方法...

First, I defined a class template for a blocker proxy object. Like Caleb's, this blocks the signals on construction, and then restores their previous state on destruction. However, it also overloads the ->operator to return a pointer to the blocked object:

首先,我为拦截器代理对象定义了一个类模板。像 Caleb 一样,这会在构建时阻塞信号,然后在销毁时恢复它们之前的状态。但是,它也会重载->运算符以返回指向阻塞对象的指针:

template<class T> class Blocker {
    T *blocked;
    bool previous;
public:
    Blocker(T *blocked)
        : blocked(blocked),
          previous(blocked->blockSignals(true)) {}
    ~Blocker() { blocked->blockSignals(previous); }
    T *operator->() { return blocked; }
};

Next, I defined a small template function to construct and return a Blocker:

接下来,我定义了一个小的模板函数来构造并返回一个 Blocker:

template<class T> inline Blocker<T> whileBlocking(T *blocked) {
    return Blocker<T>(blocked);
}

Putting this all together, I'd use it like this:

将所有这些放在一起,我会像这样使用它:

whileBlocking(checkBox)->setChecked(true);

or

或者

whileBlocking(xyzzySpin)->setValue(50);

This gets me all the benefits of RAII, with automatically paired blocking and restore around the method call, but I don't need to name any wrapper or state flags. It's nice, easy, and pretty darn foolproof.

这让我获得了 RAII 的所有好处,在方法调用周围自动配对阻塞和恢复,但我不需要命名任何包装器或状态标志。这很好,很简单,而且非常万无一失。

回答by ZestyMeta

In QObjectderived classes, you can call blockSignals(bool)to prevent the object from emitting signals. So for example:

QObject派生类中,您可以调用blockSignals(bool)以防止对象发出信号。例如:

void customChangeState(bool checked)
{
    blockSignals(true);
    ui->checkBox->setCheckState(Qt::Checked);
    // other work
    blockSignals(false);
}

The above method would change the check state without clicked, stateChanged, or any other signals being emitted.

上述方法将更改检查状态,而无需单击stateChanged、 或任何其他信号被发射。

回答by HappyCactus

Qt5.3 introduced the QSignalBlockerclass that does exactly what needed in an exception safe way.

Qt5.3 引入了QSignalBlocker类,该类以异常安全的方式完成所需的工作。

if (something) {
   const QSignalBlocker blocker(someQObject);
   // no signals here
}

回答by peter karasev

Even in QT5, its a bit cumbersome when there are many/several things to block. Here's a multi-object version that is concise to use:

即使在 QT5 中,当有很多/几件事情要阻止时,它也有点麻烦。这是一个简洁易用的多对象版本:

class SignalBlocker
{
public:
  SignalBlocker(QObject *obj)
  {
    insert( QList<QObject*>()<<obj );
  }    
  SignalBlocker(QList<QObject*>  objects)
  {
    insert(objects);
  }    
  void insert(QList<QObject*>  objects)
  {
    for (auto obj : objects)
      m_objs.insert(obj, obj->signalsBlocked());
    blockAll();
  }    
  void blockAll() {
    for( auto m_obj : m_objs.keys() )
      m_obj->blockSignals(true);
  }    
  ~SignalBlocker()
  {
    for( auto m_obj : m_objs.keys() )
      m_obj->blockSignals( m_objs[m_obj] );
  }    
private:
  QMap<QObject*,bool> m_objs;      
};

usage:

用法:

void SomeType::myFunction()
{
    SignalBlocker tmp( QList<QObject*>() 
    << m_paramWidget->radioButton_View0
    << m_paramWidget->radioButton_View1
    << m_paramWidget->radioButton_View2
    );
    // Do more work, ... 
}

回答by user2524405

When some UI element should not respond to user it is appropriate to disable it. So that user would know that this element is not accepting input.

当某些 UI 元素不应响应用户时,禁用它是合适的。这样用户就会知道这个元素不接受输入。