【发布时间】:2011-04-03 04:05:12
【问题描述】:
我们有一个QCheckBox 对象,当用户检查它或删除检查时,我们想要调用一个函数,因此我们将我们的函数连接到stateChanged ( int state ) 信号。另一方面,根据某些情况,我们也会在代码中改变QCheckBox对象的状态,这会导致不需要的信号。
在某些情况下有什么办法可以防止触发信号吗?
【问题讨论】:
标签: c++ qt checkbox signals-slots
我们有一个QCheckBox 对象,当用户检查它或删除检查时,我们想要调用一个函数,因此我们将我们的函数连接到stateChanged ( int state ) 信号。另一方面,根据某些情况,我们也会在代码中改变QCheckBox对象的状态,这会导致不需要的信号。
在某些情况下有什么办法可以防止触发信号吗?
【问题讨论】:
标签: c++ qt checkbox signals-slots
您可以使用clicked 信号,因为它仅在用户实际单击复选框时发出,而不是在您使用setChecked 手动检查时发出。
如果您只是不希望在某个特定时间发出信号,您可以像这样使用QObject::blockSignals:
bool oldState = checkBox->blockSignals(true);
checkBox->setChecked(true);
checkBox->blockSignals(oldState);
这种方法的缺点是所有信号都会被阻塞。但我想在QCheckBox 的情况下这并不重要。
【讨论】:
clicked 信号最适合此特定任务。
您可以QObject::disconnect删除相应的信号槽连接,完成后可以QObject::connect再次...
【讨论】:
在QObject派生类中,可以调用blockSignals(bool)来防止对象发出信号。比如:
void customChangeState(bool checked)
{
blockSignals(true);
ui->checkBox->setCheckState(Qt::Checked);
// other work
blockSignals(false);
}
上述方法会在没有点击、stateChanged 或任何其他信号发出的情况下更改检查状态。
【讨论】:
您始终可以使用QObject::blockSignals() 阻止 QObjects 上的信号发射。请注意,要正确处理事情,您应该记住旧状态(从函数调用返回),并在完成后恢复它。
在我的工作中,我们更喜欢 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 cmets)
【讨论】:
QObject::disconnect() 在这种情况下..
当某些 UI 元素不应响应用户时,禁用它是适当的。这样用户就会知道这个元素不接受输入。
【讨论】:
在学习 Qt 时,我遇到了这个问题,我想“原子地”更新一组相互连接的小部件。我喜欢@cjhuitt 的解决方案,但发现在proxy objects 的基础上加上一点语法糖会更好。这是我使用的方法...
首先,我为拦截器代理对象定义了一个类模板。像 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 的所有好处,自动配对阻塞和恢复方法调用,但我不需要命名任何包装器或状态标志。它很好,很简单,而且非常万无一失。
【讨论】:
QObjects都有setChecked方法,所以模板确实是必须的。
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, ...
}
【讨论】: