web-dev-qa-db-fra.com

Accéder aux QLists C++ à partir de QML

Si j'ai une liste de choses en C++, comment puis-je l'exposer à QML (dans Qt5/QtQuick 2)? Il semble que QML ne peut comprendre que les classes dérivées de QObject, ce qui pose problème, car QObjects ne peut pas être placé dans une QList ou copié. Comment puis-je faire cela:

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;

    // ...

};

Pour que je puisse faire quelque chose comme ça dans QML :?

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

Après plus d'expérience avec QML, j'ai trouvé le meilleur moyen d'avoir des listes de choses avec QAbstractListModel .

Vous faites que votre Thing dérive de QObject pour pouvoir être stocké dans un QVariant (après l’avoir enregistré). Vous pouvez ensuite renvoyer la valeur réelle Thing en tant qu’élément du modèle. Vous pouvez y accéder dans Repeater en tant que model.display.a_property_of_thing. La longueur de la liste est disponible sous la forme model.count.

Cela a les avantages et les inconvénients suivants:

  1. Rapide - il ne copie pas toute la liste pour accéder à un élément.
  2. Vous pouvez facilement obtenir des animations pour modifier la liste (ajout, réarrangement et suppression d'éléments).
  3. C'est facile à utiliser depuis QML.
  4. Pour que les animations fonctionnent, chaque fois que vous modifiez la liste, vous devez effectuer une comptabilité légèrement fausse (beginInsertRows() etc.).

...

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

Je suis tombé sur cette question en essayant de résoudre un problème similaire, où je voulais utiliser du code C++ en tant que source de modèle dans QML. La réponse donnée par TheBootroo m'a orienté dans la bonne direction, mais n'a pas fonctionné pleinement pour moi. Je n'ai pas assez de réputation pour lui répondre directement (mais j'ai relu sa réponse).

J'utilise Qt 5.0.0 J'ai trouvé ce lien très utile

La définition de ThingManager doit être modifiée comme suit

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

Notez que j'ai changé le type de retour de getThings en QList <QObject *>. Sans cette modification, Qt avertit qu'il est "Impossible de gérer le type de données non enregistré 'QList <Thing *>'".

Dans le code QML, les propriétés de Thing sont accessibles via le modèle en tant que model.modelData.size et model.modelData.name.

21
eatyourgreens

Sinon, vous pouvez utiliser QVariantList (QList<QVariant>), il passera automatiquement au tableau JavaScript lorsqu'il est passé à QML, et il est lu et écrit en C++ et QML

20
Dickson

Ah j'ai trouvé la réponse (je pense, pas testé): QQmlListProperty

Il y a quelques utilisations dans les exemples, par exemple: à qtdeclarative/examples/quick/tutorials/gettingStartedQml/filedialog/directory.*:

Malheureusement, vous ne pouvez avoir que des listes en lecture seule pour le moment.

4
Timmmm

vous avez tout à fait tort à propos de QObject, ils peuvent être donnés à une QList, simplement sous la forme d'un pointeur, car ce qui suit fonctionne parfaitement:

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

meilleure façon d'utiliser QQmlListProperty . voir cet exemple simple, j'espère vous aider ...
Exemple de types de propriétés d'objet et de liste

1
Golil

La réponse donnée par eatyourgreens est correcte. En implémentant votre classe de cette manière, vous pouvez accéder à autant de descendants que vous le souhaitez. Un autre conseil utile que j'ai trouvé utile consiste à créer un alias pour notre modèle dans l'élément délégué qml.

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

Et ensuite, dans ItemDelegate.qml, vous pouvez créer un alias pour le modèle afin de ne pas utiliser tout le temps le modèle.modelData.

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

Une façon très indirecte d'y parvenir est la suivante:

i.) Faire un modèle en qml

ListModel 
{
     id: thingModel

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

ii.) Ensuite, fournissez quelques fonctions javascript pour modifier cette liste, par exemple.

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

function jClearThing()
{
    thingModel.clear()
}

de même jDeleteThing etc ..

iii.) Vous pouvez lire sur comment appeler des fonctions qml depuis c ++

iv.) Exécutez une boucle sur votre liste C++ et appelez la fonction append de qml pour ajouter également toutes ces données à la liste qml.

v.) Sur toute mise à jour dans la liste latérale C++, modifiez également les données qml en utilisant la fonction ci-dessus pour les maintenir à jour.

0
Amit Tomar

Existe une bonne solution, mais ne mentionne pas la solution:

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

};

Lire et écrire sont applicables. Pas d'appel de fonction coûteux ni de copie de données. Juste un accès direct aux membres de la classe dans QML:

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

Pour plus d'informations, voir la doc: https://doc-snapshots.qt.io/qt5-dev/properties.html

0
fokhagyma