Wierd behaviour with takeRow() of QtreeView.
-
So i have to call takeRow() twice in order to remove item from below it's parent. Thats something i found very strange and im sure this shouldn't be the case normally. But sadly i can't pinpoint why this is happening. So may someone of you guys can help me.
So let me show you me what i see:
On startup my treeview looks all fine just like this, filled with 3 named items:
I select both childitems with extended selection enabled for the qtreeview:
I drop the child items onto the parent item to create a hierachic structure:
Now is the part i implemented myself and where the problem occures: When i select both child item and drop them onto their parentitem again, they should be removed out of the parent and appended back to the invisibleRootItem and with my current code this works as shown below BUT
as soon as i remove the 2nd takeRow() line in my dropEvent:QList<QStandardItem*> items = parentItem->takeRow(row); //I remove this line below. With this line not commented out it works fine //but i don't understand why, and im sure this shouldn't be // parentItem->takeRow(row);
the endresult looks like this:
the childs just don't get removed with this 2nd parentItem->takeRow() line commented out.
I really don't understand why, so please if you have any idea why, tell me. <3
Here is the full code for my QTreeView:
#include "ViewLayerList.h" #include <QHBoxLayout> #include <QCheckBox> #include <QLabel> #include "ViewLayerLineEdit.h" #include <QMouseEvent> #include "resizablepixmapitem.h" #include "SignalManager.h" #include <QHeaderView> #include <QPushButton> #include "ViewLayerCustomItem.h" #include <QTimer> #include <QApplication> ViewLayerList::ViewLayerList(CustomGraphicsScene *scene, QWidget *parent) : QTreeView{parent}, scene_durchgereicht(scene) { // Ä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(false); setRootIsDecorated(true); setMouseTracking(true); //setFocusPolicy(Qt::StrongFocus); //setEditTriggers(QAbstractItemView::AllEditTriggers); mydelegate = new ViewLayerItemDelegate(this); model = new ViewLayerStandartItemModel(3,1,this); for(int row = 0; row < 3; ++row) { for(int col = 0; col < 1; ++col) { QModelIndex index = model->index(row, col, QModelIndex()); model->setData(index, ""); model->setData(index, true, ViewLayerStandartItemModel::CanHaveChildrenRole); // Dieses Element kann keine Kinder haben if(row == 0 && col == 0) { model->setData(index, false, ViewLayerStandartItemModel::CanHaveChildrenRole); // Dieses Element kann keine Kinder haben } } } this->setModel(model); this->setItemDelegate(mydelegate); this->setDragDropMode(QAbstractItemView::InternalMove); this->setSelectionMode(QAbstractItemView::ExtendedSelection); this->setDragEnabled(true); this->setAcceptDrops(true); this->setDropIndicatorShown(true); } void ViewLayerList::drawRow(QPainter *painter, const QStyleOptionViewItem &options, const QModelIndex &index) const { if (selectionModel()->isSelected(index)) { // Zeichnen Sie den Einzugsbereich (Indentation) mit Ihrer gewünschten Farbe painter->fillRect(options.rect, QColor(173, 216, 230)); // "lightblue" Hervorhebungsfarbe*/ } QTreeView::drawRow(painter, options, index); // Rufen Sie die Basisimplementierung auf, um die Standardzeichnung durchzuführen } void ViewLayerList::mousePressEvent(QMouseEvent *event) { // Ermitteln Sie den Index des Items unter der Maus QModelIndex index = indexAt(event->pos()); if (!index.isValid()) { // Wenn kein gültiges Item unter der Maus ist, rufen Sie die Basisklasse-Methode auf und kehren Sie zurück QTreeView::mousePressEvent(event); return; } // Holen Sie sich die ausgewählten Indizes QModelIndexList indexes = this->selectionModel()->selectedIndexes(); //qDebug() << "Selected Index: " <<indexes; if (indexes.size() > 1) { OnlyOneItemSelected = false; }else{ //qDebug() << "Index ist 1"; OnlyOneItemSelected = true; } const QRect itemRect = this->visualRect(index); //QPointF ClickPosition = event->position(); // press position in item's coords QPointF BetterClickPos = event->position() - itemRect.topLeft(); // press position in item's coords //qDebug() << "(Better) TREE View MousePress: " << BetterClickPos; //qDebug() << "TREE Item Rect: " << itemRect; if(BetterClickPos.x() >= itemRect.width()-35 && BetterClickPos.x() <= itemRect.width()-20) { // Zugriff auf das Modell ViewLayerStandartItemModel *modelObj = qobject_cast<ViewLayerStandartItemModel*>(this->model); // Überprüfen Sie, ob das Modell korrekt gecastet wurde if(modelObj) { if(OnlyOneItemSelected == false) { for (const QModelIndex &index : indexes) { bool value = index.data(Qt::CheckStateRole).toBool(); modelObj->setData(index, !value, Qt::CheckStateRole); } }else{ bool value = index.data(Qt::CheckStateRole).toBool(); modelObj->setData(index, !value, Qt::CheckStateRole); } mydelegate->setOtherIndexClicked(true); } //Returned ohne das mouseEvent zu handeln dadurch wird ein DoubleClicken über //Der Checkbox verhindert return; } QTreeView::mousePressEvent(event); mydelegate->setOtherIndexClicked(false); } void ViewLayerList::dragEnterEvent(QDragEnterEvent *event) { // Ermitteln Sie den Index des Items unter der Maus QModelIndex index = indexAt(event->position().toPoint()); if (!index.isValid()) { // Wenn kein gültiges Item unter der Maus ist, rufen Sie die Basisklasse-Methode auf und kehren Sie zurück QTreeView::dragEnterEvent(event); return; } // Zugriff auf das Modell QStandardItemModel *modelObj = dynamic_cast<QStandardItemModel*>(model); // Überprüfen Sie, ob das Modell korrekt gecastet wurde if (modelObj) { // Vor jedem Drag-Event leeren Sie die Parent-Index-Liste parentChildMap.clear(); selectedIndexList.clear(); // Sammeln Sie die Parent-Indizes für ausgewählte Items QModelIndexList selectedIndexes = selectionModel()->selectedIndexes(); for (const QModelIndex& selectedIndex : selectedIndexes) { QModelIndex parentIndex = selectedIndex.parent(); // Fügen Sie das Paar zur Map hinzu parentChildMap.insert(selectedIndex, parentIndex); // Fügen Sie den ausgewählten Index zur Liste hinzu selectedIndexList.append(selectedIndex); // Sortieren Sie die selectedIndexList mit einer benutzerdefinierten Vergleichsfunktion std::sort(selectedIndexList.begin(), selectedIndexList.end(), [](const QModelIndex &index1, const QModelIndex &index2) { return index1.row() > index2.row(); }); // Zugriff auf das ausgewählte Element und fügen Sie es zur Liste hinzu QStandardItem* selectedItem = modelObj->itemFromIndex(selectedIndex); } int selectedItemCount = selectedIndexes.count(); qDebug() << "Anzahl der ausgewählten Items: " << selectedItemCount; // Nachdem Sie die Map gefüllt haben... for(auto it = parentChildMap.constBegin(); it != parentChildMap.constEnd(); ++it) { qDebug() << "Child: " << it.key().data() << ", Parent: " << it.value().data(); } } QTreeView::dragEnterEvent(event); } void ViewLayerList::dropEvent(QDropEvent *event) { // Ermitteln Sie den Index des Items unter der Maus QModelIndex index = indexAt(event->position().toPoint()); if (!index.isValid()) { QTreeView::dropEvent(event); return; } QModelIndex destinationIndex = indexAt(event->position().toPoint()); // Rufen Sie die Basisklasse-Methode auf, um die Standard-Verarbeitung fortzusetzen QTreeView::dropEvent(event); // Zugriff auf das Modell QStandardItemModel *modelObj = dynamic_cast<QStandardItemModel*>(this->model); // Überprüfen Sie, ob das Modell korrekt gecastet wurde if(modelObj) { qDebug() << "destinationIndex data: " << destinationIndex.data(); // Überprüfen Sie, ob der Zielindex das Wurzelelement als Elternteil hat bool isDestinationRoot = (destinationIndex.parent() == QModelIndex()); qDebug() << "DestinationHasROOT as parent: " << isDestinationRoot; // Nachdem Sie das Ereignis verarbeitet haben... for (const QModelIndex& selectedIndex : selectedIndexList) { // Holen Sie sich den Elternindex für das ausgewählte Element QModelIndex parentIndex = parentChildMap.value(selectedIndex); // Zugriff auf das Elternelement QStandardItem* parentItem = modelObj->itemFromIndex(parentIndex); // Vergleichen Sie die Daten des Elternindex mit den Daten des Zielindex if(parentIndex.data() == destinationIndex.data() && isDestinationRoot) { // Entfernen Sie das Kind-Element aus seinem Elternelement int row = selectedIndex.row(); //Here i call takeRow() the 1st time QList<QStandardItem*> items = parentItem->takeRow(row); //THIS BELOW IS THE LINE I WAS TALKING ABOUT. As long as it's there everything is working just fine, but as soon as i comment it out i get this bad behaviour //*************************************************** parentItem->takeRow(row); // Bestimmen Sie die Position, an die das Element verschoben werden soll int newPosition = parentIndex.row() + 1; // Fügen Sie das Kind-Element an der neuen Position zum invisibleRootItem hinzu modelObj->invisibleRootItem()->insertRows(newPosition, items); } } } }
-
@StudentScripter
I don't understand: you show two rows selected/to be moved, so why would you think a singletakeRow(row)
would take more than one row? And ifrow
refers to the first row (1child
), then after it has been taken the samerow
number would now refer to what had been the second row (2child
). -
@JonB i think it should take 2rows cause it's a for loop, so for every element in this list (2 selected items) = 2items in the list it should perform 1 takerow:
for (const QModelIndex& selectedIndex : selectedIndexList) {
Also the rows are taken from the last row to the first row so that this does not happen:
"And if row refers to the first row (1child), then after it has been taken the same row number would now refer to what had been the second row (2child)."Therefore im using this to sort the list:
// Sortieren Sie die selectedIndexList mit einer benutzerdefinierten Vergleichsfunktion std::sort(selectedIndexList.begin(), selectedIndexList.end(), [](const QModelIndex &index1, const QModelIndex &index2) { return index1.row() > index2.row(); });
-
@StudentScripter
I don't know, you should perhaps put some debugging information in or run under debugger.One thought (only a thought): you are iterating through and moving around items that are currently selected. I would eliminate that, while you test. Get rid of any sorting, get rid of any selection lists. Just start with, say, 1 parent item and its 2 children for the whole of your model/tree. Get that working, ignoring "selections". When that's done move back to selections/sorting/whatever.