PDA

View Full Version : QTreeView + QSortFilterProxyModel and Selection problems in the view



Tepi
7th May 2010, 09:04
Hello again Qt people,

I try to keep this as short and simple as possible, but that's not guaranteed to happen ;)

Little bit background first, I have a model inherited from QAbstractItemModel and then I have QTreeView to show the content of the model in it (example for this have been taken from EditableTreeModel (http://doc.qt.nokia.com/4.6/itemviews-editabletreemodel.html)). I have made some modifications to this example to get the wanted data in the model. Also I have put the QSortFilterProxyModel wrapper around the model to get the sorting functionality working.

So the problem:
Everything seems fine at this point and I'm able to add items to the model and they pop up nice and clean to the QTreeView, as expected. Also to the right place according to the selected sorting.
When I select some item from the view I'll update my main view (which is just a widget to show the selected items data in it). As far it seems to work as it should.

Though, when I have some item selected from the view and that same item gets removed from the model I get some odd behavior. Selection is updated but it's not consistent which of the remaining item gets selected and what is even odder, the main view can be updated with different items data than which got selected. Usually the selection and main views content is updated correctly... but it seems that if the removed item is the first child of the root item, the selection and main view doesn't get updated correctly. In my model I just have the dummy root item which represents the header and it can have child items, but these root item's children don't have any children of their own, at least just now.

So the tree structure in model could look something like this:

Root Item (representing the header)
|--- Child1
|--- Child2
|--- Child3

Is there some possibility that the model indexes don't get updated correctly? Any other suggestions?

I won't post any code just yet, but if you guys could point out the direction to look the bug for, I could probably post some specific parts of the code.

-Tepi

Tepi
7th May 2010, 10:13
Here's also some code which could be part of the problem

Code for setting the view to use the model and creating QSortFilterProxyModel wrapper around the model:


void MainWindow::setServiceModel(ServiceModel *model)
{
treeModel = model;

// Create proxyModel and set it's properties
proxyModel = new QSortFilterProxyModel(this);
proxyModel->setDynamicSortFilter(true);
proxyModel->setFilterKeyColumn(0);
proxyModel->setSourceModel(treeModel);

serviceTree->setAlternatingRowColors(true);
serviceTree->setModel(proxyModel);
//serviceTree->header()->setStretchLastSection(true);
serviceTree->setSortingEnabled(true);
//proxyModel->sort(0, Qt::AscendingOrder);

connect(serviceTree->selectionModel(), SIGNAL(selectionChanged(const QItemSelection &, const QItemSelection &)),
this, SLOT(handleSelections(const QItemSelection &, const QItemSelection &)));
}


And the handleSelections slot which is connected to the selectionChanged signal from the selectionModel


void MainWindow::handleSelections(const QItemSelection &selected, const QItemSelection &deselected)
{
QModelIndexList indexes = selected.indexes();
QModelIndex index, i;
int row = -1;
ServiceItem *service = 0;

// Go through all the selected items
foreach (index, indexes) {
row = proxyModel->mapToSource(index).row();
i = treeModel->index(row, 0, QModelIndex());
service = static_cast<ServiceItem *>(i.internalPointer());

// Do something with the selected service
// This code should be rewritten if we decide to support multiple selections
// Foreach-loop is already supporting multiple selections
if(service)
{
statusBar()->showMessage(tr("Selected service: %1").arg(service->getServiceName()));
serviceInfoWidget->setCurrentServiceItem(service);
}
else
{
serviceInfoWidget->clearServiceInfo();
}
}
//}

// If none of the services in the tree is selected then clear the ServiceInfoWidgets content
if(indexes.isEmpty())
serviceInfoWidget->clearServiceInfo();
}


This one is the remove method from the model code


bool ServiceModel::removeService(ServiceItem *service)
{
bool success = false;

beginRemoveRows(QModelIndex(), service->childNumber(), service->childNumber());
success = rootItem->removeChildren(service);

// If removing the service is successful then remove the service's name from the name list
if(success)
serviceNames.removeOne(service->getServiceName());

endRemoveRows();

return success;
}


Don't know if these help but maybe someone can spot some mistakes here... or then it's related to some other things

If you have some assumptions for which functions (also another functions which aren't posted in this post) the problem might be relating, just point it out and I'll check if I can post that code in here

Tepi
10th May 2010, 14:13
Ok, I think i figured out the source of my problem...

I was updating the models content (by creating new items in it) in another thread and now it seems that when I do the adding in the same thread as where the model was created the model and the view are working as expected. I guess the problem is that the items live in the another thread than the model... am I on the right track here?

Is there a safe way to modify the models content or create new items for it in another thread? For now I'll leave the implementation as it is, at least it's working now.

This one was a quite nasty bug to find since in most cases it looked like it was working properly and because of that I wasn't suspecting the threading to cause the problem. So, word of warning to all who are doing same kind of things with threads ;)

Any suggestions are appreciated :)

-Tepi