65

We have a QCheckBox object, 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 QCheckBox object inside code, and this causes the unwanted signal.

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

Angie Quijano
  • 4,167
  • 3
  • 25
  • 30
metdos
  • 13,411
  • 17
  • 77
  • 120

8 Answers8

100

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

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

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

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

feedc0de
  • 3,646
  • 8
  • 30
  • 55
mtvec
  • 17,846
  • 5
  • 52
  • 83
  • 4
    The disconnect then connect scenario proposed by liaK seems better than blocking all the signals for this specific task. – Longfield Aug 24 '10 at 13:58
  • 4
    @Longfield: I think using the `clicked` signal is best for this specific task. – mtvec Aug 24 '10 at 14:01
  • 1
    well, the state could also be changed by the program, not necessarily by the user through the UI. Anyway, we are talking about details here and I guess metdos has found a solution that suits him. – Longfield Aug 24 '10 at 21:19
37

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.

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

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)

feedc0de
  • 3,646
  • 8
  • 30
  • 55
Caleb Huitt - cjhuitt
  • 14,785
  • 3
  • 42
  • 49
  • 2
    Is there any specific reason for following this way?? Just asking B'cos we are used to `QObject::disconnect()` in these kinda scenarios.. – liaK Aug 24 '10 at 13:36
  • 6
    It depends on how many things might be connected to the object's signals, how sure you are that you disconnected the appropriate ones, and how easy it is to reconnect them again. Myself, I think this is usually easier, but using disconnect works as well. – Caleb Huitt - cjhuitt Aug 24 '10 at 13:55
  • 1
    @liaK: A good reason is RAII, which every (EVERY) C++ programmer should know, including the whys and hows. Basically, in this case, it will release the block even if exceptions are thrown and your manual unlock is missed. Personally, I use a similar approach, and I wonder why Qt doesn't have this in the libraries (or I did miss it). – Sebastian Mach Feb 11 '11 at 11:07
  • 5
    This was introduced in Qt5.3 QSignalBlocker: http://doc.qt.io/qt-5/qsignalblocker.html – HappyCactus Apr 17 '15 at 09:42
19

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...

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:

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:

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.

Boojum
  • 6,592
  • 1
  • 30
  • 34
  • Seems overly complicated, as all objects that need blocking are going to be derived from QObject anyway. No need for templates here I think. – André Oct 23 '20 at 08:45
  • 1
    @André, not all `QObject`s have a `setChecked` method, so the template is indeed necessary. – Parker Coates May 26 '21 at 12:27
14

You can QObject::disconnect to remove the corresponding signal-slot connection and can QObject::connect again once you are done...

liaK
  • 11,422
  • 11
  • 48
  • 73
6

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

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.

Angie Quijano
  • 4,167
  • 3
  • 25
  • 30
ZestyMeta
  • 121
  • 3
  • 3
    This will not block ui->checkBox from emitting signals, so it will not work as expected. you should call ui->checkBox->blockSignals(true) or use QSignalBlocker (starting from Qt5.3) – HappyCactus Apr 17 '15 at 09:46
4

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

if (something) {
   const QSignalBlocker blocker(someQObject);
   // no signals here
}
HappyCactus
  • 1,935
  • 16
  • 24
0

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.

0

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:

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, ... 
}
peter karasev
  • 2,578
  • 1
  • 28
  • 38