QAbstractItemModel/TreeView zeigt nichts an


  • Mod

    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.html

    Jedoch, 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 unterhalb doc = 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 🙂


  • Mod

    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();
     }
    

  • Mod

    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


  • Mod

    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.


  • Mod

    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?


  • Mod

    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 ...


  • Mod

    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();
    }
    

  • Mod

    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


  • Mod

    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... 😕


Anmelden zum Antworten