QML与C++之间的数据类型转换

在QML和C ++之间交换数据值时,它们将由QML引擎转换为具有适用于QML或C ++的正确数据类型。 这要求交换的数据具有引擎可识别的类型。

QML引擎为大量QT C++数据类型提供内置支持。此外,自定义C++类型可以注册到QML类型系统中,使它们可以用于引擎。

该页面讨论了QML引擎支持的数据类型以及如何在QML和C ++之间转换它们。

数据所有权

当数据从C ++传输到QML时,数据所有权始终由C ++保留。 该规则的例外情况是从显式C ++方法调用返回QObject时:在这种情况下,除非通过调用QQmlEngine将对象的所有权显式设置为与C ++一起使用,否则QML引擎假定该对象的所有权: 使用QQmlEngine :: CppOwnership指定的setObjectOwnership()

此外,QML引擎尊重Qt C ++对象的常规QObject父所有权语义,并且永远不会删除具有父对象的QObject实例。

基本Qt数据类型

默认情况下,QML识别以下Qt数据类型,当从C ++传递到QML时,它们会自动转换为相应的QML基本类型,反之亦然:

Qt Type QML Basic Type
bool bool
unsigned int, int int
double double
float, qreal real
QString string
QUrl url
QColor color
QFont font
QDateTime date
QPointQPointF point
QSizeQSizeF size
QRectQRectF rect
QMatrix4x4 matrix4x4
QQuaternion quaternion
QVector2DQVector3DQVector4D vector2dvector3dvector4d
Enums declared with Q_ENUM() or Q_ENUMS() enumeration

注意:Qt GUI模块提供的类,例如QColor,QFont,QQuaternion和QMatrix4x4,仅当包含Qt Quick模块时才可从QML获得。

为方便起见,可以在QML中通过字符串值或QtQml :: Qt对象提供的相关方法来指定其中许多类型。 例如,Image :: sourceSize属性的大小为size(将自动转换为QSize类型),并且可以由格式为“ widthxheight”的字符串值或Qt.size()函数指定:

Item {
    Image { sourceSize: "100x200" }
    Image { sourceSize: Qt.size(100, 200) }
}

有关更多信息,请参见QML基本类型下有关每种类型的文档。

QObject派生的类型

可以将任何QObject派生的类用作在QML和C ++之间交换数据的类型,前提是该类已在QML类型系统中注册。

引擎允许同时注册可实例化和不可实例化类型。一旦一个类被注册为QML类型,它就可以用作QML和C++之间数据交换的数据类型。有关类型注册的详细信息,请参见使用QML类型系统注册C++类型

Qt和JavaScript类型之间的转换

当在QML和C ++之间传输数据时,QML引擎具有内置支持,可将多种Qt类型转换为相关的JavaScript类型,反之亦然。 这样就可以使用这些类型并以C ++或JavaScript接收它们,而无需实现提供对数据值及其属性的访问的自定义类型。

(请注意,QML中的JavaScript环境会修改本机JavaScript对象原型(包括String,Date和Number的原型)以提供其他功能。有关更多详细信息,请参见JavaScript Host Environment。)

QVariantList和QVariantMap到JavaScript数组和对象

QML引擎提供QVariantList与JavaScript数组之间以及QVariantMap与JavaScript对象之间的自动类型转换。

例如,以下QML中定义的函数需要两个参数,即数组和对象,并使用标准JavaScript语法打印它们的内容以访问数组和对象项。 下面的C ++代码调用此函数,传递一个QVariantList和QVariantMap,它们分别自动转换为JavaScript数组和对象值:

// MyItem.qml
Item {
    function readValues(anArray, anObject) {
        for (var i=0; i<anArray.length; i++)
            console.log("Array item:", anArray[i])

        for (var prop in anObject) {
            console.log("Object item:", prop, "=", anObject[prop])
        }
    }
}

 

// C++
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));

QVariantList list;
list << 10 << QColor(Qt::green) << "bottles";

QVariantMap map;
map.insert("language", "QML");
map.insert("released", QDate(2010, 9, 21));

QMetaObject::invokeMethod(view.rootObject(), "readValues",
        Q_ARG(QVariant, QVariant::fromValue(list)),
        Q_ARG(QVariant, QVariant::fromValue(map)));

产生如下输出:

Array item: 10
Array item: #00ff00
Array item: bottles
Object item: language = QML
Object item: released = Tue Sep 21 2010 00:00:00 GMT+1000 (EST)

同样,如果C ++类型将QVariantList或QVariantMap类型用作属性类型或方法参数,则可以将值创建为QML中的JavaScript数组或对象,并在将其传递给C ++后自动转换为QVariantList或QVariantMap。

 

QDateTime到JavaScript日期

QML引擎提供QDateTime值和JavaScript Date对象之间的自动类型转换。

例如,以下QML中定义的函数需要一个JavaScript Date对象,并且还返回带有当前日期和时间的新Date对象。 下面的C ++代码调用此函数,将QDateTime值传递给引擎,当它传递给readDate()函数时,该值会由引擎自动转换为Date对象。 反过来,readDate()函数返回一个Date对象,当在C ++中收到它时,该对象将自动转换为QDateTime值:

// MyItem.qml
Item {
    function readDate(dt) {
        console.log("The given date is:", dt.toUTCString());
        return new Date();
    }
}
// C++
QQuickView view(QUrl::fromLocalFile("MyItem.qml"));

QDateTime dateTime = QDateTime::currentDateTime();
QDateTime retValue;

QMetaObject::invokeMethod(view.rootObject(), "readDate",
        Q_RETURN_ARG(QVariant, retValue),
        Q_ARG(QVariant, QVariant::fromValue(dateTime)));

qDebug() << "Value returned from readDate():" << retValue;

 

同样,如果C ++类型使用QDateTime作为属性类型或方法参数,则可以在QML中将该值创建为JavaScript Date对象,并在将其传递给C ++时自动将其转换为QDateTime值。

QTime和JavaScript日期

QML引擎提供了从QTime值到JavaScript Date对象的自动类型转换。 由于QTime值不包含日期成分,因此仅为转换创建日期成分。 因此,您不应依赖于生成的Date对象的date组件。

在后台,从JavaScript Date对象到QTime的转换是通过转换为QDateTime对象并调用其time()方法完成的。

序列类型到JavaScript数组

QML透明地支持某些C ++序列类型,使其行为类似于JavaScript数组类型。

特别是,QML当前支持:

  • QList<int>
  • QList<qreal>
  • QList<bool>
  • QList<QString> and QStringList
  • QVector<QString>
  • std::vector<QString>
  • QList<QUrl>
  • QVector<QUrl>
  • std::vector<QUrl>
  • QVector<int>
  • QVector<qreal>
  • QVector<bool>
  • std::vector<int>
  • std::vector<qreal>
  • std::vector<bool>

以及所有已注册的QList,QVector,QQueue,QStack,QSet,QLinkedList,std :: list,std :: vector,其中包含用Q_DECLARE_METATYPE标记的类型。

这些序列类型直接根据基础C ++序列实现。 这种序列可以通过两种方式暴露于QML:作为给定序列类型的Q_PROPERTY; 或作为Q_INVOKABLE方法的返回类型。 这些实现的方式存在一些差异,需要注意。

如果序列作为Q_PROPERTY公开,则按索引访问序列中的任何值将导致从QObject的属性读取序列数据,然后进行读取。 同样,修改序列中的任何值都将导致读取序列数据,然后将执行修改并将修改后的序列写回到QObject的属性。

如果该序列是从Q_INVOKABLE函数返回的,则访问和更改都便宜得多,因为不会发生读取或写入QObject属性的情况; 而是直接访问和修改C ++序列数据。

在Q_PROPERTY和从Q_INVOKABLE返回的情况下,都将复制std :: vector的元素。 这种复制可能是一项昂贵的操作,因此应谨慎使用std :: vector。

不透明地不支持其他序列类型,而是将任何其他序列类型的实例作为不透明的QVariantList在QML和C ++之间传递。

重要说明:这种序列数组类型和默认JavaScript数组类型的语义之间存在一些细微差异,这是由于在实现中使用了C ++存储类型而导致的。 特别是,从数组中删除元素将导致默认构造的值替换该元素,而不是未定义的值。 同样,将Array的length属性设置为大于其当前值的值将导致使用默认构造的元素而不是Undefined元素将Array填充为指定的长度。 最后,Qt容器类支持带符号(而不是无符号)的整数索引。 因此,尝试访问任何大于INT_MAX的索引将失败。

每种序列类型的默认构造值如下:

QList<int> integer value 0
QList<qreal> real value 0.0
QList<bool> boolean value false
QList<QString> and QStringList empty QString
QVector<QString> empty QString
std::vector<QString> empty QString
QList<QUrl> empty QUrl
QVector<QUrl> empty QUrl
std::vector<QUrl> empty QUrl
QVector<int> integer value 0
QVector<qreal> real value 0.0
QVector<bool> boolean value false
std::vector<int> integer value 0
std::vector<qreal> real value 0.0
std::vector<bool>

boolean value false

如果您希望从序列中删除元素,而不是简单地用默认构造值替换它们,请不要使用索引的删除运算符(“ delete sequence [i]”),而应使用剪接函数(“ sequence.splice(startIndex,deleteCount) )“)。

 

QByteArray转JavaScript ArrayBuffer

QML引擎提供QByteArray值和JavaScript ArrayBuffer对象之间的自动类型转换。

值类型(value type)

Qt中的某些值类型(例如QPoint)在JavaScript中表示为具有与C ++ API中相同的属性和功能的对象。 自定义C ++值类型可以使用相同的表示形式。 要使用QML引擎启用自定义值类型,需要使用Q_GADGET注释类声明。 需要在JavaScript表示中可见的属性必须使用Q_PROPERTY声明。 类似的功能需要用Q_INVOKABLE标记。 这与基于QObject的C ++ API相同。 例如,下面的Actor类被注释为小工具并具有属性:

class Actor
{
    Q_GADGET
    Q_PROPERTY(QString name READ name WRITE setName)
public:
    QString name() const { return m_name; }
    void setName(const QString &name) { m_name = name; }

private:
    QString m_name;
};

Q_DECLARE_METATYPE(Actor)

 

枚举类型

要将自定义枚举用作数据类型,必须注册其类,并且还必须使用Q_ENUM()声明该枚举,以将其注册到Qt的元对象系统。 例如,下面的Message类具有一个Status枚举:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Status status READ status NOTIFY statusChanged)
public:
    enum Status {
        Ready,
        Loading,
        Error
    };
    Q_ENUM(Status)
    Status status() const;
signals:
    void statusChanged();
};

如果Message类已在QML类型系统中注册,则可以从QML使用其Status枚举:

Message {
     onStatusChanged: {
         if (status == Message.Ready)
             console.log("Message is loaded!")
     }
 }

要将枚举用作QML中的标志类型,请参见Q_FLAG()。

注意:枚举值的名称必须以大写字母开头,以便从QML进行访问。

...
enum class Status {
          Ready,
          Loading,
          Error
}
Q_ENUM(Status)
...

枚举类在QML中注册为范围和非范围属性。 Ready值将在Message.Status.Ready和Message.Ready中注册。

使用枚举类时,可以有多个使用相同标识符的枚举。 未注册的注册将被最后注册的枚举覆盖。 对于包含此类名称冲突的类,可以通过使用特殊的Q_CLASSINFO宏注释类来禁用无范围的注册。 将名称RegisterEnumClassesUnscoped与false一起使用,以防止将作用域枚举合并到相同的名称空间中。

class Message : public QObject
    {
        Q_OBJECT
        Q_CLASSINFO("RegisterEnumClassesUnscoped", "false")
        Q_ENUM(ScopedEnum)
        Q_ENUM(OtherValue)

    public:
        enum class ScopedEnum {
              Value1,
              Value2,
              OtherValue
        };
        enum class OtherValue {
              Value1,
              Value2
        };
    };

枚举类型作为信号和方法参数

如果枚举与信号或方法都在同一类中声明,或者枚举值是在Qt命名空间中声明的值之一,则可以从QML中使用带有枚举类型参数的C ++信号和方法。

此外,如果带有enum参数的C ++信号应该可以使用connect()函数连接到QML函数,则必须使用qRegisterMetaType()注册该枚举类型。

对于QML信号,可以使用int类型将枚举值作为信号参数传递:

Message {
    signal someOtherSignal(int statusValue)

    Component.onCompleted: {
        someOtherSignal(Message.Loading)
    }
}

 

 

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章