Подтвердить что ты не робот

Предотвращать включение сигналов в Qt

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

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

4b9b3361

Ответ 1

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

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

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

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

Ответ 2

Вы всегда можете блокировать излучение сигнала в QObjects, используя 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 в комментариях)

Ответ 3

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

Ответ 4

Во время обучения 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, с автоматической спаренной блокировкой и восстановлением вокруг вызова метода, но мне не нужно указывать какие-либо флаги оболочки или состояния. Это красиво, легко и красиво проворно.

Ответ 5

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

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

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

Ответ 6

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

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

Ответ 7

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

Ответ 8

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