QUndo/Redo crashes, help me investigate
-
So i have an qgraphicsscene and an qtreeview everytime i add an item to the graphicsscene i add an item to the treeview with my qundo addcommand. This works fine without errors. But as soon as i use my remove command i get an crash when i try to undo the remove. (Using QUndoView, QUndoStack, QUndoCommand, c++ qt)
Code looks like this:
#include "CommandManager.h" AddCommand::AddCommand(ResizablePixmapItem *SceneItem, QGraphicsScene *scene, ViewLayerStandartItemModel *model) : item_durchgereicht(SceneItem), scene_durchgereicht(scene), model_durchgereicht(model) { } void AddCommand::undo() { if(listItem_durchgereicht && model_durchgereicht){ model_durchgereicht->removeRow(listItem_durchgereicht->row(), QModelIndex()); //listItem_durchgereicht = new QStandardItem(); if (item_durchgereicht) { scene_durchgereicht->removeItem(item_durchgereicht); } } } void AddCommand::redo() { if(item_durchgereicht){ scene_durchgereicht->addItem(item_durchgereicht); } if(model_durchgereicht && scene_durchgereicht && item_durchgereicht && listItem_durchgereicht){ listItem_durchgereicht = new QStandardItem(); model_durchgereicht->appendRow(listItem_durchgereicht); AddCorrespondingLayerItem(item_durchgereicht, listItem_durchgereicht); } if(item_durchgereicht){ setText("[Added] item: UNAMED"); } } void AddCommand::AddCorrespondingLayerItem(QGraphicsItem *SceneItem, QStandardItem *ListItem) { if(model_durchgereicht) { //Casten ob es ein ResizablePixmapItem ist um den Namen zu retrieven ResizablePixmapItem* resizableItem = dynamic_cast<ResizablePixmapItem*>(SceneItem); if (resizableItem) { // Holen Sie sich den Index des neu hinzugefügten Elements QModelIndex index = model_durchgereicht->indexFromItem(ListItem); // Setzen Sie die Daten für das neue Element model_durchgereicht->setData(index, false, ViewLayerStandartItemModel::CanHaveChildrenRole); // Dieses Element kann keine Kinder haben QPixmap ItemIconPixmap = resizableItem->getPixmap(); // Ersetzen Sie "path/to/your/pixmap.png" durch den Pfad zu Ihrer Bilddatei QPixmap SCItemIconPixmap = ItemIconPixmap.scaled(32,32, Qt::KeepAspectRatioByExpanding, Qt::SmoothTransformation); int xOffset = (SCItemIconPixmap.width() - 32) / 2; int yOffset = (SCItemIconPixmap.height() - 32) / 2; QPixmap finalPixmap = SCItemIconPixmap.copy(xOffset, yOffset, 32, 32); model_durchgereicht->setData(index, finalPixmap, Qt::DecorationRole); QString ItemName = resizableItem->getControlName(); model_durchgereicht->setData(index, ItemName, Qt::EditRole); //Setzt für das Listenitem als ID den selben Integer Wert wie für das dazugehörige GraphicsItem aus der Scene qDebug() << "SceneID is: " << resizableItem->getSceneID(); model_durchgereicht->setData(index, resizableItem->getSceneID(), ViewLayerStandartItemModel::ListIDRole); model_durchgereicht->setData(index, !resizableItem->isVisible(), Qt::CheckStateRole); } } } RemoveCommand::RemoveCommand(ResizablePixmapItem *SceneItem, QGraphicsScene *scene, ViewLayerStandartItemModel *model) : scene_durchgereicht(scene), SceneItem_durchgereicht(SceneItem), model_durchgereicht(model) { } void RemoveCommand::undo() { qDebug() << "ListItemdurchgereicht undo" << listItem_durchgereicht; if(scene_durchgereicht && SceneItem_durchgereicht && model_durchgereicht && listItem_durchgereicht){ scene_durchgereicht->addItem(SceneItem_durchgereicht); if (listItem_durchgereicht->row() == -1) { model_durchgereicht->appendRow(listItem_durchgereicht); } } } void RemoveCommand::redo() { if(scene_durchgereicht && SceneItem_durchgereicht && model_durchgereicht){ listItem_durchgereicht = FindListItemFromGraphicsItem(SceneItem_durchgereicht); scene_durchgereicht->removeItem(SceneItem_durchgereicht); if(listItem_durchgereicht){ model_durchgereicht->removeRow(listItem_durchgereicht->row(), QModelIndex()); } setText(QStringLiteral("[Removed] %1").arg(SceneItem_durchgereicht->getControlName())); } qDebug() << "ListItemdurchgereicht redo" << listItem_durchgereicht; } QStandardItem *RemoveCommand::FindListItemFromGraphicsItem(QGraphicsItem *SceneItem) { ResizablePixmapItem *ResizableSceneItem = qgraphicsitem_cast<ResizablePixmapItem *>(SceneItem); if (ResizableSceneItem) { int targetID = ResizableSceneItem->getSceneID(); // Durchlaufe alle Zeilen im QStandardItemModel for (int row = 0; row < model_durchgereicht->rowCount(); ++row) { QStandardItem *item = model_durchgereicht->item(row); // Überprüfe, ob die ID übereinstimmt if (item->data(ViewLayerStandartItemModel::ListIDRole).toInt() == targetID) { return item; // Gefundenes Element zurückgeben } } } return nullptr; // Falls kein übereinstimmendes Element gefunden wurde }
May anyone can help me figure out whats going on? Been trying around for two weeks but can't find a solution. Thank you all so much. <3
I get the following in debugger:
1 QStandardItemPrivate::childIndex qstandarditemmodel_p.h 96 0x7ffdd73fa2ff 2 QStandardItemPrivate::position qstandarditemmodel.cpp 59 0x7ffdd73fa2ff 3 QStandardItem::row qstandarditemmodel.cpp 1474 0x7ffdd73fb08d 4 RemoveCommand::undo CommandManager.cpp 382 0x7ff6de0248b9 5 QUndoStack::setIndex qundostack.cpp 873 0x7ffdd750f395 6 doActivate<false> qobject.cpp 4004 0x7ffdd9fa22e3 7 QMetaObject::activate qobject.cpp 4052 0x7ffdd9cea353 8 QItemSelectionModel::currentChanged moc_qitemselectionmodel.cpp 783 0x7ffdd9ed643a 9 QItemSelectionModel::setCurrentIndex qitemselectionmodel.cpp 1398 0x7ffdd9ed643a 10 QAbstractItemView::mousePressEvent qabstractitemview.cpp 1823 0x7ffe061c388b 11 QWidget::event qwidget.cpp 9317 0x7ffe05fab288 12 QFrame::event qframe.cpp 515 0x7ffe06023704 13 QCoreApplicationPrivate::sendThroughObjectEventFilters qcoreapplication.cpp 1250 0x7ffdd9ca90c8 14 QCoreApplicationPrivate::sendThroughObjectEventFilters qcoreapplication.cpp 1255 0x7ffdd9ca9c39 15 QApplicationPrivate::notify_helper qapplication.cpp 3281 0x7ffe05f651c5 16 QApplication::notify qapplication.cpp 2774 0x7ffe05f6e48c 17 QCoreApplication::notifyInternal2 qcoreapplication.cpp 1118 0x7ffdd9caa5e8 18 QCoreApplication::sendSpontaneousEvent qcoreapplication.cpp 1550 0x7ffdd9caa5e8 19 QApplicationPrivate::sendMouseEvent qapplication.cpp 2358 0x7ffe05f6ce2a 20 QWidgetWindow::handleMouseEvent qwidgetwindow.cpp 623 0x7ffe05fb7a22 ... <More>
aswell as from my qdebug(): ListItemdurchgereicht redo 0x26c8b3f6b70,
ListItemdurchgereicht undo 0x26c8b3f6b70 -
When you call removeRow, the items contained in that row are deleted. You have to call takeItem before to be able to reuse it.
-
Hi,
Aren't you meddling with dangling pointers ? You have quite a lot of ifs testing non-null pointers but nowhere do you reset any of them.
-
In addition to @SGaist, can you post your headers and main.cpp as well?
-
@SGaist @Axel-Spoerl Thanks for tuning in you both i will post further files below, so may you can help me out whats going on. :D
Also how should i reset it? When i pass something like
SceneItem_durchgereicht = nullptr;
After the redo action for example the adding/removing can't be undone as far as i know. Im using the qundo framework.
Here is commandmanager.h:
#ifndef COMMANDMANAGER_H #define COMMANDMANAGER_H #include <QUndoCommand> #include <QGraphicsItem> #include <QStandardItem> #include "resizablepixmapitem.h" #include "ViewLayerStandartItemModel.h" #include <QGraphicsScene> #include <QLineEdit> class AddCommand :public QUndoCommand { public: AddCommand(ResizablePixmapItem *SceneItem, QGraphicsScene *scene, ViewLayerStandartItemModel *model); // QUndoCommand interface void undo() override; void redo() override; private: ResizablePixmapItem *item_durchgereicht; QGraphicsScene *scene_durchgereicht; QStandardItem *listItem_durchgereicht; ViewLayerStandartItemModel *model_durchgereicht; void AddCorrespondingLayerItem(QGraphicsItem *SceneItem, QStandardItem *ListItem); }; class RemoveCommand :public QUndoCommand { public: RemoveCommand(ResizablePixmapItem *SceneItem, QGraphicsScene *scene, ViewLayerStandartItemModel *model); // QUndoCommand interface void undo() override; void redo() override; //void setListItem(QStandardItem *item); private: ResizablePixmapItem *SceneItem_durchgereicht; QGraphicsScene *scene_durchgereicht; QStandardItem *listItem_durchgereicht; ViewLayerStandartItemModel *model_durchgereicht; QStandardItem *FindListItemFromGraphicsItem(QGraphicsItem *SceneItem); }; #endif // COMMANDMANAGER_H
Here is the graphicsscene.cpp where i push those to undoStack:
void CustomGraphicsScene::dropEvent(QGraphicsSceneDragDropEvent *event) { if(event->mimeData()->property("Key").canConvert(QMetaType(QMetaType::Int))){ int key = event->mimeData()->property("Key").toInt(); switch(key){ case 10: { clearSelection(); ResizablePixmapItem * pixItem = new ResizablePixmapItem(QPixmap(":/resource/quick.png")); pixItem->setRect(0,0,100,100); pixItem->setFlags(QGraphicsItem::ItemIsMovable | QGraphicsItem::ItemIsSelectable | QGraphicsItem::ItemSendsScenePositionChanges); //Erhöht den Counter für jedes gedroppete Item SceneItemCounter++; pixItem->setSceneID(SceneItemCounter); qDebug() << "SceneID: " << pixItem->getSceneID(); addCommand = new AddCommand(pixItem, this, model); undoStack->push(addCommand); //addItem(pixItem); pixItem->setPos(event->scenePos() - QPointF(pixItem->boundingRect().width()/2, pixItem->boundingRect().height()/2)); pixItem->setSelected(true); } break; } event->acceptProposedAction(); // Setzen Sie den Fokus auf die QGraphicsView, die diese Szene anzeigt. Wichtig um Delete Funktion //direkt nach dem Drop Funktionsfähig zu machen QList<QGraphicsView*> viewList = views(); if (!viewList.isEmpty()) { viewList.first()->setFocus(); } }else{ QGraphicsScene::dropEvent(event); } } void CustomGraphicsScene::DeleteItemFromSceneAndSendToLayerView(){ QList<QGraphicsItem *> selectedItemsList = selectedItems(); if (!selectedItemsList.isEmpty()) { // Iteriere durch alle ausgewählten Items for (QGraphicsItem *itemToRemove : selectedItemsList) { //Casten ob es ein ResizablePixmapItem ist um den Namen zu retrieven ResizablePixmapItem* resizableItem = dynamic_cast<ResizablePixmapItem*>(itemToRemove); if (resizableItem) { RemoveCommand *removeCommand = new RemoveCommand(resizableItem, this, model); undoStack->push(removeCommand); } } } }
-
When you call removeRow, the items contained in that row are deleted. You have to call takeItem before to be able to reuse it.
-
-
@StudentScripter When you set an item in the model, the model becomes the owner of the item and thus controls its lifetime. If calling removeRows would leave the item(s) it contains intact, you would just have a memory leak by now.