29

如果我有 C++ 中的内容列表,如何将其公开给 QML(在 Qt5 / QtQuick 2 中)?似乎 QML 只能理解QObject- 派生类,这是一个问题,因为QObjects 不能放入 aQList或复制。我该怎么做呢:

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;

    // ...

};

这样我就可以在 QML 中做这样的事情:?

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

9 回答 9

26

或者,您可以使用QVariantList( QList<QVariant>),它会在传递给 QML 时自动更改为 JavaScript 数组,并且可以从 C++ 和 QML 读取和写入

于 2013-01-14T16:09:49.897 回答
25

我在尝试解决类似问题时遇到了这个问题,我想在 QML 中使用 C++ 代码作为模型源。TheBootro 给出的答案为我指明了正确的方向,但并没有完全适合我。我没有足够的声誉直接回答他(但我确实赞成他的回答)。

我正在使用 Qt 5.0.0 我发现这个链接很有帮助

ThingManager 的定义应该修改如下

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

请注意,我将 getThings 的返回类型更改为 QList<QObject*>。如果没有此更改,Qt 会警告它“无法处理未注册的数据类型 'QList<Thing*>'”。

在 QML 代码中,Thing 的属性可以通过模型作为 model.modelData.size 和 model.modelData.name 访问。

于 2013-04-05T09:49:47.897 回答
9

在获得更多 QML 经验后,我发现拥有事物列表的最佳方式是使用QAbstractListModel.

你让你的Thing派生,QObject所以它可以存储在一个QVariant(注册后)。然后您可以将实际值Thing作为模型项返回。您可以在Repeateras中访问它model.display.a_property_of_thing。列表长度为model.count.

这有以下优点和缺点:

  1. 快速 - 它不会复制整个列表来访问一个元素。
  2. 您可以轻松获取列表更改的动画(添加、重新排列和删除项目)。
  3. 它很容易从 QML 中使用。
  4. 为了使动画能够正常工作,每当您更改列表时,您都必须做一些稍微古怪的簿记(beginInsertRows()等)

...

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 >= mThings.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];
}
于 2014-12-11T08:54:50.147 回答
5

啊,我找到了答案(我认为,未经测试):QQmlListProperty

示例中有一些用途,例如qtdeclarative/examples/quick/tutorials/gettingStartedQml/filedialog/directory.*

不幸的是,您目前只能拥有只读列表。

于 2013-01-11T23:39:09.367 回答
3

您对 QObject 是完全错误的,它们可以简单地以指针的形式提供给 QList,如下所示完美:

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;
};
于 2013-01-29T08:48:44.450 回答
2

最好的方法是使用 QQmlListProperty 。看 这个简单的例子,希望对你有所帮助。
对象和列表属性类型示例

于 2015-01-21T13:38:15.690 回答
1

题主给出的答案eatyourgreens是正确的。通过以这种方式实现您的类,您可以访问任意数量的后代。我发现另一个有用的技巧是在 qml 委托元素内为我们的模型创建一个别名。

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

然后在 ItemDelegate.qml 中,您可以为模型创建别名以不一直使用 model.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
        }
    }
}
于 2014-12-10T12:29:46.010 回答
0

实现它的一种高度间接的方法是:

i.) 在 qml 中制作模型

ListModel 
{
     id: thingModel

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

ii.) 然后提供几个 javascript 函数来修改这个列表,例如。

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

function jClearThing()
{
    thingModel.clear()
}

类似地 jDeleteThing 等。

iii.) 您可以阅读有关如何从 c++ 调用 qml 函数的信息

iv.) 在 C++ 列表上运行一个循环并调用 qml 的 append 函数将所有数据添加到 qml 列表中。

v.) 在 C++ 侧列表中的任何更新中,同时使用上述函数修改 qml 数据以使其保持更新。

于 2013-01-11T23:14:34.730 回答
0

存在一个好的,但没有提到的解决方案:

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

};

读写都适用。没有昂贵的函数调用和数据复制。只需直接访问 QML 中的类成员:

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

有关更多信息,请参阅文档:https ://doc-snapshots.qt.io/qt5-dev/properties.html

于 2018-01-23T10:46:29.247 回答