我们有一个QCheckBox
对象,当用户检查它或删除检查时,我们想调用一个函数,因此我们将函数连接到stateChanged ( int state )
信号。另一方面,根据某些情况,我们也会改变QCheckBox
代码内部对象的状态,这会导致不需要的信号。
在某些情况下有什么方法可以防止触发信号吗?
我们有一个QCheckBox
对象,当用户检查它或删除检查时,我们想调用一个函数,因此我们将函数连接到stateChanged ( int state )
信号。另一方面,根据某些情况,我们也会改变QCheckBox
代码内部对象的状态,这会导致不需要的信号。
在某些情况下有什么方法可以防止触发信号吗?
您可以使用该clicked
信号,因为它仅在用户实际单击复选框时发出,而不是在您使用setChecked
.
如果您只是不希望在某个特定时间发出信号,则可以QObject::blockSignals
这样使用:
bool oldState = checkBox->blockSignals(true);
checkBox->setChecked(true);
checkBox->blockSignals(oldState);
这种方法的缺点是所有信号都会被阻塞。但我想在QCheckBox
.
您始终可以使用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 to HappyCactus in comments)
在学习 Qt 时,我遇到了一个我想“原子地”更新的一组互连小部件的问题。我喜欢@cjhuitt 的解决方案,但发现它在使用一些基于代理对象的语法糖时效果会更好。这是我使用的方法...
首先,我为拦截器代理对象定义了一个类模板。像 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; }
};
接下来,我定义了一个小模板函数来构造并返回一个 Blocker:
template<class T> inline Blocker<T> whileBlocking(T *blocked) {
return Blocker<T>(blocked);
}
把这一切放在一起,我会这样使用它:
whileBlocking(checkBox)->setChecked(true);
或者
whileBlocking(xyzzySpin)->setValue(50);
这让我获得了 RAII 的所有好处,在方法调用周围自动配对阻塞和恢复,但我不需要命名任何包装器或状态标志。这很好,很简单,而且非常万无一失。
您可以QObject::disconnect
删除相应的信号槽连接,并在完成QObject::connect
后再次...
在QObject
派生类中,您可以调用blockSignals(bool)
以防止对象发出信号。例如:
void customChangeState(bool checked)
{
blockSignals(true);
ui->checkBox->setCheckState(Qt::Checked);
// other work
blockSignals(false);
}
上述方法将更改检查状态,而stateChanged
不会发出单击、 或任何其他信号。
Qt5.3 引入了QSignalBlocker类,它以异常安全的方式完成所需的工作。
if (something) {
const QSignalBlocker blocker(someQObject);
// no signals here
}
即使在 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, ...
}
当某些 UI 元素不应该响应用户时,禁用它是合适的。这样用户就会知道这个元素不接受输入。