项目中需要使用一个QCheckBox来控制某些界面的显隐,并且在不满足条件的情况下,需要将CheckBox的值恢复到原先的状态。
如CheckBox本身控制着远端设备的启停,从unchecked状态点击,按理说会启动设备,并将CheckBox状态变为checked。但是如果在启动的过程中失败了,这个时候需要恢复CheckBox的状态。
这就是项目的需求。
为了实现状态的恢复,特意设计了一个recover函数,最开始的一版使用的是setChecked()函数,如下:
void recoverState(bool checked)
{
// 注意,此处使用的是setChecked函数
ui.checkBox->setChecked(checked);
ui.widget1->setVisible(checked);
ui.widget2->setVisible(checked);
}
结果就发现,界面在多次点击的状态下出现了紊乱,简而言之就是checked状态和界面的显隐不匹配(界面的显隐是通过stateChanged信号控制的)。
在调试中发现,虽然此时通过setChecked函数将其状态设置为false了,但是其checkState的值仍然是Qt::Checked,这就是造成显隐状态不一致的原因。
这就把我整懵了,百思不得其解的情况下我就去扒了源码,摘录如下:
setChecked的源码:
void QAbstractButton::setChecked(bool checked)
{
Q_D(QAbstractButton);
if (!d->checkable || d->checked == checked) {
if (!d->blockRefresh)
checkStateSet();
return;
}
if (!checked && d->queryCheckedButton() == this) {
// the checked button of an exclusive or autoexclusive group cannot be unchecked
#if QT_CONFIG(buttongroup)
if (d->group ? d->group->d_func()->exclusive : d->autoExclusive)
return;
if (d->group)
d->group->d_func()->detectCheckedButton();
#else
if (d->autoExclusive)
return;
#endif
}
QPointer<QAbstractButton> guard(this);
d->checked = checked;
if (!d->blockRefresh)
checkStateSet();
d->refresh();
if (guard && checked)
d->notifyChecked();
if (guard)
d->emitToggled(checked);
#ifndef QT_NO_ACCESSIBILITY
QAccessible::State s;
s.checked = true;
QAccessibleStateChangeEvent event(this, s);
QAccessible::updateAccessibility(&event);
#endif
}
setCheckState源码:
*/
void QCheckBox::setCheckState(Qt::CheckState state)
{
Q_D(QCheckBox);
if (state == Qt::PartiallyChecked) {
d->tristate = true;
d->noChange = true;
} else {
d->noChange = false;
}
d->blockRefresh = true;
setChecked(state != Qt::Unchecked);
d->blockRefresh = false;
d->refresh();
if ((uint)state != d->publishedState) {
d->publishedState = state;
emit stateChanged(state);
}
}
由源码可以看出,setChecked是QAbstractButton的方法,而setCheckState是QCheckBox的方法,在setCheckState中调用了setChecked。
而我实在没有看出通过直接调用setChecked的方式,这就导致CheckBox的状态和其checkState不一致的情况(通过界面操作是不影响的,这里说的是通过代码来控制)。
因此,如果想要通过代码的来控制QCheckBox的状态,在涉及到影响其他控件的情况下,最好采用setCheckState的方式来进行管理。
修改之后的代码如下:
void recoverState(bool checked)
{
// 换为setCheckState的方式
ui.cbWlan->setCheckState(checked ? Qt::Checked : Qt::Unchecked);
ui.widget1->setVisible(checked);
ui.widget2->setVisible(checked);
}