【问题标题】:Accessing C++ QLists from QML从 QML 访问 C++ QLists
【发布时间】:2013-01-11 21:52:39
【问题描述】:

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

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;

【问题讨论】:

  • 在黑暗中拍摄,但也许 Q_DECLARE_METATYPE() 和/或 qRegisterMetaType() 会有所帮助?
  • 请注意,从技术上讲,QMl 可以处理从 QDeclarativeObject 派生的任何内容。

标签: c++ qml qt5 qlist qtquick2


【解决方案1】:

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

【讨论】:

  • 但是QVariant 不能保存复合值可以吗?
  • QVariant 几乎可以容纳“一切”,包括另一个QVariantListQVariantMap
  • 啊有趣,我不知道!
  • 一般来说,支持许多模板类型的 QList:QList C++ sequence types are supported transparently in QML as JavaScript Array typesqt-project.org/doc/qt-5/qtqml-cppintegration-data.html
【解决方案2】:

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

我正在使用 Qt 5.0.0 我发现this link 很有帮助

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。如果没有此更改,Qt 会警告它“无法处理未注册的数据类型 'QList'”。

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

【讨论】:

  • 通常支持许多模板类型的 QList:QList C++ sequence types are supported transparently in QML as JavaScript Array typesqt-project.org/doc/qt-5/qtqml-cppintegration-data.html
  • 您可以使用 qmlRegisterType("com.yourapp",1,0,"Thing") 函数注册类型,而无需使用 QObject。您的“事物”必须声明为一个类。
【解决方案3】:

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

您使您的Thing 派生自QObject,因此它可以存储在QVariant 中(在注册之后)。然后您可以将实际的Thing 作为模型项返回。您可以在Repeater 中以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];
}

【讨论】:

  • 这确实是最好的方法,即使设置起来并不容易——至少是第一次。
【解决方案4】:

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

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

很遗憾,您目前只能拥有只读列表。

【讨论】:

【解决方案5】:

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

【讨论】:

  • 最好使用“QList getThings()”。
  • 我不能让它工作错误:无法处理未注册的数据类型 'QList' 的属性...它与 QObject* 一起工作正常
  • 但是可以暴露写吗?
【解决方案6】:

最好的方式使用QQmlListProperty .看这个简单的例子,我希望对你有所帮助。
Object and List Property Types Example

【讨论】:

    【解决方案7】:

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

    【讨论】:

      【解决方案8】:

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

      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.) 你可以阅读how to call qml functions from c++

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

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

      【讨论】:

        【解决方案9】:

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

        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

        【讨论】:

        • 没那么简单。使用上述方法,在 QML 中,我无法访问 count() 之类的方法,并且由于 QList 实际上不是 Javascript 数组,因此也不能使用 length 属性。
        • 如果 QML 和 C++ 之间的数据类型转换得到官方支持 (https://doc.qt.io/qt-5/qtqml-cppintegration-data.html) 的数据类型,那么对我来说它就是这样工作的:var a = thingManager.things; x = a.length QList.
        猜你喜欢
        • 2012-03-19
        • 2018-05-17
        • 1970-01-01
        • 1970-01-01
        • 2013-12-04
        • 1970-01-01
        • 1970-01-01
        • 2012-01-25
        相关资源
        最近更新 更多