QT: beginInsertRows bei root?
-
was ist bei dir rootItem?
Wird dieses Item im view angezeigt und soll der Benutzer auf ebene des root-items neue elemente erzeugen können?
-
rootItem erstelle ich im constructor selbst, das ist ein normales Item. Es soll nicht angezeigt werden und der Benutzer soll auch keine neuen Unterknoten erstellen können. Ich aber schon
-
Eisflamme schrieb:
rootItem erstelle ich im constructor selbst, das ist ein normales Item. Es soll nicht angezeigt werden und der Benutzer soll auch keine neuen Unterknoten erstellen können. Ich aber schon
Ok dann ist der Index doch klar.
beginInsertRows ( <QModelIndex des root-Items>, int first, int last )
und für das RootItem solltest du den index so bekommen:
index(0,0, QModelIndex())
-
Also ich habe jetzt (Variableninhalte als Zahlen dargestellt):
beginInsertRows(QModelIndex(0, 0, parent), 0, 0); parentItem->InsertChild(item, 0); //...
und er dumpt bei beginInsertRows... (Edit: sorry, der blöde gelbe Pfeil ist immer eine Zeile zu spät)
Der Callstack umfasst 8 Aufrufe oder so, aber so ziemlich am Ende ruft er parent auf:
QModelIndex TreeModel::parent( const QModelIndex& index ) const { if(!index.isValid()) return QModelIndex(); Item* childItem = static_cast<Item*>(index.internalPointer()); Item* parentItem = childItem->GetParent(); if(parentItem == rootItem.get()) return QModelIndex(); return createIndex(parentItem->GetRow(), 0, parentItem); }
Er läuft bis zur letzten Zeile createIndex, aber parentItem ist null, sodass GetRow() dumpt. Interessant ist hier aber, dass childItem in diesem Fall das rootItem vom einzufügenden Baum ist. Klar, dass der kein parent hat und gleichzeitig aber eben auch nicht rootItem ist, hm...
Edit:
Also das Problem scheint zu sein, dass index(0, 0, QModelIndex()) folgendes macht:Wenn der angezeigte Baum leer ist (rootItem also vorhanden), liefert index einfach einen Index ohne internalPointer zurück, d.h. offensichtlich nicht gültig für rootItem. Gibt es einen Eintrag im Baum, so wird ein Index auf den zurückgegeben (z.B. ist bei einem sichtbaren Element eben index(0, 0, QModelIndex()) der Index von eben diesem ersten Eintrag im Baum). Anders gesagt: index(0, 0, QModelIndex()) == QModelIndex() für 0 angezeigte Element im Baum...
daher glaube ich irgendwie auch, dass es keinen QModelIndex gibt, daher aber auch beginInsertRows/endInsertRows gar nicht erst aufgerufen werden muss?! Oder die haben den Fall nicht bedacht?
-
Eisflamme schrieb:
Also ich habe jetzt (Variableninhalte als Zahlen dargestellt):
beginInsertRows(QModelIndex(0, 0, parent), 0, 0);
das ist doch falsch. Das das überhaupt kompiliert ist mir ein rätzel. Denn QModelIndex(0, 0, parent) ist kein gültiger Konstruktor Aufruf (Was ist überhaupt parent...). Denn QModelIndex hat so einen Konstruktor gar nicht....
probiers mal mit
beginInsertRows(index(0, 0, QModelIndex()), 0, 0);
-
Eisflamme schrieb:
QModelIndex TreeModel::parent( const QModelIndex& index ) const { if(!index.isValid()) return QModelIndex(); Item* childItem = static_cast<Item*>(index.internalPointer()); Item* parentItem = childItem->GetParent(); if(parentItem == rootItem.get()) return QModelIndex(); return createIndex(parentItem->GetRow(), 0, parentItem); }
Er läuft bis zur letzten Zeile createIndex, aber parentItem ist null, sodass GetRow() dumpt. Interessant ist hier aber, dass childItem in diesem Fall das rootItem vom einzufügenden Baum ist. Klar, dass der kein parent hat und gleichzeitig aber eben auch nicht rootItem ist, hm...
Ah jetzt versteh ich, du hast ein Denkfehler. Wenn parent() für dein rootItem (bzw. dessen index)aufgerufen wird, dann musst du ein invaliden ModelIndex zurückliefern, da ja das rootItem selbst keine parent hat.
Dir fehlt eine abfrage ob childItem == rootItem.get() ist.
-
Was anderes. WO crasht es, wenn du
beginInsertRows(QModelIndex(), 0, 0);
aufrufst?
Bzw. wie sieht bei dir die komplette methode aus, in der du ein neues element ins model hinzufügst?
Edit: eventuell hilft dir auch das Qt beispiel weiter: http://qt-project.org/doc/qt-4.8/itemviews-editabletreemodel.html
Da wird das erzeugen von neuen zeilen durch den Benutzer gestartet aber das sollte ja nicht das Problem sein, dass auf deinen Anwendungsfall zu adaptieren.
-
tl;dr: siehe Edit 3
Sorry, das war ein Tippfehler, natürlich nutze ich da index statt QModelIndex mit den drei Parametern...
hm, okay, aber diese Abfrage ist in dem QT-Beispiel ja gar nicht drin?
In jedem Fall liefert aber QModelIndex() immer dasselbe wie index(0, 0, QModelIndex()), nämlich einen invaliden QModelIndex(), dessen internalPointer nie auf rootItem zeigt.
Aber er bricht jetzt trotz dieser Änderung zusammen, da er nämlich wiederum in beginInsertRows(parent, 0, 0) mit parent == QModelIndex() irgendwo...
So und jetzt hab ich schon das nächste Problem...
Bevor ich in das Model ganz viel neues Zeug reinschieße, möchte ich es gerne entleeren. Jetzt muss ich dazu sagen, dass beginRemoveRows(...) und endRemoveRows() neu sind... ja, die sollte man wohl beim Löschen immer angeben. Tu ich im Normalfall auch, nur bei diesem Reset tat ich es bislang nicht. Interessant ist jetzt aber, dass er bei endRemoveRows() meckert, weil er in qEmpty
if(numberOfChildren != 0) { beginRemoveRows(index(0, 0, QModelIndex()), 0, numberOfChildren - 1); for(unsigned int n = 0; n < numberOfChildren; ++n) rootItem->RemoveChildAt(0); endRemoveRows(); }
So und hier dumpt er gearde, weil endRemoveRows() das Signal clicked() auslöst, das ich fange... und das erhält einen QModelIndex, der zwar gültig ist, dessen internalPointer aber nicht gültig ist... eine Abfrage darauf, ob das Model einfach leer ist, hat dieses Problem jedoch erledigt.
Jetzt klappt also das remove, trotzdem läuft er über beginInsertRows(...) seltsamerweise in einen parent()-Aufruf rein, in dem childItem gleich dem Root des einzufügenden Nodes ist... ich versteh gar nichts mehr, wieso kennt er den überhaupt? Der sollte eigentlich als gelöscht gelten... ist er auch, zumindest auf Seite des eigentlichen Trees unter rootItem.
Edit: Danke für den Link, ich schau mir das Mal im Detail an.
Edit2: Hm, das Problem könnte auch sein, dass ich einen Unterbaum mit weiteren Unterbaumelementen einfüge. Ich füge halt einen gesamten Baum in meinen Baum ein. Für Drag&Drop klappt das jedoch problemlos...
Edit 3: Jetzt geht's... das Problem war, dass ich erst das gesamte Modell gelöscht hab, beginRemoveRows und endRemoveRows aber nicht ausreichend waren, um ads interne Modell upzudaten... d.h. obwohl ich das gelöscht habe und das auch mit den zwei begin/end-Methoden kenntlich gemacht habe, hat er beim Einfügen gedacht, dass diese Elemente noch drin waren.
Ein reset() nach dem Löschen hat das Problem behoben, persistentModel ist dann nämlich korrekterweise leer und das Einfügen funktioniert ebenfalls problemlos.
Vielen Dank für die Hilfe, jetzt funktioniert es endlich problemlos (fürs Erste). Falls mir noch jemand erklären kann, wieso begin/endRemoveRows nicht ausreichen, um das interne Modell upzudaten, wäre ich sehr froh.
-
Eisflamme schrieb:
Vielen Dank für die Hilfe, jetzt funktioniert es endlich problemlos (fürs Erste). Falls mir noch jemand erklären kann, wieso begin/endRemoveRows nicht ausreichen, um das interne Modell upzudaten, wäre ich sehr froh.
Für deinen Fall (komplettes leeren des Models) ist beginResetModel()/endResetModel die bessere Wahl.
die beginXX/endXX methoden dienen hauptsächlich dafür einem View(oder proxy), welche das Model verwenden, anstehende Änderungen im Model selbst mitzuteilen.
Was meinst du mit "das interne Modell"? Das TreeModel, welches du implementiert hast, ist doch das "interne Modell".
-
Vll. ist es auch das angezeigte, aber ohne reset läuft er irgendwann bei beginInsertRows in einen Node, der eigentlich bereits gelöscht wurde. Bei dem guckt er auf den parent und der ist dann nullptr und da er von dessen parent wieder irgendwas möchte (die Änderung mit dem ChildItem habe ich in parent() bereits eingefügt), dumpt er dort.
Keine Ahnung, was das für ein Model ist, intern bezeichnet der das als persistentModel (irgendwo in den Tiefen des Debuggers)...
Edit: begin/endResetModel funktionieren übrigens prima, danke