QActionGroup互斥不生效的问题
问题描述
我有一个单实例的QAction
的集合actions_
然后传入不同的QToolbar
进行添加到不同的QToolbar
实例中。其中使用到了QActionGroup
将其中的单个QAction
进行互斥的处理。当创建了多个Qtoolbar实例后,释放了其中一个实例,其他QToolbar
的QActionGroup
互斥就不生效了。
之前的实现方式
action_group_ = new QActionGroup(toolbar_);
toolbar_->addAction(action_group_->addAction(actions_->get(action_id_t::CONTROL_MOUSE)));
toolbar_->addAction(action_group_->addAction(actions_->get(action_id_t::CONTROL_MARKER)));
toolbar_->addAction(action_group_->addAction(actions_->get(action_id_t::CONTROL_BANDPOWER)));
actions_->get(action_id_t::CONTROL_MOUSE)->setChecked(true);
原因分析:
action_group_->addAction(actions_->get(action_id_t::CONTROL_MOUSE)) 会生成一个新的QAction
取名为Temp_action,当QToolbar被释放的时候,action_group_也被释放了,同时Temp_action也被释放了,此时导致 actions_->get(action_id_t::CONTROL_MOUSE)) QAction
中的互斥属性就被释放掉了。(具体的原因看后面的源码分析就可以理解了)
解决方案
在创建的时候就将QActionGroup
的action
创建出来,并用集合管理起来。问题就得到了解决。
//创建
control_group_ = new QActionGroup(this);
id_2_action_group_[action_id_t::CONTROL_STRAT] = control_group_->addAction(get(action_id_t::CONTROL_STRAT));
id_2_action_group_[action_id_t::CONTROL_PAUSE] = control_group_->addAction(get(action_id_t::CONTROL_PAUSE));
action_group_ = new QActionGroup(this);
id_2_action_group_[action_id_t::CONTROL_MOUSE] = action_group_->addAction(get(action_id_t::CONTROL_MOUSE));
id_2_action_group_[action_id_t::CONTROL_MARKER] = action_group_->addAction(get(action_id_t::CONTROL_MARKER));
id_2_action_group_[action_id_t::CONTROL_BANDPOWER] = action_group_->addAction(get(action_id_t::CONTROL_BANDPOWER));
get(action_id_t::CONTROL_MOUSE)->setChecked(true);
//调用
toolbar_->addAction(actions_->get_group(action_id_t::CONTROL_MOUSE));
toolbar_->addAction(actions_->get_group(action_id_t::CONTROL_MARKER));
toolbar_->addAction(actions_->get_group(action_id_t::CONTROL_BANDPOWER));
源码分析,找到问题的本质
下面值列举了关键的代码用于分析:
//addAction方法
QAction *QActionGroup::addAction(QAction* a)
{
Q_D(QActionGroup);
if(!d->actions.contains(a)) {
d->actions.append(a);
QObject::connect(a, SIGNAL(triggered()), this, SLOT(_q_actionTriggered()));
//关键在于这一句,将传入进来的QAction的状态改变信号和新的槽函数_q_actionChanged进行连接
//下面的代码我们就将分析 槽函数中的内容
QObject::connect(a, SIGNAL(changed()), this, SLOT(_q_actionChanged()));
QObject::connect(a, SIGNAL(hovered()), this, SLOT(_q_actionHovered()));
}
if(!a->d_func()->forceDisabled) {
a->setEnabled(d->enabled);
a->d_func()->forceDisabled = false;
}
if(!a->d_func()->forceInvisible) {
a->setVisible(d->visible);
a->d_func()->forceInvisible = false;
}
if(a->isChecked())
d->current = a;
QActionGroup *oldGroup = a->d_func()->group;
//并且从老的Group中移动出来,永远保存的只有最后创建出来的QActionGroup 对QAction的管理是有效的
if(oldGroup != this) {
if (oldGroup)
oldGroup->removeAction(a);
a->d_func()->group = this;
a->d_func()->sendDataChanged();
}
return a;
}
//槽函数
//当某个QAction的的状态改变时候,槽函数就是将其他的checked状态设置成为false
//所以出现bug原因就被找到了
void QActionGroupPrivate::_q_actionChanged()
{
Q_Q(QActionGroup);
QAction *action = qobject_cast<QAction*>(q->sender());
Q_ASSERT_X(action != nullptr, "QActionGroup::_q_actionChanged", "internal error");
if (exclusionPolicy != QActionGroup::ExclusionPolicy::None) {
if (action->isChecked()) {
if (action != current) {
if(current)
current->setChecked(false);
current = action;
}
} else if (action == current) {
current = 0;
}
}
}