PDA

View Full Version : selecting lots of treewidget itemns slow ...



jh
15th June 2006, 17:35
hi,

i have a QTreeWidget which shows the hierachy of a model. this model
has lots of items (>10000). the items can be selected in the tree widget and
in a 3D OpenGL view. when an item is selected in the 3D view the associated
treewidget item should be selected as well. this is done by reacting on a signal
and calling setItemSelected of the tree widget. well since selecting of items
by itself is quiet ok it gets really slow when i call setItemSelected.
how can i speed up selecting/deselecting of items in a qtreewidget?

i used the abstractview class before but found it too complicated, so i
would like to stay with QTreeWidget.

best regards,
jh

jpn
15th June 2006, 17:42
How do you find the appropriate item to be selected?

If you are setting many items selected/deselected in a loop, you can temporarily disable updates during the loop (see QWidget::setUpdatesEnabled(bool)) so the treewidget won't update it's contents after every single selection change.

jh
15th June 2006, 20:37
i have a qhash which stores the model item as a key and the treewidget item
as a value.

updates disabled did not help, even if the items to be selected are
not shown (parent collapsed) it does take a long time to call setItemSelected for
2500 items.

selecting the item by clicking (select the first, scroll to the end of the list,
press shift and select the last item) takes only some msecs, but calling
setItemSelected does take seconds!

jh

wysota
15th June 2006, 20:47
Can we see the code?

jh
15th June 2006, 20:58
// this method is connected to a signal which indicates that an opengl item
// has been changed
void Container_Widget::openglitem_changed(void* item)
{

if( !isVisible() ) {
return;
}

// if no opengl painter, do nothing
if(!_painter) return;

// cast void ptr to an opengl item
mesh3D::OpenGL_ListItem* opengl_item =
static_cast<mesh3D::OpenGL_ListItem*>(item);

// get a list which contains all treewidget items which are associated
// to the opengl item, the list is the value of a qhash, key is the

const QList<Container_TreeWidgetItem*>* list =
Container_TreeWidgetItem::items(opengl_item->element());

if(list == 0) return;

treeWidget->blockSignals(true);
// treeWidget->setUpdatesEnabled(false);

for(int i=0; i<list->count(); i++) {

// printf("[%d/%d] check treewidgetitem ... \n", i, list.count());

if(treeWidget->isItemSelected((*list)[i]) != opengl_item->selected()) {

// printf("[%d] change treewidgetitem ... \n", i);

// treeWidget->setItemSelected((*list)[i], opengl_item->selected());

}

}

// treeWidget->setUpdatesEnabled(true);
treeWidget->blockSignals(false);

#ifdef DEBUG_mesh3D_gui_Container_Widget
printf("</Container_Widget::openglitem_changed()>\n");
fflush(stdout);
#endif

}

jh
15th June 2006, 21:03
sorry, have not been finished commenting the code.




// this method is connected to a signal which indicates that an opengl item
// has been changed

void Container_Widget:openglitem_changed(void* item)
{

if( !isVisible() ) {
return;
}

// if no opengl painter, do nothing
if(!_painter) return;

// cast void ptr to an opengl item
mesh3D::OpenGL_ListItem* opengl_item =
static_cast<mesh3D::OpenGL_ListItem*>(item);

// get a list which contains all treewidget items which are associated
// to the opengl item, the list is the value of a qhash, key is the

const QList<Container_TreeWidgetItem*>* list =
Container_TreeWidgetItem::items(opengl_item->element());

if(list == 0) return;

treeWidget->blockSignals(true);
// treeWidget->setUpdatesEnabled(false);

for(int i=0; i<list->count(); i++) {
treeWidget->setItemSelected((*list)[i], opengl_item->selected());
}

// treeWidget->setUpdatesEnabled(true);
treeWidget->blockSignals(false);


}



this method is called for each changed item, which can be some thousand
times. as long as setItemSelected is commented out it's ok, calling setitemseleced
slows it dramatically down.

jh

jh
16th June 2006, 15:19
since i still i haven't found the problem, i wrote a small test program.
here's a screenshot:
http://www.pamidion.de/screenshot.png

it just creates 1000 items with 100 sub items each. if you select some
items and click on push button 'select children' the sub items get selected
by iterating over the children and call setItemSelect for each one.
for 100 items it takes 300msec on a Pentium M 1,8 GhZ !!! expanding items and
selecting them using the mouse is much faster !?

you can download the testprogram with an qmake project and makefiles
here: http://www.pamidion.de/treewidgettest.zip

any hints or any other way to select items in a qtreewidget ?

best regards,
jh

gfunk
16th June 2006, 19:49
Maybe you can run a profiler to see what is really taking the most time inside of setItemSelected(). It seems like you are really pushing Qt to its limits though!
Though, there is probably something about selecting a range of indexes in QItemSelection, QItemSelectionRange... I'm not sure how it quite works though, it probably operates similar to how you manually select the indexes with the mouse.

wysota
16th June 2006, 20:03
What exactly do you want to do? IMHO your code if far from being efficient. You create and iterate over something which seems to be a quite big list. Iterating over a list which has more than 100 items is not a good idea, IMO.

jh
16th June 2006, 21:13
iterating over the list is not the problem, the processing of the
method speeds up if not setItemSelected is called. in the testprogramm
i do nothing more than traversing the children of an item
and select one by one (the testprogram is different from the code
i showed in the previous posting).

in the meantime i changed the testprogram. i subclassed QTreeWidget
to get access to the protected methods itemFromIndex and indexFromItem.
having access to modelindexes i can assemble a QItemSelection and
can use the selectionmodel to select items. this only takes some 10 msecs
in the sample program instead of 300msecs. so the problem must be
somewhere in method setItemSelected or at least in the fact that
only one item can be selected at a time. nice side effect is that
i get a list of selected and deselected items from the selection model.

it would be very nice if indexFromItem/itemFromIndex were not
protected so i do not need to subclass QTreeWidget to get
public access to those methods.

best regards,
jh

ps:
why is iterating over a big list not a good idea? from the qt docs:


QList uses 0-based indexes, just like C++ arrays. To access the item at a particular index position, you can use operator[](). On non-const lists, operator[]() returns a reference to the item and can be used on the left side of an assignment:

if (list[0] == "Bob")
list[0] = "Robert";
Because QList is implemented as an array of pointers, this operation is very fast (constant time). For read-only access, an alternative syntax is to use at():

wysota
16th June 2006, 21:16
There are protected because you shouldn't touch them. If you want direct access to model indexes, use QTreeView instead of QTreeWidget.

jh
16th June 2006, 21:24
There are protected because you shouldn't touch them. If you want direct access to model indexes, use QTreeView instead of QTreeWidget.

in that case i have to implement the model as well. that is
exactly what i wanted to avoid. the model indexes i only need because
a selection model needs them. i would prefer to assemble a
QItemSelection by using QTreeWidgetItems.

jh

wysota
16th June 2006, 22:14
From what I've seen a model will only ease your job, as the "item" approach doesn't work well for complex situations (as you already discovered). Maybe you could use QStandardItemModel? Most situations can be handled by the model (or it can be subclassed and slightly changed).

jh
17th June 2006, 10:50
the problem with the model/view classes of qt in my case is that i always
need a row, a column and a parent. the core model (geometric elements
which are shown in an OpenGL view), which should be shown
also in the treeview do not store the parent of an element and since the hierachy of
the model is determined by sets (which include elements and also sets of elements)
the elements of the core model do not have an index. to find the row and the
parent needed for creating a model index i had to traverse the model. this is
to slow so i have to store a treeitem and its associated element.
it could be easier to use persistent modelindexes but the documentation is very poor
about the class QPersistentModelIndex so i came back to the item approach. an
item (subclass of QTreeWidgetItem) stores the associated element , so i don't
have to know its actual position in the model's hierachy.

so i came to the conclusion that with the current core model i 'abuse' the
item approach to implement an item model which stores and handles the
row/column/parent relationships needed for a treeview.

thanx and best regards,
jh

wysota
19th June 2006, 15:27
the problem with the model/view classes of qt in my case is that i always
need a row, a column and a parent.
No you don't. Parent is always optional (item can have an invalid parent) and you can access an item through a pointer or a number (see internalPointer() and internalId() methods of QAbstractItemModel).


the core model (geometric elements
which are shown in an OpenGL view), which should be shown
also in the treeview do not store the parent of an element and since the hierachy of
the model is determined by sets (which include elements and also sets of elements)
the elements of the core model do not have an index. to find the row and the
parent needed for creating a model index i had to traverse the model. this is
to slow so i have to store a treeitem and its associated element.

I don't know what exactly you mean by "the core model", but IMO you should have a single model only -- the "core model" should be a subclass of QAbstractItemModel.


it could be easier to use persistent modelindexes but the documentation is very poor
about the class QPersistentModelIndex so i came back to the item approach. an
item (subclass of QTreeWidgetItem) stores the associated element , so i don't
have to know its actual position in the model's hierachy.
Persistent indexes won't help you here.


so i came to the conclusion that with the current core model i 'abuse' the
item approach to implement an item model which stores and handles the
row/column/parent relationships needed for a treeview.

As I said -- make a single model.

jh
19th June 2006, 19:52
No you don't. Parent is always optional (item can have an invalid parent) and you can access an item through a pointer or a number (see internalPointer() and internalId() methods of QAbstractItemModel).


if parent is invalid it means that it is a root/toplevel item. the data i want to
show in a treeview has a hierachy but the children do not have any
special order, i.e. the child items do not have an index (row). the itemmodel
on the other hand, which is used to show the data in a treeview, must
obviously have an order. otherwise items could not be shown in a widget. consequently
i have to implement an item model which describes the hierachy (parent) and also
the order of childitems (row). this is done for a qtreewidget (item approach) by
trolltech anyway. so i use this one.


I don't know what exactly you mean by "the core model", but IMO you should have a single model only -- the "core model" should be a subclass of QAbstractItemModel.


the core model has nothing to do with any qt view class. it is the model that
describes the 'core data' of the application. in my case, the core model
consists of surfaces/triangles/points/lines etc.

but anyway, although i have to subclass qtreewidget to get access to
indexFromItem and itemFromIndex Qt is still a fantastic product. :)

best regards,
jh