Issue
I have a number of actions put together in a QActionGroup, because I want them to be exclusive (if one is checked, all the others should be unchecked). However, I also want to be able to uncheck all of them. At the moment if I check action1, and then click the button again, it will not uncheck, the only way to uncheck it is to click on a different action. In short, QActionGroup ensures exactly 1 of the actions is checked - I want to ensure exactly 1 OR 0 actions are checked. Is there a workaround for this?
I'm using PySide but I'm fairly comfortable with QT in c++ as well, so a solution in c++ would be fine - I'm sure I could convert it.
Code (not very helpful, but I'm posting in case it is relevant)
def defineGroups(self):
navigation = QActionGroup(self)
navigation.addAction(self.action1)
navigation.addAction(self.action2)
navigation.addAction(self.action3)
navigation.addAction(self.action4)
navigation.addAction(self.action5)
navigation.addAction(self.action6)
navigation.addAction(self.action7)
All the actions are checkable (set in QT Designer).
Related: There is a bug report similar to this, but the designers have decided not to change anything: https://bugreports.qt-project.org/browse/QTBUG-3512
Solution
I am using Qt in C++ instead of Python, but I hope these information could help.
Unfortunately there seems no direct solution to this problem at present. The "exclusive" property of QActionGroup
limits the feature of selection, which ensures at least and only one action is selected at any one time (but at the begining of the program, you have to manually set one of actions to be "checked")
Here is part of the source code, you can see how it works on QAction
if the "exclusive" property is installed by QActionGroup
{
if ( !isExclusive() )
return;
QAction* s = (QAction*) sender();
if ( b ) {
if ( s != d->selected ) {
d->selected = s;
for ( QPtrListIterator<QAction> it( d->actions); it.current(); ++it ) {
if ( it.current()->isToggleAction() && it.current() != s )
it.current()->setOn( FALSE );
}
emit activated();
emit selected( s );
} else if ( !s->isToggleAction() ) {
emit activated();
}
} else {
if ( s == d->selected ) {
// at least one has to be selected
s->setOn( TRUE );
}
}
}
(Ref: http://qt-x11-free.sourcearchive.com/documentation/3.3./classQActionGroup_182fe3a5a859e6397a4ba05be346d894.html older version of Qt, but the concept is the same)
To solve the problem, at least you have two options:
Still use
QActionGroup
and add anotherQAction
"None is selected" (or something similar) in your list. This might be the most proper option since it considers "no selection" as a selection and can exploit the feature ofQActionGroup
.Not to use
QActionGroup
. In this case, you have to manually implement the exclusiveness. Once aQAction
is selected,emit
a signal that sends a pointerQAction*
to aSLOT
which compares the present selection with the previous one from buffer (default = 0), if difference occurs, deselect (usesetCheck(false)
) the previous one and assign the present selection to buffer.
It's just a rough idea, but still feasible if you really need the feature
[EDIT]: Though I am not sure about the exact functionality you want, I wrote some C++ code here FYR. Notice that this is just a demo of the "ostensible" feature of the exclusively checkable QActions and the ability of unchecking any checked one.The according responses of checked/unchecked state still need further implementation.
TheClass.h:
class TheClass
{
Q_OBJECT
......
public slots:
void action0Checked();
void action1Checked();
void action2Checked();
void compareChecked(QAction *newChecked);
signals:
void selectedID(QAction *newChecked);
private:
QAction *buffer;
QList<QAction*> actionList;
}
TheClass.cpp:
TheClass::TheClass()
{
QMenuBar *menuBar = new QMenuBar(this);
buffer = 0;
QMenu *menu = menuBar->addMenu("ActionList");
actionList.append(menu->addAction("Action0"));
actionList.append(menu->addAction("Action1"));
actionList.append(menu->addAction("Action2"));
for (int i=0; i<3; i++) {
actionList[i]->setCheckable(true);
}
connect(actionList[0], SIGNAL(triggered()), this, SLOT(action0Checked()));
connect(actionList[1], SIGNAL(triggered()), this, SLOT(action1Checked()));
connect(actionList[2], SIGNAL(triggered()), this, SLOT(action2Checked()));
connect(this, SIGNAL(selectedID(QAction*)), this, SLOT(compareChecked(QAction*)));
}
void TheClass::action0Checked()
{
if (actionList[0]->isChecked()) {
// do sth.
emit selectedID(actionList[0]);
}
}
void TheClass::action1Checked()
{
if (actionList[1]->isChecked()) {
// do sth.
emit selectedID(actionList[1]);
}
}
void TheClass::action2Checked()
{
if (actionList[2]->isChecked()) {
// do sth.
emit selectedID(actionList[2]);
}
}
void TheClass::compareChecked(QAction *newChecked)
{
if (newChecked != buffer) {
if (buffer != 0)
buffer->setChecked(false);
buffer = newChecked;
}
}
Answered By - Tay2510
0 comments:
Post a Comment
Note: Only a member of this blog may post a comment.