webentwicklung-frage-antwort-db.com.de

Zugriff auf C++ - QListen von QML aus

Wenn ich eine Liste mit Dingen in C++ habe, wie kann ich diese in QML freigeben (in Qt5/QtQuick 2)? Es scheint, als könnte QML nur QObject- abgeleitete Klassen verstehen, was ein Problem ist, da QObjects nicht in eine QList eingefügt oder kopiert werden kann. Wie mache ich das:

struct Thing
{
    int size;
    QString name;
};

class ThingManager : public QObject
{
    Q_OBJECT

    // These macros support QtQuick, in case we one day want to use it to make a slick
    // interface (when QML desktop components are released).
    Q_PROPERTY(QList<Thing> things READ things NOTIFY thingssChanged)

public:
    // ...
    QList<Thing> things() const;

    // ...

};

Damit ich so etwas in QML machen kann :?

var a = thingManager.things[0].name;
24
Timmmm

Nach mehr Erfahrung mit QML habe ich herausgefunden, dass der beste Weg, Listen von Dingen zu haben, mit einem QAbstractListModel ist.

Sie lassen Ihre Thing von QObject ableiten, damit sie in einem QVariant (nach der Registrierung) gespeichert werden kann. Dann können Sie das aktuelle Thing als Modellelement zurückgeben. Sie können in Repeater als model.display.a_property_of_thing darauf zugreifen. Die Listenlänge ist als model.count verfügbar.

Dies hat folgende Vor- und Nachteile:

  1. Schnell - es kopiert nicht die gesamte Liste, um auf ein Element zuzugreifen.
  2. Sie können leicht Animationen für Änderungen an der Liste erhalten (Hinzufügen, Umordnen und Entfernen von Elementen).
  3. Es ist einfach mit QML zu verwenden.
  4. Damit die Animationen funktionieren können, müssen Sie bei jeder Änderung der Liste etwas faffy Buchhaltung durchführen (beginInsertRows() usw.).

...

class Things : public QObject
{
...
};

Q_DECLARE_METATYPE(Thing*)

class ThingList : public QAbstractListModel
{
    Q_OBJECT

public:
    explicit ThingList(QObject *parent = 0);
    ~ThingList();

    int rowCount(const QModelIndex& parent = QModelIndex()) const override;
    QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const override;

public slots:

    // Extra function to get the thing easily from outside Repeaters.
    Thing* thing(int idx);

private:
    QList<Thing*> mThings;
};

int ThingList::rowCount(const QModelIndex& parent) const
{
    return mThings.size();
}

QVariant ThingList::data(const QModelIndex& index, int role) const
{
    int i = index.row();
    if (i < 0 || i >= mPorts.size())
        return QVariant(QVariant::Invalid);

    return QVariant::fromValue(mThings[i]);
}

Thing* ThingList::thing(int idx)
{
    if (idx < 0 || idx >= mThings.size())
        return nullptr;

    return mThings[idx];
}
8
Timmmm

Ich bin auf diese Frage gestoßen, als ich versuchte, ein ähnliches Problem zu beheben, bei dem ich C++ - Code als Modellquelle in QML verwenden wollte. Die Antwort von TheBootroo wies mich in die richtige Richtung, funktionierte aber nicht für mich. Ich habe nicht genug Ruf, um ihm direkt zu antworten (aber ich habe seine Antwort bestätigt).

Ich verwende Qt 5.0.0 Ich fand diesen Link sehr hilfreich

Die Definition von ThingManager sollte wie folgt geändert werden

class ThingManager : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<QObject*> things READ getThings NOTIFY thingsChanged)

public:
    QList<QObject*> getThings () const { return m_things; }

signals:
    void thingsChanged ();

private:
    QList<QObject*> m_things;
};

Beachten Sie, dass ich den Rückgabetyp von getThings in eine QList <QObject *> geändert habe. Ohne diese Änderung warnt Qt, dass der nicht registrierte Datentyp 'QList <Thing *>' 'nicht verarbeitet werden kann.

Im QML-Code können die Eigenschaften von Thing als model.modelData.size und model.modelData.name über das Modell aufgerufen werden.

21
eatyourgreens

Alternativ können Sie QVariantList (QList<QVariant>) verwenden. Bei der Übergabe an QML wird das Feld automatisch in ein JavaScript-Array geändert. Es kann von C++ und QML gelesen und beschrieben werden

20
Dickson

Ah, ich habe die Antwort gefunden (ich denke, nicht getestet): QQmlListProperty

In den Beispielen gibt es einige Verwendungen, z. bei qtdeclarative/examples/quick/tutorials/gettingStartedQml/filedialog/directory.*:

Leider können Sie momentan nur schreibgeschützte Listen haben.

4
Timmmm

sie haben ein falsches Verhalten in Bezug auf QObject. Sie können einer QList übergeben werden, einfach in Form eines Zeigers, da Folgendes perfekt funktioniert:

class Thing : public QObject
{
    Q_OBJECT

    Q_PROPERTY (int     size READ getSize CONSTANT)
    Q_PROPERTY (QString name READ getName CONSTANT)

public:
    Thing(QObject * parent = NULL) : QObject(parent) {}

    int     getSize () const { return m_size; }
    QString getName () const { return m_name; }

private:
    int     m_size;
    QString m_name;
};

class ThingManager : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QList<Thing*> things READ getThings NOTIFY thingsChanged)

public:
    QList<Thing*> getThings () const { return m_things; }

signals:
    void thingsChanged ();

private:
    QList<Things*> m_things;
};
2
TheBootroo

am besten verwenden Sie QQmlListProperty . dieses einfache Beispiel. Ich hoffe, ich helfe Ihnen .
Objekt- und Listeneigenschaften - Beispiel

1
Golil

Die von eatyourgreens gegebene Antwort ist richtig. Indem Sie Ihre Klasse auf diese Weise implementieren, können Sie auf so viele Nachkommen zugreifen, wie Sie möchten. Ein weiterer hilfreicher Tipp, den ich als hilfreich empfunden habe, ist das Erstellen eines Alias ​​für unser Modell innerhalb des qml-Delegatenelements.

ListView {
   anchors.fill: parent
   model: thing_manager.things
   delegate: ItemDelagate {}
   clip: true
   spacing: 10
}

In der ItemDelegate.qml können Sie einen Alias ​​für das Modell erstellen, um die model.modelData nicht immer zu verwenden

Item{
    width: 600
    height: 200
    property var thing: model.modelData
    Rectangle {
        anchors.fill: parent
        color: "red"
        Text {
            text: thing.name // or any other field
        }
    }
}
1
Mr.Coffee

Ein sehr indirekter Weg, dies zu erreichen, ist folgender:

i.) Machen Sie ein Modell in qml

ListModel 
{
     id: thingModel

     ListElement 
     {
         size: 10
         name: "Apple"
     }     
}

ii.) Stellen Sie dann ein paar Javascript-Funktionen bereit, um diese Liste zu ändern, z.

function jAppendThing( newSize, newName )
{
    thingModel.append({"size": nameSize, "name": newName })
}

function jClearThing()
{
    thingModel.clear()
}

ähnlich jDeleteThing etc ..

iii.) Sie können über lesen, wie Sie qml-Funktionen von c ++ aus aufrufen.

iv.) Führen Sie eine Schleife in Ihrer C++ - Liste aus und rufen Sie die Append-Funktion von qml auf, um all diese Daten ebenfalls in die qml-Liste einzufügen.

v.) Ändern Sie bei jedem Update in der C++ - Seitenliste auch die qml-Daten mit der obigen Funktion, um sie auf dem neuesten Stand zu halten.

0
Amit Tomar

Existiert eine gute, aber nicht erwähnte Lösung:

class ThingManager : public QObject
{
Q_OBJECT

// These macros support QtQuick, in case we one day want to use it to make a slick
// interface (when QML desktop components are released).
Q_PROPERTY(QList<Thing> things MEMBER m_things NOTIFY thingssChanged)

// ...
private:
// ...
QList<Thing> m_things;
// ...

};

Lesen und Schreiben sind anwendbar. Kein teurer Funktionsaufruf und keine Datenkopie. Einfach direkter Zugriff auf die Teilnehmer in QML:

var a = thingManager.things[0].name;

Weitere Informationen finden Sie im Dokument: https://doc-snapshots.qt.io/qt5-dev/properties.html

0
fokhagyma