QListview zusammengesetzte Daten aus Model anzeigen



  • Frohes Neues nachträglich in die Runde.

    Ich bin mal wieder auf ein Problem gestoßen, bei dem ich nicht weiterkomme...
    Ich habe ein QListView mit einem QStandardItemModel und möchte im ListView die Daten des Models in einer zusammengesetzten Form anzeigen.

    Konkret hat das Model 4 Spalten mit einem String und 3 ints. Aus diesen vier Spalten soll nun für jede Reihe ein einzelner String zusammengesetzt und im View angezeigt werden.

    Die Frage ist nun, wie mache ich das? 😁
    Bzw. ist das überhaupt mit Standard-Qt-Mitteln möglich, ohne das ich ein neues View schreiben muss?

    Ich habe mir auch das ItemDelegate angeschaut, aber ich verstehe es so, dass es nur dafür da ist wie die Daten angezeigt werden.



  • Du brauchst ein ProxyModel, z.B. von QSortAndFilterProxyModel (oder so) ableiten.
    Könntest auch direkt von QAbstractItemModel ableiten und die paar Funktionen implementieren, die du brauchst und an dein drunterliegendes Model weiterleiten.

    Im Grunde musst du nur data Implementieren und die Daten da drin zusammenfassen und als einen String zurückgeben.



  • Ah, danke. Ist ja tatsächlich recht einfach. Ich werds morgen mal schreiben... 🙂



  • Ich hab leider immernoch ein kleines Problem, der String wird nicht angezeigt...

    Mein ProxyModel:

    class ObjectiveProxyModel : public QSortFilterProxyModel
    {
        Q_OBJECT
    
    public:
        ObjectiveProxyModel(QObject* parent = 0) : QSortFilterProxyModel(parent) {}
    
        QVariant data(const QModelIndex &proxyIndex, int role = Qt::DisplayRole) const override;
    };
    

    Die Implementierung von data():

    QVariant ObjectiveProxyModel::data(const QModelIndex &proxyIndex, int) const
    {
        QString ret;
        QModelIndex index = mapToSource(proxyIndex);
    
        //QString erzeugen...
    
        return QVariant(ret);
    }
    

    Der String wird korrekt erzeugt, aber im ListView wird nichts angezeigt. Es sind auch keine leeren Felder im ListView...



  • Update: Problem gelöst.
    Hatte am Anfang der data() die Zeile

    if(role != Qt::DisplayRole) return QSortFilterProxyModel::data(proxyIndex,role);
    

    vergessen...



  • Ich greife diesen Thread nochmal für eine andere Frage auf.

    In meinem datamodel habe ich einige dataitems, die an mehrere verschiedene andere Items angehängt werden sollen.

    Bsp:

    root
       |
       |- A
       |  |- A1
       |  |- A2
       |  |   |- B1
       |  |
       |  |- A3
       |  |   |- B3
       |  |
       |  |- A4
       |
       |- B
          |- B1
          |- B2
          |- B3
    

    Ich möchte also sowas wie Referenzen oder Pointer auf andere Items im Model einfügen.
    Klar, ich kann das z.B. über einen ID-Wert in den B-Items machen und mir so die passenden B-Items für das jeweilige A-Item aus dem Model-raussuchen. Aber geht das bei einem QStandartItemModel auch direkt?



  • Mir ist nicht 100% klar, was du konkret willst, aber ich glaub nicht, dass das mit einem QStandardItemModel direkt lösbar ist. So ein Qt Model ist eigentlich kein "Modell" in dem Sinne... Das ist ein Modell für die GUI Darstellung, nicht das eigentliche drunterliegende Datenmodell. Also würdest du auch für zwei Vorkommen im Baum zwei Items erstellen.
    Wenn du das dynamischer haben willst, würde ich ein eigenes QAbstractItemModel schreiben und nicht mit QStandardItemModel arbeiten. Damit hättest du zumindest die Möglichkeit, die "Objekte" bzw. Indizes on the fly zurückzugeben, ohne vorher alles aufzubauen (denk z.B. an das Dateisystem, würde keinen Sinn machen, das ganz am Anfang komplett mit QStandardItems aufzubauen, obwohl man vielleicht nie irgendwas aufklappen wird).



  • Danke für die Antwort.
    Ich habe festgestellt, das ich den Begriff DataModel noch nicht richtig verstanden habe, ich habe das jetzt hoffentlich nachgeholt.
    Aber es ergeben sich daraus weitere Fragen für mich...

    Wenn ich es richtig verstehe ist das QAbstractItemModel eine Abstraktionsschicht zwischen den eigentlichen DataModels und den Views.
    Ich habe noch etwas Verständnisprobleme, wie das QAbstractItemModel mit dem eigentlichen DataModel verbunden wird.

    Konkretes Beispiel: ein Haushaltsbuch.
    Ich habe eine Klasse DataItemModel : QAbstractItemModel,
    dazu die Klasse DataItem für die Items im Model

    Außerdem habe ich 5 DataModels: "product", "productList", "entry", "bill" und "book".

    Das DataModel "product" ist eine Klasse mit Name (string) und Preis (double). Jedes konkrete product wird nur einmal instanziert, für Lebensmittel hätte ich z.B. die products "Milch", "Käse" usw.
    Die Instanzen werden von "produktList" gehalten.

    "entry" wäre ein DataModel für die Einkaufsposten, bestehend aus einem Pointer auf eine "product"-Instanz und einem int für die Anzahl.

    Ein weiteres DataModel wäre "Bill" mit einem Datum und einem vector aus "entry"s

    Und zu guter letzt habe ich das DataModel "book", dass die "bill"s hält.

    Die Frage ist jetzt, wie ich das "DataItemModel" an die DataModels anbinde. Werden die DataModels von "DataItem" abgeleitet oder setze ich in den "DataItem"s bloß einen Pointer auf die DataModel-Instanz und reiche im einfachsten Fall die get/set-Aufrufe für die daten einfach weiter?
    Nach meiner Auffassung wäre letzteres das sinnvollere Vorgehen, da ich ja z.B. das product "Milch" mehrmals haben kann. Und wenn ich z.B. "Milch" in "H-Milch" umbenennen möchte, müsste ich sonst alle entsprechenden DataItems raussuchen und umbennen.

    Wie gesagt, ich habe noch etwas Verständnisprobleme. Und bevor ich mir irgendeinen Blödsinn angewöhne frage ich lieber nach, ob es allgemeine Richtlinien gibt, wie eine gute/sinnvolle Anbindung vom ItemModel an die DataModels aussieht... 🙂



  • Dann lies dir mal die Qt doku zu dessen Model View konzept durch.
    Da sind auch einige Beispiele verlinkt:
    http://doc.qt.io/qt-5/model-view-programming.html



  • Danke, ich hatte die Doku schon gelesen (zugegebenermaßen nicht komplett, nur das, was ich für den Einstieg brauchte) und habe anhand des read-write Beispiels ein eigenes Model geschrieben.
    Dennoch beantwortet die Doku nicht meine Frage. Dort steht, das die konkreten Daten nicht unbedingt im ItemModel gehalten werden (ist mir mittlerweile auch klar) und das man die DataModels von den vorhandenen ItemModels ableiten kann/sollte.
    Aber ist das auch der übliche Weg?
    Und wie funktioniert es dann, wenn z.B. das DataItem für die DataModel-Instanz mit den Daten für "Milch", das in meinem Beispiel nur einmal vorhanden sein soll, mehrmals im ItemModel vorhanden sein soll? Jedes DataItem hat ja nur einen parent...



  • Ist mir etwas zu viel Text, will mich da jetzt nicht so reindenken 😉
    Was ich im Endeffekt sagen wollte... Du wirst normalerweise eh ein "Modell" haben, deinen Code, deine Logik... Irgendwelche domänenspezifischen Klassen. Bei dir dann wahrscheinlich Product, Bill usw... Das ist das, was ich im MVC als Modell bezeichnen würde. Das ist unabhängig von der GUI und irgendwelchen Vorgaben, dass du Indizes, Variants usw. brauchst. Das QAbtractItemModel hingegen ist ein Hilfskonstrukt für die GUI, ein Adapter zwischen GUI und deinem eigentlichen Modell.
    Wenn ich eigene Klassen von QAbstractItemModel ableite, dann benutze ich den internalPointer (der Member vom Index heißt so, wenn ich mich recht erinnere), um auf die eigentlichen Daten zu referenzieren. Notfalls gibts dann auch mal eigene Datenstrukturen innerhalb von dem GUI-Modell, und der internalPointer verweist dann darauf, wenn mir ein Zeiger/Id nicht reicht.



  • Danke, das hilft mir weiter.
    Ich habe immer noch leichte Verständnisschwierigkeiten, irgendwie habe ich eine ganze Eiche vorm Kopp, ein Brett ist das schon nicht mehr...

    Um mal bei dem Beispiel Haushaltsbuch zu bleiben:
    "book" hält alle Einkaufsbelege (Klasse "bill") und wird vom QAbstractItemModel abgeleitet. Intern gibt es einfach einen vector<bill> für die Belege.
    Wenn jetzt ein Index angefordert wird erstellst du ihn intern mit der Position im vector als Row und setzt den internalPointer des Index auf die entsprechende bill-Instanz.
    Bei einer Datenanfrage castest du intern den Pointer zu "bill" und holst die enstprechenden Daten.
    Habe ich das soweit richtig verstanden?



  • @Cherup sagte in QListview zusammengesetzte Daten aus Model anzeigen:

    "book" hält alle Einkaufsbelege (Klasse "bill") und wird vom QAbstractItemModel abgeleitet.

    Nein, das würde ich eben nicht machen.
    Das eigentliche Modell sollte nichts mit Qt zu tun haben. Einfach eine Klasse Book mit einem vector<Bill> von mir aus.

    Dann ein BookModel, abgeleitet von QAbstractItemModel. Und an der Stelle ist die Frage, wie kompliziert der Zugriff auf die eigentlichen Daten ist. Wenn das einfach eine flache Liste ist, brauchst du gar nichts, sondern überschreibst die paar Funktionen (ich hab das jetzt nicht 100% parat, mach nicht mehr viel GUI), z.B. rowCount gibt dann die Anzahl der Einträge im vector zurück, und index erstellt dann einfach den index. In data kannst du direkt row hernehmen und in deine eigentlichen Daten reingreifen.