Qt: ItemModel-Inhalt in Datei speichern und wieder einlesen?
-
Ich rätsele schon einige Zeit daran herum, wie ich den Inhalt eines QStandardItemModels schnell und effektiv "konservieren", also in einer Datei auf der Festplatte ablegen kann. Das Auslesen jedes einzelnen Items (und das spätere Wiedereinlesen) ist da hoffentlich nicht die beste Lösung, oder?
Vielleicht kann mir ja jemand Rat geben, das würde mir sehr helfen.
-
Stiefel2000 schrieb:
Das Auslesen jedes einzelnen Items (und das spätere Wiedereinlesen) ist da hoffentlich nicht die beste Lösung, oder?
Was bleibt dir denn anderes übrig? Außer dich interessieren nicht alle Daten.
Wie du die Daten ablegst bleibt dir überlassen und hängt davon ab wie komplex die Daten sind. (ini, xml, json, ...)
-
Mich interessieren wirklich nicht alle Daten. Ich hatte gehofft, dass es einen Weg gäbe, da effektiver ranzugehen. Wenn ich z.B. ein struct habe, kann ich das extrem einfach in einer Datei speichern und ebenso einfach komplett wieder einlesen. Nun ist eine Klasse zwar etwas komplizierter, aber ich hatte gehofft, dass Qt da etwas bieten würde.
-
Du kannst operator>>/operator<< überladen, das ist auch nicht wirklich kompliziert...
Und anstatt QStandardItemModel zu nehmen (was sicher nicht so einfach zu serialisieren ist, mit den ganzen möglichen children und columns usw.) schreibst du dir ein eigenes Model (was nicht so schwer ist), und führst intern eine eigene Datenstruktur mit (schau dir dazu das simpletreemodel-Beispiel an). Mit passenden operator>> /operator<< ist dann ratzfatz eingelesen/ausgegeben.Aber prinzipiell kannst du natürlich auch ein QStandardItemModel speichern und einlesen.
-
Puh, Operatoren habe ich noch nie überladen. Und das Model zu wechseln würde jetzt unglaublich viel Arbeit machen (ich benötige eine ganze Reihe der vorgegebenen Funktionen).
Der Gedanke mit der Ausgabe über QMetaType (ich glaube, das war das mit der serialisierten Ausgabe) kam mir auch schon. Nur haben da eben die Operatoren gefehlt :P.
Kannst du mir eine halbwegs sichere Herangehensweise ans Überladen empfehlen? Und: Würde dann das Aus- und Einlesen wirklich schneller gehen oder wird nur der Code kürzer?
-
Stiefel2000 schrieb:
Das Auslesen jedes einzelnen Items (und das spätere Wiedereinlesen) ist da hoffentlich nicht die beste Lösung, oder?
l'abra d'or schrieb:
operator>>/operator<< überladen
Was macht denn http://doc.trolltech.com/4.6/qstandarditem.html#operator-lt-lt-140 ? Wenn ich das richtig sehe brauchst du bloss eine Methode die das invisible root item deines QStandardItemModels ermittelt und dann alle items rekursiv in die Datai packt?
Ich spinn hier mal bisser, keine c++ hier zum testen (pseudo xml):
bool DeineKlasseMitModelDrin::saveModel(QTextStream * ostr = 0, QStandardItem * item = 0, int depth = 0) { if (ostr == 0) { QFile file("out.txt"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {return false;} QTextStream out(&file); item = deinModel->invisibleRootItem(); out << "\n<model>\n"; if ( ! saveModel(&out, item)) {return false;} out << "</model>\n if (out.status() == QTextStream::Ok) {return true;} return false; } int rows = item->rowCount(); int cols = item->colCount(); for (int r=0;r < rows; ++r) { // check once each row if ostr is still good() to use if (ostr.status() != QTextStream::Ok) {return false;} ostr << QString("<row depth=%1 count=%2>\n").arg(depth).arg(r); for (int c=0;c < cols; ++c) { ostr << QString("<col depth=%1 count=%2>\n").arg(depth).arg(c); saveModel(ostr,deinModel->item(r,c),depth+1); ostr << QString("\n</col>\n"); } ostr << QString("</row>\n"); } if (ostr.status() == QTextStream::Ok) return true; return false; }
-
Danke erstmal. Eine solche Methode kann ich implementieren - allerdings verstehe ich deinen Quelltext nicht ganz: Wird denn an irgendeiner Stelle mal auf den Inhalt eines Items zugegriffen, um diesen zu speichern? Warum genau ordnest du rows und cols die Anzahl der jeweiligen child-items zu? Das wäre bei mir momentan immer Null, ich müsste den Daten eine ganz bestimmte Struktur aufpressen, damit die Schleife a) auch anläuft und b) bzw. c) jedes Item genau einmal erfasst wird.
Der "<<"-Operator der Items wird gar nicht verwendet - ich habe leider auch keine Vorstellung, wie ich ihn anwenden könnte (rein syntaktisch, da bräuchte ich ein Beispiel). Außerdem bin ich mir nicht sicher, inwiefern ich so eine Aneinanderreihung von Item-Daten hinterher noch lesen könnte. Mit der XML-Formatierung ließe sich zwar etwas machen, aber das bläht die Datei enorm auf und scheint mir leider nicht effizienter als das reine Auslesen "per Hand" zu sein, da ich ja stets den Dateiinhalt parsen muss, bevor ich damit arbeiten kann.Falls ich den eintscheidenden Punkt an deinem Code übersehen habe und du mir das etwas näher erklären könntest, würde mir das vielleicht helfen.
-
Stiefel2000 schrieb:
Der "<<"-Operator der Items wird gar nicht verwendet - ich habe leider auch keine Vorstellung, wie ich ihn anwenden könnte (rein syntaktisch, da bräuchte ich ein Beispiel).
Vergiss den ersten code
ich sotlle nix posten wenn ich es nicht testen kann. Versuchs so :
Diesmal ohne XML aber immer noch nur Textformat ... Child-Beziehungen ergeben sich durch Einrückung mittels '\t':
// mw.h /////////////////////////////////////////////////////////// #ifndef MW_H #define MW_H #include <QtGui> class MW : public QWidget { Q_OBJECT public: // fürs testem MW(QWidget *parent = 0) : QWidget(parent) { // flaches Model model1 = new QStandardItemModel(4,4,this); model1->setObjectName("m1"); for (int row = 0; row < 4; ++row) { for (int column = 0; column < 4; ++column) { QStandardItem *item = new QStandardItem(QString("r %0, c %1").arg(row).arg(column)); model1->setItem(row, column, item); } } // tree Model model2 = new QStandardItemModel; model2->setObjectName("m2"); QStandardItem *parentItem = model2->invisibleRootItem(); for (int i = 0; i < 4; ++i) { QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); parentItem->appendRow(item); parentItem = item; } // combined Model // added colums somehow are converted to rows ? model3 = new QStandardItemModel; model3->setObjectName("m3"); parentItem = model3->invisibleRootItem(); for (int i = 0; i < 4; ++i) { QStandardItem *item = new QStandardItem(QString("item %0").arg(i)); parentItem->appendRow(item); QList<QStandardItem*> colList; for (int column = 0+i*2; column < 2+i*2; ++column) { colList << new QStandardItem(QString("appendColumnItem %1").arg(column)); } item->appendColumn(colList); parentItem = item; } QVBoxLayout * l = new QVBoxLayout(this); view1 = new QTreeView(this); view1->setModel(model1); view2 = new QTreeView(this); view2->setModel(model2); view3 = new QTreeView(this); view3->setModel(model3); l->addWidget(view1); l->addWidget(view2); l->addWidget(view3); setLayout(l); } ~MW(); bool saveModel(QStandardItemModel * mod, int depth = 0) { QFile file("out_"+mod->objectName()+".txt"); if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {return false;} QTextStream out(&file); QStandardItem * item = mod->invisibleRootItem(); outChild(item,out) ; return (out.status() == QTextStream::Ok); } void outChild(QStandardItem * item, QTextStream &out, int depth=0) { // neue Zeile falls column==0 if (item->column()==0) { out << "\n"; } // einrückung um depth // keyword // the displayrole -- could use UserDate+n for specials out << QString(depth,'\t') << "DATA: " << item->data(Qt::DisplayRole).toString(); if (item->hasChildren()) { for(int r=0;r< item->rowCount();++r) { for(int c=0;c < item->columnCount();++c) { outChild(item->child(r,c),out,depth+1); } } } } QStandardItemModel *model1, *model2, *model3; private: QTreeView *view1, *view2, *view3; }; #endif // MW_H // mw.cpp /////////////////////////////////////////////////////////// #include "mw.h" MW::~MW() { }
#include <QtGui/QApplication> #include <QtGui> #include <QDebug> #include "mw.h" int main(int argc, char *argv[]) { QApplication a(argc, argv); MW w; w.saveModel(w.model1); w.saveModel(w.model2); w.saveModel(w.model3); w.show(); return a.exec(); }
Dateiausgabe:
// flaches modelDATA: DATA: r 0, c 0 DATA: r 0, c 1 DATA: r 0, c 2 DATA: r 0, c 3 DATA: r 1, c 0 DATA: r 1, c 1 DATA: r 1, c 2 DATA: r 1, c 3 DATA: r 2, c 0 DATA: r 2, c 1 DATA: r 2, c 2 DATA: r 2, c 3 DATA: r 3, c 0 DATA: r 3, c 1 DATA: r 3, c 2 DATA: r 3, c 3
// tree model
DATA: DATA: item 0 DATA: item 1 DATA: item 2 DATA: item 3
// combined model
DATA: DATA: item 0 DATA: appendColumnItem 0 DATA: appendColumnItem 1 DATA: item 1 DATA: appendColumnItem 2 DATA: appendColumnItem 3 DATA: item 2 DATA: appendColumnItem 4 DATA: appendColumnItem 5 DATA: item 3 DATA: appendColumnItem 6 DATA: appendColumnItem 7
-
irgendwie scheint
QStandardItem::appendColumns()
die Datenstruktur glatt zu Bügeln, statt als Columns sind die als Row rein gekommen ... ::kratzamkopfundwunder::Falls du nicht alle Items exportieren willst bietet es sich an, eine neue UserDataRole zu kreieren ... dann kannst du nur die Exportieren die da ein valides QVariant zurückliefern ... oder so
-
Puh, das ist ein Stück Code. Ich habe die letzte Stunde damit verbracht, mir eine eigene Funktion zusammenzuschustern. Ich werde deine aber noch genauer untersuchen und ausprobieren (ein kleines Wettrennen veranstalten :P) - vielen Dank dafür.
Ich habe übrigens ein QTableView über dem Model, ich brauche nichts einzurücken. Und ich möchte zwar alle Items auslesen, benötige aber eigentlich nicht sämtliche Daten eines Items, Qt::DisplayRole reicht eventuell schon.
-
Stiefel2000 schrieb:
Puh, das ist ein Stück Code.
Interessant sind ja nur
bool saveModel(QStandardItemModel * mod, int depth = 0) { usw. }
undvoid outChild(QStandardItem * item, QTextStream &out, int depth=0) { usw. }
. Der Rest ist Test und Augabe ?!Stiefel2000 schrieb:
Ich habe übrigens ein QTableView über dem Model, ich brauche nichts einzurücken.
Die Einrückung wird in der Ausgabedatei gemacht ... damit du sie auch wieder einlesen kannst
dazu machst du das selbe wie beim speichern (nur Rückwärts *g*) und nutzt
QStandardItem::operator>>()