QAbstractProxyModel richtig ableiten für QTreeView + Belohnung



  • Hallo zusammen,

    gleich vorweg, ihr seid nun meine letzte Hoffnung, hab schon in Blogs, Foren und Social Networks gefragt,
    bisher konnte keiner die Herausforderung stemmen.

    Als Anlage habe ich ein kleines Demoprojekt angefügt (Quelltexte) Qt 4.8.3 - daraus sind auch diese Snippets. http://www65.zippyshare.com/v/88903744/file.html

    Das QSqlRelationalTableModel habe ich an das QTreeView gebunden, funktioniert einwandfrei.

    QTreeView *view = new QTreeView;
    view->setModel(DatabaseManager::instance()->landmodel());
    view->setItemDelegate(new QSqlRelationalDelegate(view));
    

    Was ich aber benötige ist, dass die Foreign Keys die 3. Dimension (Ebenen im TreeView) sind.

    Also ca. so:
    Sepp
    - Marienplatz
    - Domplatz
    - Stadtpark
    Max
    - U-Bahn Station
    - Kino

    Die Herausforderung besteht jetzt darin, das 2 Dimensionale (Zeilen, Spalten) Model in die 3 Dimensionale View (Zeilen, Spalten, Ebenen) umzuwandeln.

    Das ganze soll anscheinend mit einer Implementation von QAbstractProxyModel funktionieren, nur leider häng ich hier jetzt.
    Weder Examples noch eine ausreichende Dokumentation sind zu finden.

    Kann mir jemand helfen und erklären, wie ich mithilfe des Proxy's die Owner als Parent und deren Land als Child Items in die TreeView bekomme?

    Danke für jegliche Hilfe! Falls mir jemand eine gute Hilfestellung/Erklärung geben kann, die zu einer richtigen Lösung führt, kann gerne meinen Share-Online Premium Account nutzen! Dieser ist ab jetzt noch 30 Tage gültig. (Geschenkt natürlich)



  • Irgendwas irgendwo runterladen und sich reindenken ist viel zu umständlich für so ein Forum 😉
    Ich hab deine Beschreibung nicht ganz verstanden, aber ich vermute, dir fehlt der ORM Schritt und du versuchst, die Datenbankstruktur direkt in ein Modell zu quetschen. Würd ich nicht machen. Lies die Daten aus, erstell daraus Objekte, schreib ein Modell, das die Objekte abbildet.
    Wenn ich am Thema vorbeigeredet habe, erklärts genauer 🙂



  • Hi Mechanis,

    das Projekt bildet an sich nur das ab, was ich beschrieben habe. Hab es auf 2 Klassen beschränkt. Aber ich kanns auch gern nochmal erklären:

    Die ORM Schicht (denke ich) ist ja das QSqlRelationalTableModel => Das kümmert sich um hinzufügen, löschen, updaten, select, filter.. usw. Ich seh also bisher keinen Grund extra Datenklassen anzulegen, wenn ich mit diesen eh nichts anderes mache. Zudem hab ich dann den "Komfort" des SqlModel's nicht.

    Das eigentliche Problem ist, dass eine Datenbanktabelle keine Baumstruktur abbildet bzw. abbilden kann.

    Wenn ich 3 Äpfel habe, die an einen Apfelbaum-Rot gehören und 2 Äpfel, die an einen Apfelbaum-Grün gehören bekomm ich vom Model folgendes zurück:

    Apfel 1 | Apfelbaum-Rot
    Apfel 2 | Apfelbaum-Rot
    Apfel 3 | Apfelbaum-Rot
    Apfel 1 | Apfelbaum-Grün
    Apfel 2 | Apfelbaum-Grün

    Ich hätte das ganze aber gerne als Baumansicht, wobei (ähnlich Gruppenwechsel) jeder unique foreign key ein Root Item bzw. die Äpfel alle Child Items sind.

    Also in etwa so:
    Apfelbaum-Rot
    - Apfel 1
    - Apfel 2
    - Apfel 3
    Apfelbaum-Grün
    -Apfel 1
    -Apfel 2

    und dafür brauch ich eine Implementation eines eigenen ProxyModels. Abgeleitet von QAbstractItemProxy => Nur ich weiß nicht WIE ich die Klasse umsetzen soll, um so ein Ergebnis zu erzielen.

    Ich hoffe ich konnte es einigermaßen genau erklären 😉



  • X_2F9 schrieb:

    Die ORM Schicht (denke ich) ist ja das QSqlRelationalTableModel => Das kümmert sich um hinzufügen, löschen, updaten, select, filter.. usw.

    ORM ist Object Relational Mapping und soll eben helfen, eine Objekthierarchie auf eine relationale Datenbank abzubilden (und umgekehrt). Und so ein Problem hast du hier eben. Was du beschreibst sind CRUD Operationen. Die sind bei dir wahrscheinlich nicht wirklich gekapselt, also nicht mal sowas wie Active Record?

    Ich bin mir nicht sicher, ob das mit einem Proxy Model geht. Das ist nicht dazu gedacht, Daten zu transformieren. Es ist höchstens dazu gedacht, Spalten oder Zeilen auszublenden. Selbst wenn du Spalten hinzufügen willst, bekommst du Probleme.
    Was du auf jeden Fall machen musst, ist ein eigenes Model (kein Proxy Model) zu schreiben. Da kommst wohl nicht drum herum. Dann wirst die Daten vielleicht in irgendwelchen internen Objekten verwalten müssen, damit du die Hierarchie entsprechend aufbauen kannst. Von dem her kannst du auch gleich Datenobjekte dafür definieren und das Laden irgendwie kapseln. Darum muss sich ja nicht unbedingt ein Model kümmern (ein richtiges "Modell" schon, aber das QAbstractItemModel von Qt ist ja kein Modell im MVC Sinne, es ist eher ein Controller, von dem her würde ich schon ein richtiges Modell schreiben).



  • Hm, ja verstehe wie du das meinst. In C# hab ich mit MVVM ja auch die Objekte Mappen müssen ... geht das in Qt das ich trotzdem das QSqlRelationalTableModel nehme und aus den Rekords die Objekte erzeuge? - Hab leider nurnoch bis Freitag Zeit das Projekt fertigzustellen und jetzt die ganze Datenbankgeschichte manuell schreiben wird wohl den Rahmen sprengen.

    Zumindest ein neuer Ansatz, dann erkunde ich mich mal wie ich nen Modell anständig umsetz. 🙂

    *Sorry für die Unbeholfenheit, muss für mein Abschlussprojekt der Schule eine Qt App schreiben und vorher noch nie etwas von Qt gehört.*



  • Ich hab ehrlich gesagt noch nie QSqlRelationalTableModel benutzt und halte das auch für keine gute Idee... Du würdest es aber wohl schon irgendwie dazu missbrauchen können. Wär aber wahrscheinlich einfacher, direkt mit Sql zu arbeiten.
    Bis Freitag ist ja noch genug Zeit, sowas sollte doch in 1-2 Stunden umgesetzt sein...





  • Da wird kein Proxy Model benutzt, auch wenn er von einem "Proxy" Model redet.



  • Moin,
    es geht schon mit einem proxy model.
    Ein unfertiges Example habe ich hier gefunden: http://www.qtcentre.org/threads/26163-Map-table-to-tree-through-model-view-possible

    #include <QtGui>
    
    class MyProxy : public QAbstractProxyModel {
    public:
        MyProxy() : QAbstractProxyModel(){}
        QModelIndex mapToSource(const QModelIndex &proxyIndex) const {
            if(!proxyIndex.isValid()) return QModelIndex();
            int c = columnFromIndex(proxyIndex);
            int r = rowFromIndex(proxyIndex);
            return sourceModel()->index(r,c);
        }
    
        QModelIndex mapFromSource(const QModelIndex &sourceIndex) const {
            if(!sourceIndex.isValid()) return QModelIndex();
            if(sourceIndex.column()==0)
                return createIndex(sourceIndex.row(), 0, calculateId(sourceIndex));
            return createIndex(0, 0, calculateId(sourceIndex));
        }
        int columnCount(const QModelIndex &parent = QModelIndex()) const {
            return 1;
        }
        int rowCount(const QModelIndex &parent = QModelIndex()) const {
            if(!parent.isValid())
                return qMin(0x10000, sourceModel()->rowCount());
            int c = mapToSource(parent).column();
            if(c==sourceModel()->columnCount()-1)
                return 0;
            return 1;
        }
        QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const {
            if(parent.isValid()) {
                // if parent is valid then in the source model
                // we want to receive the same row but the next column, provided that row==0 && col==0
                // otherwise the index is not valid
                if(row!=0 || column!=0) return QModelIndex();
                return createIndex(row, column, (int)parent.internalId()+1);
            }
            if(column!=0) return QModelIndex();
            // parent is not valid thus we can calculate the id the same way as for the source model
            return createIndex(row, 0, calculateId(row, 0));
        }
        QModelIndex parent(const QModelIndex &child) const {
            if(!child.isValid())
                return QModelIndex();
            // parent of an index in the source model is the same row but previous column
            int c = mapToSource(child).column();
            int r = mapToSource(child).row();
            if(c==0){
                // if original column == 0 then there is no parent
                return QModelIndex();
            }
            c -= 1;
            if(c==0){
                return createIndex(r, 0, calculateId(r, c));
            }
            return createIndex(0, 0, calculateId(r, c));
        }
    
        bool hasChildren ( const QModelIndex & parent = QModelIndex() ) const
        {
            return true;
        }
    private:
        int columnFromIndex(const QModelIndex &proxyIndex) const {
            quint32 id = proxyIndex.internalId();
            int c = (id & 0xffff);
            return c;
        }
        int rowFromIndex(const QModelIndex &proxyIndex) const {
            quint32 id = proxyIndex.internalId();
            int r = (id & 0xffff0000) >> 16;
            return r;
        }
        int calculateId(const QModelIndex &sourceIndex) const {
            quint32 r = sourceIndex.row();
            quint32 c = sourceIndex.column();
            return calculateId(r, c);
        }
        int calculateId(quint32 r, quint32 c) const{
            return (((r & 0xffff) << 16) | (c & 0xffff));
        }
    };
    
    int main(int argc, char **argv) {
        QApplication app(argc, argv);
        QTreeView tv;
        QStandardItemModel model(10,3);
        for(int i=0; i<model.rowCount(); i++){
            for(int j=0; j<model.columnCount(); j++){
                if (i <  2 && j == 0)
                    model.setData(model.index(i,j), 120, Qt::DisplayRole);
                else
                    model.setData(model.index(i,j), qrand() % 100, Qt::DisplayRole);
            }
        }
        MyProxy proxy;
        proxy.setSourceModel(&model);
        tv.setModel(&proxy);
        tv.show();
        QTableView orig;
        orig.setModel(&model);
        orig.show();
        return app.exec();
    }
    

    Der konvertiert eine Table in eine Tree struktur. Es fehlt "nur" noch dass elemente mit dem gleichen Wert auf der ersten ebene des Trees zusammengefasst werden.

    oder so: http://www.qtforum.org/article/33291/qsqlquerymodel-and-qtreeview.html



  • Ich bin skeptisch, was sowas angeht. Ich hatte schon genug Probleme mit Proxy Models. Man sollte sie nicht zu sehr vergewaltigen.
    In dem Beispiel wird auch index und parent überschrieben. Wo ist dann der Vorteil gegenüber einem einfach QAbstractItemModel, warum muss es ein Proxy Model sein?


Anmelden zum Antworten