PDA

View Full Version : [SOLVED] qSort() and iterator const-ness(?)



zaphod.b
28th July 2010, 15:13
Hi all,

to give you an idea of what I am trying to do:
My application loads a bunch of perspective plugins. Each perspective provides an action to activate itself. All actions are added to a QActionGroup that resides inside PerspectiveManager. Before the actions are presented in a toolbar, they need to be sorted.

I am not able to sort the list of actions as provided by QActionGroup::actions(). So far, I tend to relate my problem to the iterator's const-ness, but I may be mistaken. Please have a look:


QToolBar *PerspectiveManager::toolBar()
{
//version 1 -- This doesn't sort:
//QAlgorithmsPrivate::qSortHelper(..) returns immediately
//due to RandomAccessIterator end < RandomAccessIterator start
qSort(actionGroup_.actions().begin(),
actionGroup_.actions().end(),
sortOrder);

//version 2 -- "Force non-const iterator" (?)
//This enters sortOrder(..), but QAction *one and two
//are not accessible there
QList<QAction*>::iterator it1 = actionGroup_.actions().begin();
QList<QAction*>::iterator it2 = actionGroup_.actions().end();
qSort(it1,
it2,
sortOrder);

toolBar_.clear();
toolBar_.addActions(actionGroup_.actions());
return &toolBar_;
}

//Not called when trying to sort as in version 1
bool PerspectiveManager::sortOrder(QAction *one, QAction *two)
{
//demonstrate that one and two are not accessible in version 2
//(running into SIGSEGV)
QString s1(one->text());
QString s2(two->text());

//sorting shall be done via meta object system
bool b = //one is somehow "less than" two

return b;
}

Bad thing is, I had it running in mock code, but am not getting there again... :confused:

Thanks for you consideration!

Lykurg
28th July 2010, 15:21
I think that actions() returns a temporary list which you sort, but when you call actions() again, the sort will be gone. So try to use
QList<QAction *> tmpList = actionGroup_.actions();
qSort(tmpList.begin(), tmpList.end(), sortOrder);
toolBar_.clear();
toolBar_.addActions(tmpList);

zaphod.b
28th July 2010, 15:27
Supplement:

When I change the sorting function's signature:


//before
bool PerspectiveManager::sortOrder(QAction *one, QAction *two)
//after - mark the references
bool PerspectiveManager::sortOrder(QAction *&one, QAction *&two)

at least the debugger can resolve the parameters' addresses (before it would only show "<not accessible>"). However I still run into a SIGSEGV.

zaphod.b
28th July 2010, 15:59
Hi Lykurg,


I think that actions() returns a temporary list which you sort, but when you call actions() again, the sort will be gone. So try to use
QList<QAction *> tmpList = actionGroup_.actions();
qSort(tmpList.begin(), tmpList.end(), sortOrder);
toolBar_.clear();
toolBar_.addActions(tmpList);


Perfectly right!
Edit: Well, I don't sort this way, but this explains why begin() and end() are in wrong order or rather totally unrelated.

Thx alot!

PS: How could i have found out? The QActionGroup doc doesn't say anything about temp'ness.

Lykurg
28th July 2010, 16:10
PS: How could i have found out? The QActionGroup doc doesn't say anything about temp'ness.


QList<QAction *> QActionGroup::actions () constSays that it returns a list, with is not related to the internal used one by QActionGroup.

QList<QAction *>& QActionGroup::actions () constThat would return a reference to the internal used list.

zaphod.b
28th July 2010, 16:17
Right, I was not careful enough and implicitly made assumptions that were not contracted by the interface. :(

perden
30th September 2010, 16:02
For anyone else stumbling into this thread, you can also sort actions that are already added to a menu, but some extra care must be taken.

The method shown in earlier posts will only work if the QActions are not owned by the QMenu you clear them from, or if they are shown in any other widget. Otherwise QMenu::clear() will delete them. So this means if you added them via QMenu::addAction() or a similar function, and don't use the action anywhere else in your application, you'll likely segfault.

You can work around this by temporarily setting the parent of all the actions in the list to NULL.



QMenu * menu;

// pull out actions already in the menu
QList<QAction *> actions = menu->actions();
// re-parent to null to avoid deletion and segfault
foreach(QAction * action, actions) {action->setParent(NULL);}

/*
* ... sort or other operations ...
*/

// clear out old actions (w/o deleting)
menu->clear();
// actions' parent is menu again
menu->addActions(actions);