dgermann
31st May 2013, 10:54
Hello
I have some issues with updating a QTreeView correctly when the model, connected to the view through one or two proxy models, inserts or removes items.
The overall purpose is to have a view on the model that can switch between showing the model as a tree structure and as a flat structure (showing all items, leaves and internal nodes, in a table). It should also be able to sort both views. The model itself has a tree structure (which should not actually be converted into a flat structure).
My implementation uses the following classes:
Model, which is derived from QAbstractItemModel and provides a tree-structured model
Viewer, which is derived from QTreeView and looks onto the model
ViewerProxyModel, which is derived from QSortFilterProxyModel
TreeToTableProxyModel, which translates the tree structure of the model to a flat structure
The Viewer always looks onto an instance of ViewerProxyModel, which in turn either directly looks onto an instance of Model, or, if a flat hierarchy is desired, first onto an instance of TreeToTableProxyModel, which finally looks onto an instance of Model.
Now, my problem is that in the case where the TreeToTableProxyModel is used, the Viewer does not update properly if items are inserted into or removed from the Model instance. To handle these updates, I implemented the following methods:
void TreeToTableProxyModel::sourceRowsAboutToBeInserted (const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
QModelIndex modelStartIndex = sourceModel()->index(aStart, 0, aParent);
QModelIndex modelEndIndex = sourceModel()->index(aEnd, 0, aParent);
QModelIndex proxyStartIndex = mapFromSource(modelStartIndex);
QModelIndex proxyEndIndex = mapFromSource(modelEndIndex);
beginInsertRows(proxyStartIndex.parent(), proxyStartIndex.row(), proxyEndIndex.row());
//beginResetModel();
}
}
void TreeToTableProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
QModelIndex modelStartIndex = sourceModel()->index(aStart, 0, aParent);
QModelIndex modelEndIndex = sourceModel()->index(aEnd, 0, aParent);
QModelIndex proxyStartIndex = mapFromSource(modelStartIndex);
QModelIndex proxyEndIndex = mapFromSource(modelEndIndex);
beginRemoveRows(proxyStartIndex.parent(), proxyStartIndex.row(), proxyEndIndex.row());
//beginResetModel();
}
}
void TreeToTableProxyModel::sourceRowsInserted(const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
//emit layoutAboutToBeChanged();
mMapFromSource.clear();
mMapToSource.clear();
createMap(...);
//emit layoutChanged();
endInsertRows();
//endResetModel();
}
}
void TreeToTableProxyModel::sourceRowsRemoved(const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
//emit layoutAboutToBeChanged();
mMapFromSource.clear();
mMapToSource.clear();
createMap(...);
//emit layoutChanged();
endRemoveRows();
//endResetModel();
}
}
which are connected to the appropriate signals of the Model instance:
connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)), this, SLOT(sourceRowsAboutToBeInserted(const QModelIndex&, int, int)));
etc. in the setSourceModel() method of TreeToTableProxyModel.
My problem is that I need to recompute the mapping of the TreeToTableProxyModel if items are inserted or removed. But when recomputing the mapping, I should probably emit layoutAboutToBeChanged() and layoutChanged(). This has to be done between beginInsertRows() and endInsertRows() or beginRemoveRows() and endRemoveRows(), which probably interferes with each other. Anyway, this solution does not update the Viewer properly. I did also test the variant without beginInsertRows()... and layoutChanged()... and directly using beginResetModel() and endResetModel(). This works almost fine. The only problem here (apart from the fact that it may be an overkill to reset the whole model) is that the sorting is not completely restored. If I first sort by column A and then by column B, items with the same value in column B remain sorted according to A. This secondary sorting is lost once the model has been reset.
I suppose I could use the beginReset() and endReset() variant and manually sort again by the correct columns. But this does not seem like the proper solution. How should I implement this?
I have some issues with updating a QTreeView correctly when the model, connected to the view through one or two proxy models, inserts or removes items.
The overall purpose is to have a view on the model that can switch between showing the model as a tree structure and as a flat structure (showing all items, leaves and internal nodes, in a table). It should also be able to sort both views. The model itself has a tree structure (which should not actually be converted into a flat structure).
My implementation uses the following classes:
Model, which is derived from QAbstractItemModel and provides a tree-structured model
Viewer, which is derived from QTreeView and looks onto the model
ViewerProxyModel, which is derived from QSortFilterProxyModel
TreeToTableProxyModel, which translates the tree structure of the model to a flat structure
The Viewer always looks onto an instance of ViewerProxyModel, which in turn either directly looks onto an instance of Model, or, if a flat hierarchy is desired, first onto an instance of TreeToTableProxyModel, which finally looks onto an instance of Model.
Now, my problem is that in the case where the TreeToTableProxyModel is used, the Viewer does not update properly if items are inserted into or removed from the Model instance. To handle these updates, I implemented the following methods:
void TreeToTableProxyModel::sourceRowsAboutToBeInserted (const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
QModelIndex modelStartIndex = sourceModel()->index(aStart, 0, aParent);
QModelIndex modelEndIndex = sourceModel()->index(aEnd, 0, aParent);
QModelIndex proxyStartIndex = mapFromSource(modelStartIndex);
QModelIndex proxyEndIndex = mapFromSource(modelEndIndex);
beginInsertRows(proxyStartIndex.parent(), proxyStartIndex.row(), proxyEndIndex.row());
//beginResetModel();
}
}
void TreeToTableProxyModel::sourceRowsAboutToBeRemoved( const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
QModelIndex modelStartIndex = sourceModel()->index(aStart, 0, aParent);
QModelIndex modelEndIndex = sourceModel()->index(aEnd, 0, aParent);
QModelIndex proxyStartIndex = mapFromSource(modelStartIndex);
QModelIndex proxyEndIndex = mapFromSource(modelEndIndex);
beginRemoveRows(proxyStartIndex.parent(), proxyStartIndex.row(), proxyEndIndex.row());
//beginResetModel();
}
}
void TreeToTableProxyModel::sourceRowsInserted(const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
//emit layoutAboutToBeChanged();
mMapFromSource.clear();
mMapToSource.clear();
createMap(...);
//emit layoutChanged();
endInsertRows();
//endResetModel();
}
}
void TreeToTableProxyModel::sourceRowsRemoved(const QModelIndex& aParent, int aStart, int aEnd)
{
if (sourceModel())
{
//emit layoutAboutToBeChanged();
mMapFromSource.clear();
mMapToSource.clear();
createMap(...);
//emit layoutChanged();
endRemoveRows();
//endResetModel();
}
}
which are connected to the appropriate signals of the Model instance:
connect(sourceModel(), SIGNAL(rowsAboutToBeInserted(const QModelIndex&, int, int)), this, SLOT(sourceRowsAboutToBeInserted(const QModelIndex&, int, int)));
etc. in the setSourceModel() method of TreeToTableProxyModel.
My problem is that I need to recompute the mapping of the TreeToTableProxyModel if items are inserted or removed. But when recomputing the mapping, I should probably emit layoutAboutToBeChanged() and layoutChanged(). This has to be done between beginInsertRows() and endInsertRows() or beginRemoveRows() and endRemoveRows(), which probably interferes with each other. Anyway, this solution does not update the Viewer properly. I did also test the variant without beginInsertRows()... and layoutChanged()... and directly using beginResetModel() and endResetModel(). This works almost fine. The only problem here (apart from the fact that it may be an overkill to reset the whole model) is that the sorting is not completely restored. If I first sort by column A and then by column B, items with the same value in column B remain sorted according to A. This secondary sorting is lost once the model has been reset.
I suppose I could use the beginReset() and endReset() variant and manually sort again by the correct columns. But this does not seem like the proper solution. How should I implement this?