Предотвратить выстрелы в Qt - PullRequest
50 голосов
/ 24 августа 2010

У нас есть объект QCheckBox, когда пользователь проверяет его или снимает проверку, мы хотим вызвать функцию, поэтому мы подключаем нашу функцию к сигналу stateChanged ( int state ). С другой стороны, согласно некоторому условию мы также меняем состояние объекта QCheckBox внутри кода, и это вызывает нежелательный сигнал.

Есть ли какой-нибудь способ предотвратить срабатывание сигнала при определенных условиях?

Ответы [ 8 ]

77 голосов
/ 24 августа 2010

Вы можете использовать сигнал clicked, потому что он излучается только тогда, когда пользователь фактически установил флажок, а не когда вы вручную проверяете его, используя setChecked.

Если вы просто не хотите, чтобы сигнал излучался в одно конкретное время, вы можете использовать QObject::blockSignals следующим образом:

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

Недостатки этого подходаявляется то, что все сигналы будут заблокированы.Но я думаю, это не имеет значения в случае QCheckBox.

34 голосов
/ 24 августа 2010

Вы всегда можете заблокировать излучение сигнала на объектах QO, используя QObject::blockSignals().Обратите внимание, что, чтобы быть правдивым, вы должны запомнить старое состояние (возвращаемое из вызова функции) и восстановить его, когда закончите.

На моей работе мы предпочитаем 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;
};

Редактировать : Начиная с Qt 5.3, смотрите QSignalBlocker (h / t к HappyCactus в комментариях)

13 голосов
/ 24 августа 2010

Вы можете QObject::disconnect удалить соответствующее соединение сигнального слота и можете QObject::connect снова , когда вы закончите ...

12 голосов
/ 22 июня 2014

Во время изучения Qt я столкнулся с этой проблемой с набором взаимосвязанных виджетов, которые я хотел обновить «атомарно». Мне понравилось решение @ cjhuitt, но я обнаружил, что оно идет еще лучше с небольшим количеством синтаксического сахара на основе прокси-объектов . Вот подход, который я использовал ...

Сначала я определил шаблон класса для прокси-объекта-блокировщика. Как и у Калеба, это блокирует сигналы при строительстве, а затем восстанавливает их прежнее состояние при разрушении. Однако он также перегружает оператор -> для возврата указателя на заблокированный объект:

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; }
};

Затем я определил небольшую шаблонную функцию для создания и возврата блокировщика:

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

Сложив все это вместе, я бы использовал это так:

whileBlocking(checkBox)->setChecked(true);

или

whileBlocking(xyzzySpin)->setValue(50);

Это дает мне все преимущества RAII с автоматически спаренной блокировкой и восстановлением вокруг вызова метода, но мне не нужно называть какую-либо оболочку или флаги состояния. Это красиво, просто и чертовски надежно.

6 голосов
/ 24 августа 2010

В QObject производных классах вы можете вызвать blockSignals(bool), чтобы объект не излучал сигналы.Так, например:

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

Приведенный выше метод изменяет состояние проверки без нажатия, stateChanged или любых других излучаемых сигналов.

3 голосов
/ 17 апреля 2015

Qt5.3 представил класс QSignalBlocker , который делает именно то, что нужно безопасным способом.

if (something) {
   const QSignalBlocker blocker(someQObject);
   // no signals here
}
0 голосов
/ 13 июля 2015

Даже в 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;      
};

использование:

void SomeType::myFunction()
{
    SignalBlocker tmp( QList<QObject*>() 
    << m_paramWidget->radioButton_View0
    << m_paramWidget->radioButton_View1
    << m_paramWidget->radioButton_View2
    );
    // Do more work, ... 
}
0 голосов
/ 26 июня 2013

Когда какой-либо элемент пользовательского интерфейса не должен отвечать пользователю, целесообразно отключить его. Чтобы этот пользователь знал, что этот элемент не принимает входные данные.

...