Предотвращать включение сигналов в Qt
У нас есть объект QCheckBox
, когда пользователь проверяет его или удаляет проверку, мы хотим вызвать функцию, чтобы мы подключили нашу функцию к сигналу stateChanged ( int state )
. С другой стороны, в соответствии с некоторым условием мы также меняем состояние объекта QCheckBox
внутри кода, и это вызывает нежелательный сигнал.
Есть ли способ предотвратить срабатывание сигнала при некоторых условиях?
Ответы
Ответ 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
Если какой-либо элемент пользовательского интерфейса не отвечает на пользователя, его следует отключить. Чтобы пользователь знал, что этот элемент не принимает ввод.