QAbstractItemModel/TreeView zeigt nichts an
-
Hallo,
versuche mich gerade an einer ersten Modelklasse in Qt, ich will einen Dokumentenbaum darstellen, alles eigentlich relativ einfach gestrickt.
Orientieren tue ich mich hierbei an diesem Beispiel:
http://doc.qt.nokia.com/4.6/itemviews-simpletreemodel.htmlJedoch, jetzt wo alles implementiert ist, bleibt der TreeView leer.
Mein Code:
DocumentTreeModel::DocumentTreeModel(QObject *parent) : QAbstractItemModel(parent) { //doc ist die Wurzel des Baumes doc = new DocRoot("Root"); } QModelIndex DocumentTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); DocRoot* p = 0; if(parent.isValid()) p = static_cast<DocRoot*>(parent.internalPointer()); else p = doc; DocRoot* childItem= p->getByIndex(row);// entweder 0 oder ein DocRoot aus dem Container if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); } QModelIndex DocumentTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) return QModelIndex(); DocRoot* p = static_cast<DocRoot*>(index.internalPointer()); DocRoot* par = p->getParent(); if(!par || par ==doc) return QModelIndex(); return createIndex(par->row(), 0, par); } int DocumentTreeModel::rowCount(const QModelIndex &parent) const { if (parent.column() > 0) return 0; if (!parent.isValid()) return doc->count(); return 2; } int DocumentTreeModel::columnCount(const QModelIndex &parent) const { return 1; } QVariant DocumentTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role != Qt::DisplayRole) return QVariant(); DocRoot* p = static_cast<DocRoot*>(index.internalPointer()); if(p && p != doc) return QVariant(p->getText()); return QVariant(); } Qt::ItemFlags DocumentTreeModel::flags(const QModelIndex &index) const void DocumentTreeModel::update() { emit dataChanged(createIndex(0,0,doc->getByIndex(0)),createIndex(doc->count()-1,0,doc->getByIndex(doc->count()-1))); }
Da gibt es keine großen Unterschiede zu dem Qt Beispiel, getText gibt ein QString zurück, ich weiss nicht, ob QVariant(p->getText()); Evtl. fehl schlägt oder hier nicht korrekte Daten liefert.
Wo anders fülle ich dies dann mit Daten:
Document::t_form* form = new Document::t_form; form->first = str; DocRoot* root = new TreeModelProxy<Document::t_form>(form,str); root->addNode(new TreeModelProxy<DiagramScene>(form->second.second,"Landscape")); root->addNode(new TreeModelProxy<DiagramScene>(form->second.first,"Portrait")); //DocRoot* p = root->getByIndex(0)->getParent(); doctree.addNode(root); doctree.update(); //ui->treeView->setModel(&doctree);
Insgesamt sieht das alles für mich korrekt aus, aber ich verstehe nicht, warum nichts angezeigt wird...
-
Dein Vorgehen?
docTree = DocumentTreeModel(); . . . DocRoot* root = new TreeModelProxy<Document::t_form>(form,str); root->addNode(new TreeModelProxy<DiagramScene>(form->second.second,"Landscape")); root->addNode(new TreeModelProxy<DiagramScene>(form->second.first,"Portrait")); doctree.addNode(root); doctree.update(); . . .
addNode
hängt*root
unterhalbdoc = new DocRoot("Root");
(Konstructor) ein?Wenn der anschließend etwas sucht, kann es sein das in
QModelIndex DocumentTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) return QModelIndex(); DocRoot* p = 0; if(parent.isValid()) p = static_cast<DocRoot*>(parent.internalPointer()); else p = doc; DocRoot* childItem= p->getByIndex(row);// entweder 0 oder ein DocRoot aus dem Container if (childItem) return createIndex(row, column, childItem); else return QModelIndex(); }
etwas in
createIndex(row, column, childItem);
schiefläuft?Ich habe leider nur ein rudimentäres Verständnis der QtXML Objekte - noch nie was mit gemacht, sorry
-
Das hat nix mit XML zu tun.
addNode fügt auch jeweils sich als parent ein, das klappt alles:void addNode(DocRoot* node) { children.push_back(node); node->parent = this; }
Ja, doctree.addNode(root) -> doc->addNode(root)
index wird in der Tat nur einmal aufgerufen, aber wo ist da der Fehler?
Das ist eigentlich 1:1 aus dem Beispiel übernommen.
-
Die "flache" Hirarchy ist beabsichtigt?
nach addNode mit [item1,item1a,item2,item2a,item2b] sieht das DocumentTreeModel so aus:
rootitem_from_constructor |-- item1 |-- item1a |-- item2 |-- item2a |-- item2b
wohingegen fürs indexing eher sowas gebraucht wird:
rootitem_from_constructor |-- item1 | `-- item1a |-- item2 |-- item2a `-- item2b
Aber vermutlich baust du die flache Struktur intern in
- getByIndex(row) - hasIndex(row,col,parentIndex) - createIndex(wor,colum,DocRoot*)
wieder um?
Es scheint an dem Indexing zu liegen, er steigt denke ich bei
QVariant DocumentTreeModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant("data invalid"); // <------------ änder mal if (role != Qt::DisplayRole) return QVariant("not display role"); // <------------ änder mal DocRoot* p = static_cast<DocRoot*>(index.internalPointer()); if(p && p != doc) return QVariant("tried to get data"); // // <------------ änder mal return QVariant("compiler-statisfy-return from data"); // <------------ änder mal }
aus.
QModelIndex DocumentTreeModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) // Zugriff auf root item mit parent = QModelIndex(), hast du überladen und an die flache Hierarchie angepasst? return QModelIndex(); // Zugriff auf child item mit parent != QModelIndex() DocRoot* p = 0; if(parent.isValid()) // der übergebene Index ist valid? p = static_cast<DocRoot*>(parent.internalPointer()); // flache hirarchy: gibt immer das rootitem aus dem constructor als parent wieder was effectiv das selbe ist wie der "else" fall? else p = doc; DocRoot* childItem= p->getByIndex(row);// entweder 0 oder ein DocRoot aus dem Container if (childItem) { // return createIndex(row, column, childItem); // etwas selbstgeschriebenes ... versuch doch mal QModelIndex test = createIndex(row,column, childItem); // createIndex(row, column, 0) gibt einen invaliden QModelIndex() zurück? qDebug() << test.isValid(); // wenn da immer false rauskommt wird das auch bei data() passieren return test; } else return QModelIndex(); }
-
Es soll eine flache Hierachie sein, ja.
Ein unsichtbares Rootelement, und dann jeweils ein element mit 2 kindern.
Als erster Test.Verstehe deine Änderungen nicht ganz, aber wie gesagt, der Code ist so aus dem Beispiel übernommen...
-
Falls du das addNode(.) benutzt nachdem das Model bereits dem View zugeordnet war, schau mal hier:
http://doc.trolltech.com/4.1/qabstractitemmodel.html#details unter
Subclassing
Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(), and removeColumns(). When implementing these functions, it is important to notify any connected views about changes to the model's dimensions both before and after they occur:
* An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards.
* An insertColumns() implementation must call beginInsertColumns() before inserting new columns into the data structure, and it must call endInsertColumns() immediately afterwards.Ich hatte mir Anfang des Jahres ein TreeModel für ein Interface das Sql-Queries kapselt geschrieben, kann Dienstag mal nachschaun wie ich das dort gemacht habe - mein Startpunkt war auch das SimpleTreeModel Beispiel... allerdings besteht bei mir die komplette Struktur der Tabellen vorher, also keine onthefly Hinzufügen von neuen Einträgen ... ist eine ReadOnly Tabelle im Moment
-
So, hab das jetzt mal als Testprojekt zusammengehackt, unter http://codenode.de/TreeModel.zip kann man sich es runterladen.
Wieso das jetzt nicht klappt ist mir ein Rätsel.
Vielleicht findet ja jemand den Fehler...
-
Ich bekomme leider nur ein "File Not Found!" beim versuch die Datei downzuloaden.
-
guenni81 schrieb:
Ich bekomme leider nur ein "File Not Found!" beim versuch die Datei downzuloaden.
Äh, ja. Falscher Ordner. Jetzt klappts.
-
Darf ich nochmal auf
padreigh schrieb:
Falls du das addNode(.) benutzt nachdem das Model bereits dem View zugeordnet war, schau mal hier:
http://doc.trolltech.com/4.1/qabstractitemmodel.html#details unter
Subclassing
Models that provide interfaces to resizable data structures can provide implementations of insertRows(), removeRows(), insertColumns(), and removeColumns(). When implementing these functions, it is important to notify any connected views about changes to the model's dimensions both before and after they occur:
* An insertRows() implementation must call beginInsertRows() before inserting new rows into the data structure, and it must call endInsertRows() immediately afterwards.
* An insertColumns() implementation must call beginInsertColumns() before inserting new columns into the data structure, and it must call endInsertColumns() immediately afterwards.verweisen? Ich hab 9h Autobahn hinter mir, guck mir den Quelltext morgen mal an und bau das ein um zu sehen obs tut. Ich denke dein addNode geht einfach an QTs Model/View Struktur vorbei, daführ haben die spezielle Methoden vorgesehen die du reimplementieren musst?
-
Ja, aber im Beispiel wird das nicht verwendet.
Es wird ja auch nix angezeigt wenn ich das vorher erstelle.
Auch ein erneutes setModel bringt nichts.
-
Schwere Nuss .. was ich bisher fand:
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); model = new DocumentTreeModel(); ui->treeView->setModel(model); qDebug() << "! Startup !"; model->debugOut(); } DocumentTreeModel::DocumentTreeModel(QObject *parent) : QAbstractItemModel(parent) { doc = new DocRoot("Root"); DocRoot * it1 = new DocRoot("Item A"); it1->addNode(new DocRoot("Sub1")); it1->addNode(new DocRoot("Sub2")); doc->addNode(it1); } void DocumentTreeModel debugOut(int lvl=0, DocRoot * dr=0) { if (lvl == 0 && dr == 0) { debugOut(0, doc); } else { qDebug() << QString(lvl*4,' ') << dr->getText(); foreach(DocRoot *ch, dr->getAllChilds()) debugOut(lvl+1, ch); } } void MainWindow::on_pushButton_clicked() { static int num = 1; QString txt("Item" + QString::number(++num)); DocRoot* root = new DocRoot(txt); root->addNode(new DocRoot(QString(txt+"a"))); root->addNode(new DocRoot(QString(txt+"b"))); model->addNode(root); model->update(); model->debugOut(); }
Bringt die Datenstruktur auf
"" "Root" " " "Item A" " " "Sub1" " " "Sub2" " " "Item2" " " "Item2a" " " "Item2b" " " "Item3" " " "Item3a" " " "Item3b" " " "Item4" " " "Item4a" " " "Item4b"
statt
"" "Root" " " "Item1" " " "Item2" " " "Item2" " " "Item2a" " " "Item2b" " " "Item3" " " "Item3a" " " "Item3b" " " "Item4" " " "Item4a" " " "Item4b" " " "Item5" " " "Item5a" " " "Item5b"
aber des Rätsels Lösung war das noch nicht ...
-
padreigh schrieb:
Schwere Nuss .. was ich bisher fand:
Bringt die Datenstruktur auf
"" "Root" " " "Item A" " " "Sub1" " " "Sub2" " " "Item2" " " "Item2a" " " "Item2b" " " "Item3" " " "Item3a" " " "Item3b" " " "Item4" " " "Item4a" " " "Item4b"
Das wäre ja auch die korrekte.
Aber warum nichts angezeigt wird, ist mir trotzdem ein Rätsel...
-
Hauptänderungen:
- DocumentTreeModel::addNode(DocRoot* node) Implementierung
- DocumentTreeModel::index(..) createIndex(row,1 !!!,pointer)
- DocumentTreeModel::columnCount() return != count();
- ne Menge Debugausgaben-- documenttreemodel.h --
#ifndef DOCUMENTTREEMODEL_HPP #define DOCUMENTTREEMODEL_HPP #include <QAbstractItemModel> #include <QModelIndex> #include <vector> #include <QtDebug> class DocRoot { std::vector<DocRoot*> children; DocRoot* parent; QString text; public: DocRoot(QString text,DocRoot* parent=0):parent(parent),text(text){} virtual ~DocRoot(){for(size_t i =0; i < children.size(); ++i)delete children[i];} void addNode(DocRoot* node){children.push_back(node);node->parent = this;} void removeNode(DocRoot* node){} QString getText()const{return text;} DocRoot* getByIndex(int i) { return i < children.size() && i >= 0 ? children[i] : 0; } DocRoot* getParent(){return parent;} int count()const {return children.size();} int row(DocRoot* p) { for(int i =0;parent && i < children.size(); ++i) { if(p == children[i]) return i; } return 0; } int row() { if(parent) return parent->row(this); return 0; } std::vector<DocRoot*> getAllChilds() { return children; } }; class DocumentTreeModel : public QAbstractItemModel { Q_OBJECT DocRoot* doc; public: explicit DocumentTreeModel(QObject *parent = 0); virtual ~DocumentTreeModel(){delete doc;} QVariant data(const QModelIndex &index, int role) const; Qt::ItemFlags flags(const QModelIndex &index) const; QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const; QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const; QModelIndex parent(const QModelIndex &index) const; int rowCount(const QModelIndex &parent = QModelIndex()) const; int columnCount(const QModelIndex &parent = QModelIndex()) const; void update(); void addNode(DocRoot* node){ beginInsertRows(QModelIndex(),doc->count(),doc->count()+1); doc->addNode(node); endInsertRows(); } signals: public slots: void debugOut(int lvl=0, DocRoot * dr=0) { if (lvl == 0 && dr == 0) { debugOut(0, doc); } else { qDebug() << QString(lvl*4,' ') << dr->getText(); foreach(DocRoot *ch, dr->getAllChilds()) debugOut(lvl+1, ch); } } }; #endif // DOCUMENTTREEMODEL_HPP
-- documenttreemodel.cpp --
#include "documenttreemodel.hpp" #include <QMessageBox> #include <QtDebug> DocumentTreeModel::DocumentTreeModel(QObject *parent) : QAbstractItemModel(parent) { doc = new DocRoot("Root"); DocRoot * it1 = new DocRoot("Item A"); it1->addNode(new DocRoot("Sub1")); it1->addNode(new DocRoot("Sub2")); doc->addNode(it1); } QModelIndex DocumentTreeModel::index(int row, int column, const QModelIndex &parent) const { DocRoot* p = 0; QString msg = "INDEX for "; if ( !parent.isValid()) { p = doc; msg.append("ROOT_ITEM("); } else { p = static_cast<DocRoot*>(parent.internalPointer()); msg.append(" ITEM("); } msg.append(p->getText()+")") .append(" get child #"+QString::number(row)+" ("); DocRoot * child = p->getByIndex(row); if (child) { msg.append(child->getText()+")"); qDebug() << msg; return createIndex(row,1,child); } msg.append("invalid)"); qDebug() << msg; return QModelIndex(); } QModelIndex DocumentTreeModel::parent(const QModelIndex &index) const { if (!index.isValid()) { qDebug() << "dtm::PARENT() -> invalid QModelIndex()"; return QModelIndex(); } DocRoot* p = static_cast<DocRoot*>(index.internalPointer()); if (! p || p == doc) { qDebug() << "dtm::PARENT() -> invalid QModelIndex()"; return QModelIndex(); } DocRoot* par = p->getParent(); if (par == doc ) { qDebug() << "dtm::PARENT() -> invalid QModelIndex()"; return QModelIndex(); } qDebug() << "dtm::PARENT() -> createIndex(" << par->row() << ",0," << par->getText() << ")"; return createIndex(par->row(), 0, par); // } int DocumentTreeModel::rowCount(const QModelIndex &parent) const { if (!parent.isValid()) { qDebug() << "dtm::ROW_COUNT " << doc->count() << "(" << doc->getText() << ")"; return doc->count(); } DocRoot* p = static_cast<DocRoot*>(parent.internalPointer()); if (p) { qDebug() << "dtm::ROW_COUNT " << p->count() << "(" << p->getText() << ")"; return p->count(); } return 0; } int DocumentTreeModel::columnCount(const QModelIndex &parent) const { if(parent.isValid()) { DocRoot* p = static_cast<DocRoot*>(parent.internalPointer()); if (p) return 1; else return 0; } return 1; } QVariant DocumentTreeModel::data(const QModelIndex &index, int role) const { if (! role == Qt::DisplayRole) return QVariant(); QString msg = "dtm::DATA (" + QString::number(index.row()) + ", " + QString::number(index.column()) + ", "; if (index.parent().isValid()) msg.append("parent:" + static_cast<DocRoot*>(index.parent().internalPointer())->getText() + ")"); msg.append("with role: [" + QString::number(role) + "] "); if (!index.isValid()) { qDebug() << msg << " with invalid index"; return QVariant(); } DocRoot* p = static_cast<DocRoot*>(index.internalPointer()); if (p) { qDebug() << msg << " with text " << p->getText(); return QVariant(p->getText()); } qDebug() << msg << " with invalid child"; return QVariant(); } Qt::ItemFlags DocumentTreeModel::flags(const QModelIndex &index) const { if (!index.isValid()) return 0; qDebug() << "flags"; return Qt::ItemIsEnabled | Qt::ItemIsSelectable; } QVariant DocumentTreeModel::headerData(int section, Qt::Orientation orientation, int role) const { return QVariant(); } void DocumentTreeModel::update() { emit dataChanged(createIndex(0,0,doc->getByIndex(0)),createIndex(doc->count()-1,0,doc->getByIndex(doc->count()-1))); }
-- mainwindow.cpp --
#include "mainwindow.hpp" #include "ui_mainwindow.h" MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); model = new DocumentTreeModel(); ui->treeView->setModel(model); qDebug() << "<Startup>"; model->debugOut(); qDebug() << "<Startup />"; } MainWindow::~MainWindow() { delete ui; delete model; } void MainWindow::changeEvent(QEvent *e) { QMainWindow::changeEvent(e); switch (e->type()) { case QEvent::LanguageChange: ui->retranslateUi(this); break; default: break; } } void MainWindow::on_pushButton_clicked() { static int num = 1; QString txt("ButtonADD_" + QString::number(++num)); DocRoot* root = new DocRoot(QString(txt+"_Item")); root->addNode(new DocRoot(QString(txt+"_sub_a"))); root->addNode(new DocRoot(QString(txt+"_sub_b"))); model->addNode(root); model->update(); model->debugOut(); }
-
Ja, jetzt klappts. Danke
Allerdings ist dass hier falsch:
return createIndex(row,1,child);
So kann er nämlich nicht in die Verästelung des Baumes rein.
Ein letztes Problem habe ich allerdings:
Das letzte Item im Baum wird nicht korrekt dargestellt, da wird die Linie weitergezogen als es sein sollte.
So, als wäre darunter noch ein Knoten...
-
Ich seh leider keine Linie (anderer Windowmanager ) viel Glück beim weitersuchen
-
Hab gerade mal kurz deinen Code durch C&P komplett bei mir eingefügt, bleibt aber das ergebnis, das die letzte Node, wenn ausgeklappt falsch gezeichnet wird unter WinDoof...