QML/QT 如何将对象从 C++ 转换为 QML?

Posted

技术标签:

【中文标题】QML/QT 如何将对象从 C++ 转换为 QML?【英文标题】:QML/QT How to convert object from C++ to QML? 【发布时间】:2020-09-23 19:32:13 【问题描述】:

我花了一些时间,但最终我设法将 javascript/QML POJO 转换为不是从 QObject 派生的自定义对象。

我认为通过一个工作示例更容易理解我的问题。所以让我们从这个开始:

struct SomeType  /* Just a plain struct that does not derive from QObject! */ ; 

SomeType FooFactory::convertQMLToSomeType(const QJSValue& val) 
  SomeType result = /*... some kind of conversion takes place here ... */
  return result;


void FooFactory::registerTypes(QQmlEngine& engine) 
  QMetaType::registerConverter<QJSValue, SomeType>(FooFactory::convertQMLToSomeType);

它的作用是注册一个转换器,用于将QSValue 转换为SomeType。所以现在,每当我在 QML 中做这样的事情时

my_prop =  "foo": "some plain javascript object" ;

假设 my_prop 在相应的 C++ 类中是这样暴露的:

Q_PROPERTY(SomeType my_prop MEMBER _myProp);
SomeType _myProp;

// Somewhere else outside the class, this is needed for registering the converter
Q_DECLARE_METATYPE(SomeType);

字符串被隐式转换为SomeType,无需手动操作。

..

太棒了!

但是转换的相反方向呢?我需要 QML 来处理字符串,而不是 QVariant(SomeType) 对象(在处理元系统时,QT 总是在内部使用 QVariant 包装器来存储用户定义的类型)。

我已经尝试过像这样注册一个逆变器:

QMetaType::registerConverter&lt;SomeType, QJSValue&gt;(FooFactory::convertBackToQML);

或者这个

QMetaType::registerConverter&lt;QVariant(SomeType), QJSValue&gt;(FooFactory::convertBackToQML);

但是这些方法都不起作用。我相信第二行很有希望,但由于注册静态元类型的问题,我什至无法编译那行。

那么,我将如何解决这个问题?简短提醒一下,我无法从 QObject 导出 SomeType,是的,我知道这是做这类事情的最常见方法。

有人有想法吗?还是我在叫错树?非常感谢!

【问题讨论】:

你的结构可以是 Q_GADGET 而不是 QObject 吗? 很遗憾没有。该结构是从与 QT 没有任何联系的封装库中使用的,因此我无法在其中添加这些声明。 【参考方案1】:

这可能不是您想要的,但它也可能是您唯一的选择。您可以围绕您的结构创建一个 QObject 包装类。只需为要公开的结构中的任何值创建属性。

class SomeWrapper : public QObject

    Q_OBJECT
    Q_PROPERTY(QString someData READ someData WRITE setSomeData NOTIFY someDataChanged)

public:
    explicit SomeWrapper(QObject *parent = nullptr) : QObject(parent) 

    QString someData()  return m_struct.someData; 
    void setSomeData(QString data)
    
        if (data != m_struct.someData)
        
            m_struct.someData = data;
            emit someDataChanged();
        
    

signals:
    void someDataChanged();

private:
    SomeType m_struct;
;

【讨论】:

【参考方案2】:

如果您想像使用普通 JavaScript 对象一样操作您的结构,那么另一种选择可能是使用 QVariantMap 作为您的属性的类型。然后,您可以在属性的 getter 和 setter 中定义所需的转换:

class Whichever : public QObject 
    Q_OBJECT
    Q_PROPERTY(QVariantMap my_prop READ myProp WRITE setMyProp)

    QVariantMap myProp() const 
         QVariantMap map;
         map["some_field"] = _myProp.someField;
         // Fill the other fields as needed.
         return map;
    

    void setMyProp(const QVariantMap& map) 
        _myProp.someField = map["some_field"].toString();
        // Fill the other _myProp fields as needed.
    
;

QVariantMap 和实际 JavaScript 对象之间的转换将由 QML 引擎自动处理。通过嵌套相应的QVariantMaps 也应该可以嵌套 JavaScript 对象。

当然,这个选项使得属性声明对于它对应的类型不是那么明确,如果你需要声明这个类型的多个属性,可能需要更多的样板代码(你可以定义和使用你自己的如果需要,宏)。但这可能是实现您所描述的最简单的方法之一。

【讨论】:

以上是关于QML/QT 如何将对象从 C++ 转换为 QML?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 C++ 代码将 QML 对象转换为 QQuickWindow?

如何在 Blackberry Cascades qml 和 qt 中点击列表项组件

在 QML qt 中控制 OSM 映射

如何在 QML 中隐藏光标

如何在QML(QT Creator)中使用发光脉动效果制作元素动画?

在 QML/Qt 中模糊部分背景图像