QTreeView crashes (ItemSelectionChanged, QSortFilterProxyModel)
-
So i have a qtreeview subclass. Everything worked till i set the proxy filter model. Now it crashes as soon as i click an item in my treeview:
treeview.cpp:
#include <QApplication> #include <QFile> #include <QDataStream> #include <QJsonDocument> #include <QJsonObject> #include <QJsonArray> #include <QUndoCommand> #include <QSortFilterProxyModel> ViewLayerList::ViewLayerList(CustomGraphicsScene *scene, QWidget *parent) : QTreeView{parent}, scene_durchgereicht(scene) { setStyle(new ViewLayerDropIndicatorStyle(style())); // Ändern Sie den Abstand zwischen den Items und der vertikalen Scrollbar setViewportMargins(0, 0, 50, 0); // Passen Sie den rechten Rand (20) an Ihre Anforderungen an //Versteckt die sinnlose Kopfzeile setHeaderHidden(true); setRootIsDecorated(true); setMouseTracking(true); mydelegate = new ViewLayerItemDelegate(this); model = new ViewLayerStandartItemModel(0,1, this); this->setModel(model); this->setItemDelegate(mydelegate); this->setDragDropMode(QAbstractItemView::InternalMove); this->setSelectionMode(QAbstractItemView::ExtendedSelection); this->setDragEnabled(true); this->setAcceptDrops(true); this->setDropIndicatorShown(false); this->setMouseTracking(true); proxy = new QSortFilterProxyModel; proxy->setSourceModel(model); //proxy->setRecursiveFilteringEnabled(true); setModel(proxy); //Gibt das model an die GraphicsScene durch scene_durchgereicht->setModel(model); //Connect um die Textänderung im Delegaten an die Viewlayerlist zu übergeben und von hier aus //an ModulImage weiterzuverschicken (siehe Signalmanager Mainwindow.cpp) connect(mydelegate, &ViewLayerItemDelegate::SendItemNameChangedToViewLayerList, this, &ViewLayerList::ReceiveItemNameChangeFromDelegate); //Connect um die Textänderung im Delegaten an die Viewlayerlist zu übergeben und von hier aus //an ModulImage weiterzuverschicken (siehe Signalmanager Mainwindow.cpp) connect(mydelegate, &ViewLayerItemDelegate::SendViewLayerItemEditingFinishedToViewLayerList, this, &ViewLayerList::ReceiveDelegatedEditingISFinished); connect(scene_durchgereicht, &CustomGraphicsScene::SendSelectionChangedFromSceneToLayerList, this, &ViewLayerList::ReceiveSelectionChangedFromScene); connect(this, &ViewLayerList::SendGroupSelectionChangeToScene, scene_durchgereicht, &CustomGraphicsScene::ReceiveGroupSelectionChangeFromViewLayerList); connect(this->selectionModel(), &QItemSelectionModel::selectionChanged, this, &ViewLayerList::selectionChanged); } //Eine Funktion um von einem QStandardItem aus das dazugehörige GraphicsItem zu bestimmen, gibt ein ResizablePixmapItem zurück ResizablePixmapItem* ViewLayerList::FindGraphicsItemFromListItem(QStandardItem *ListItem){ QList<QGraphicsItem*> items = scene_durchgereicht->items(); ResizablePixmapItem* korrespondierendesGraphicsItem = nullptr; for (QGraphicsItem* item : items) { // Überprüfen Sie, ob das Item die gleiche Scene ID hat wie das Standard-Item mit der ListID if (ResizablePixmapItem* pixmapItem = qgraphicsitem_cast<ResizablePixmapItem*>(item)) { if (pixmapItem->getSceneID() == ListItem->data(ViewLayerStandartItemModel::ListIDRole).toInt()) { korrespondierendesGraphicsItem = pixmapItem; break; } } } // Überprüfen Sie, ob das korrespondierende GraphicsItem gefunden wurde, bevor Sie es weitergeben if (korrespondierendesGraphicsItem) { return korrespondierendesGraphicsItem; } else { // Handle den Fall, wenn das korrespondierende GraphicsItem nicht gefunden wurde return nullptr; } } void ViewLayerList::ReceiveSelectionChangedFromScene(const QList<QStandardItem*>& selectedItems) { // Zugriff auf das SelectionModel, um ausgewählte Items zu erhalten QItemSelectionModel* selectionModel = this->selectionModel(); if (selectionModel) { selectionModel->clearSelection(); // Alle vorherigen Auswahl aufheben // Durchlaufe alle übergebenen QStandardItems foreach (QStandardItem* selectedItem, selectedItems) { // Überprüfe, ob das Item gültig ist if (selectedItem) { // Finde den QModelIndex für das QStandardItem QModelIndex index = model->indexFromItem(selectedItem); // Überprüfe, ob der Index gültig ist if (index.isValid()) { // Wähle die Zeile aus selectionModel->select(index, QItemSelectionModel::Select); } } } } } void ViewLayerList::selectionChanged(const QItemSelection &selected, const QItemSelection &deselected) { //Setzt das selection Change der Scene auf true um Selektionupdates in der Scene zu verhindern scene_durchgereicht->isUpdatingSelection = true; // Zugriff auf das SelectionModel, um ausgewählte Items zu erhalten QItemSelectionModel *selectionModel1 = this->selectionModel(); qDebug() << "SELMODEL1" << selectionModel1; if (selectionModel1) { selectedIndexes.clear(); scene_durchgereicht->clearSelection(); //emit scene_durchgereicht->selectionChanged(); selectedIndexes = selectionModel1->selectedIndexes(); QList<QGraphicsItem*> SelectedGraphicsItemsList; if(selectedIndexes.count() > 0){ for (const QModelIndex &index : selectedIndexes) { QStandardItem *SelectedItem = model->itemFromIndex(index); //Ruft die Funktion zum finden des korrespondierenden GraphicsItems auf ResizablePixmapItem *CorrespondingGraphicsItem = FindGraphicsItemFromListItem(SelectedItem); if (CorrespondingGraphicsItem) { CorrespondingGraphicsItem->setSelected(true); SelectedGraphicsItemsList.append(CorrespondingGraphicsItem); } } } //Sendet für das GroupRect die Seleteten Items emit SendGroupSelectionChangeToScene(SelectedGraphicsItemsList); SelectedGraphicsItemsList.clear(); scene_durchgereicht->update(); } update(); //Setzt das selection Change der Scene auf falsch um Selektionupdates in der Scene wieder zu erlauben scene_durchgereicht->isUpdatingSelection = false; }
Is it normal that selectionModel selection changed triggers twice on startup of the programm? cause in my case it gives the qDebug() 2 times. When using the debugger it crashes on startup... When i remove the setModel(proxy) everything works fine.
1 ViewLayerList::selectionChanged ViewLayerList.cpp 318 0x7ff703c1e5b2 2 QAbstractItemView::setSelectionModel qabstractitemview.cpp 788 0x7fff0594c366 3 QTreeView::setSelectionModel qtreeview.cpp 245 0x7fff059c33a9 4 QAbstractItemView::setModel qabstractitemview.cpp 724 0x7fff0594a2b5 5 QTreeView::setModel qtreeview.cpp 203 0x7fff059c31ef 6 ViewLayerList::ViewLayerList ViewLayerList.cpp 73 0x7ff703c1d8b4 7 CustomDockableItemList::CustomDockableItemList CustomDockableItemList.cpp 38 0x7ff703c07f9f 8 MainWindow::MainWindow MainWindow.cpp 138 0x7ff703c05a3e 9 qMain main.cpp 8 0x7ff703c04616 10 qtEntryPoint qtentrypoint_win.cpp 50 0x7ff703c2c092 11 __tmainCRTStartup 0x7ff703c01395 12 WinMainCRTStartup 0x7ff703c014c6
-
@StudentScripter There is no need for the first
setModel
call. You instantiate your model, then you create the proxy model and set your original model as the source model. Then you use the proxy in your tree. Since you now know your are using a proxy model you cannot directly use the model indices as the are proxy related, not source model related. As @SGaist wrote you have to usemapToSource
for getting the correct content.
The documentation is quite good on that and I strongly suggest you study the within mentioned basic examples how proxy models work. That helped me a lot using the sort filter model in my app.
Aside from that on YouTube there is KDAB Qt Widgets and More. They have some videos about QTreeView and how to use QSortFilterProxyModels which are really worth watching. -
@StudentScripter said in QTreeView crashes (ItemSelectionChanged, QSortFilterProxyModel):
1 ViewLayerList::selectionChanged ViewLayerList.cpp 318 0x7ff703c1e5b2
It is crashing in your code so you should fix your code.
-
@StudentScripter why
setModel(model)
and then latersetModel(proxy)
? And are you sure yourViewLayerStandartItemModel
ist working correctly? You can useQAbstractItemModelTester
during runtime to see if you have basic problems in your model. -
@DerReisende The second one is a qsortfilterproxy model so first i have to retrieve the real model in order to instantiate and than set the filter model. Or how am i supposed to to do that?
EDIT: Well the problem seems to be that the filter proxy overrides my model and therefore everywhere when i used my model as reference before its now all wrong??? Is this normal
For example here it gives me: "EditedListItem 0x0 model ViewLayerStandartItemModel(0x1b6999e6440) indexAtMosue QModelIndex(0,0,0x1b6999d6e30,QSortFilterProxyModel(0x1b6999e65e0))"
//Erhält Live Buchstabe für Buchstabe ein Update von dem Delegaten void ViewLayerList::ReceiveItemNameChangeFromDelegate(const QString &Name) { QStandardItem *EditedListItem = model->itemFromIndex(indexAtMouse); qDebug() << "EditedListItem" << EditedListItem << "model" << model << "indexAtMosue" << indexAtMouse;
-
@StudentScripter Hi,
That's normal, when you call
setModel(proxy);
your view will replace its current model with the proxy model. CallingsetModel
multiple times does not "stack" them.You want to check mapToSource to get the proxied model's model index.
-
@StudentScripter There is no need for the first
setModel
call. You instantiate your model, then you create the proxy model and set your original model as the source model. Then you use the proxy in your tree. Since you now know your are using a proxy model you cannot directly use the model indices as the are proxy related, not source model related. As @SGaist wrote you have to usemapToSource
for getting the correct content.
The documentation is quite good on that and I strongly suggest you study the within mentioned basic examples how proxy models work. That helped me a lot using the sort filter model in my app.
Aside from that on YouTube there is KDAB Qt Widgets and More. They have some videos about QTreeView and how to use QSortFilterProxyModels which are really worth watching. -
@StudentScripter said in QTreeView crashes (ItemSelectionChanged, QSortFilterProxyModel):
and therefore everywhere when i used my model as reference before its now all wrong??
The answer is "some calls will now be wrong, some will still work, depends on the call". You probably do need to look through all your existing code to decide on a case-by-case basis.
You need to be aware that your view is now attached to the proxy model, not (directly) to the source model. The problem here is that the output shows your
indexAtMouse
is an index into theQSortFilterProxyModel
butmodel->itemFromIndex
(wheremodel
is the source model) needs the index to be in the same model asmodel
. Hence the need tomapToSource()
in this case.However, for example
proxyModel->data(proxyModelIndex)
will work as it used to when your model was only the source model, prior to you introducing the proxy model on top. If it's a proxy model the proxy's code will do the mapping to source for you, and return the source model's data.This is because QSFPM reimplements certain virtual methods to pass them through to base calls with needed mappings to/from source. You gather this by looking at e.g. https://doc.qt.io/qt-6/qsortfilterproxymodel.html#reimplemented-public-functions. You may also need to look at the base https://doc.qt.io/qt-6/qabstractproxymodel.html. Between the two of them you see that
data()
is reimplemented, so your original code works even if you call it on the proxy model. But you don't see that the proxy in any way reimplements QStandardItemModel::itemFromIndex() (it's not virtual, and it only belongs toQStandardItemModel
which is not the type for a QSFPM). So that means you need to deal with mapping to/from source/proxy in your code.Hopefully this clarifies what you need to think about.
-
@JonB @SGaist @DerReisende Thank you all very much. Thats kinda tedious to do but guess thats how it is. Appreciate all your help and indeed mapFrom and mapToSource are what fixes my issue. :D Y'all have a nice day.
-