QML类型系统

本文探讨了C++与QML的集成方式,包括在QML中定义和使用C++类型,基本类型与对象类型的区分,自定义QML类型,以及如何通过C++注册QML类型,包括实例化和非实例化类型,属性修改器和附加属性的实现。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

从C ++定义QML类型

用C ++编写QML扩展

概述-QML和C ++集成

QML类型系统

在QML文档中的对象层次结构定义中可以使用的类型可以来自各种来源。它们可能是:

  • 由QML语言本地提供
  • QML模块通过C ++注册
  • 由QML模块作为QML文档提供

 

基本类型

基本型是一种指的是简单的值,例如一个int或一个string。这与QML对象类型相反,QML对象是指具有属性,信号,方法等的对象。基本类型与对象类型不同,不能用于声明QML对象:例如,不能声明int{}对象或size{}对象。

  • 单个值(例如,int表示单个数字,var表示单个项目列表)
  • 包含一组简单的名-值对的值(例如,size是指具有widthheight属性的值)

支持的基本类型

引擎默认支持某些基本类型,不需要使用import语句,而其他一些则需要客户端导入提供它们的模块。下面列出的所有基本类型都可以用作QML文档中的类型,但以下情况除外:

  • list 必须与QML对象类型结合使用
  • enumeration 不能直接使用,因为枚举必须由已注册的QML对象类型定义

QML语言提供的基本类型

以下列出了QML语言本身支持的基本类型:

bool

Binary true/false value

double

Number with a decimal point, stored in double precision

enumeration

Named enumeration value

int

Whole number, e.g. 0, 10, or -20

list

List of QML objects

real

Number with a decimal point

string

Free form text string

url

Resource locator

var

Generic property type

QML模块提供的基本类型

QML模块可以使用更多基本类型扩展QML语言。例如,QtQuick下面列出了模块提供的基本类型:

date

Date value

point

Value with x and y attributes

rect

Value with x, y, width and height attributes

size

Value with width and height attributes

基本类型的属性更改行为

一些基本类型具有属性:例如,字体类型具有pixelSizefamilybold特性。与对象类型的属性不同,基本类型的属性不提供自己的属性更改信号。只能为基本类型属性本身创建属性更改信号处理程序:

Text {
    // invalid!
    onFont.pixelSizeChanged: doSomething()

    // also invalid!
    font {
        onPixelSizeChanged: doSomething()
    }

    // but this is ok
    onFontChanged: doSomething()
}


//属性中的基本类型发生改变,会调用 属性改变操作

Text {
    onFontChanged: console.log("font changed")

    Text { id: otherText }

    focus: true

    // changing any of the font attributes, or reassigning the property
    // to a different font value, will invoke the onFontChanged handler
    Keys.onDigit1Pressed: font.pixelSize += 1
    Keys.onDigit2Pressed: font.b = !font.b
    Keys.onDigit3Pressed: font = otherText.font
}

JavaScript类型

QML引擎支持JavaScript对象和数组。可以使用通用var类型创建和存储任何标准JavaScript 类型。

import QtQuick 2.0

Item {
    property var theArray: []  //创建数组
    property var theDate: new Date()  //创建日期

    Component.onCompleted: {
        for (var i = 0; i < 10; i++)
            theArray.push("Item " + i)
        console.log("There are", theArray.length, "items in the array")
        console.log("The time is", theDate.toUTCString())
    }
}

QML对象类型

QML对象类型是可以从中实例化QML对象的类型。QML对象类型是从QtObject派生的,并由QML模块提供。应用程序可以导入这些模块以使用它们提供的对象类型。该QtQuick模块提供了在QML中创建用户界面所需的最常见的对象类型

QML对象类型是可以实例化QML对象的类型。

用语法术语来说,QML对象类型是一种可以用来声明对象的方法,方法是指定类型名称,后跟一组包含该对象属性的花括号。这与基本类型不同,基本类型不能以相同的方式使用。例如,Rectangle是QML对象类型:可用于创建Rectangle类型对象。使用诸如int和的原始类型无法完成此操作,这些原始类型bool用于保存简单的数据类型而不是对象。

可以通过创建一个定义类型的.qml文件来定义自定义QML对象类型,如在文档中作为QML对象类型定义所讨论的那样,也可以通过从C ++定义QML类型并向QML引擎注册该类型来进行定义,如定义QML中所述来自C ++的类型。请注意,在两种情况下,类型名称都必须以大写字母开头,以便在QML文件中声明为QML对象类型。

使用QML定义类型

要创建对象类型,应将QML文档放入名为<TypeName> .qml的文本文件中,其中<TypeName>是所需的类型名称。类型名称具有以下要求:

  • 它必须由字母数字字符或下划线组成。
  • 它必须以大写字母开头。

然后,引擎会自动将该文档识别为QML类型的定义。另外,在解析QML类型名称时,与引擎在直接目录中搜索时一样,以这种方式定义的类型将自动提供给同一目录中的其他QML文件

// SquareButton.qml
import QtQuick 2.0

Rectangle {
    id: root

    property bool pressed: mouseArea.pressed

    signal buttonClicked(real xPos, real yPos)

    function randomizeColor() {
        root.color = Qt.rgba(Math.random(), Math.random(), Math.random(), 1)
    }

    property int side: 100
    width: side; height: side
    color: "red"

    MouseArea {
        id: mouseArea
        anchors.fill: parent
        onClicked: root.buttonClicked(mouse.x, mouse.y)
    }
}

//使用

// application.qml
import QtQuick 2.0

SquareButton {
    id: squareButton

    onButtonClicked: {
        console.log("Clicked", xPos, yPos)
        randomizeColor()
    }

    Text { text: squareButton.pressed ? "Down" : "Up" }
}

从C ++定义对象类型

 

注册实例化对象类型

任何QObject派生的C ++类都可以注册为QML对象类型的定义

要将QObject派生的类注册为可实例化的QML对象类型,请调用qmlRegisterType()将该类作为QML类型注册到特定的类型名称空间中。然后,客户端可以导入该名称空间以使用该类型。

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
    Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
    // ...
};

//使用qmlRegisterType注册C++类,就可以在QML文档中使用
qmlRegisterType<Message>("com.mycompany.messaging", 1, 0, "Message");


//在qml中调用 

import com.mycompany.messaging 1.0

Message {
    author: "Amelie"
    creationDate: new Date()
}

注册非实例类型

有时,可能需要向QML类型系统注册QObject派生的类,而不是将其注册为可实例化的类型。例如

  • 接口类型
  • 基类
  • 声明的枚举
  • 单例

Qt的QML模块提供用于注册非实例类型的几种方法:

  • qmlRegisterType()(无参数)注册无法实例化且无法从QML引用的C ++类型。这使引擎能够强制转换QML实例化继承自基类的类型。
  • qmlRegisterInterface()注册现有的Qt接口类型。该类型不能从QML实例化,并且不能使用它声明QML属性。但是,从QML使用这种类型的C ++属性将执行预期的接口强制转换。
  • qmlRegisterUncreatableType()注册一个命名的C ++类型,该类型不可实例化,但应标识为QML类型系统的类型。如果应从QML访问类型的枚举或附加属性,但该类型本身不可实例化,则此方法很有用。
  • qmlRegisterSingletonType()注册可以从QML导入的单例类型,如下所述。

请注意,向QML类型系统注册的所有C ++类型都必须是QObject派生的,即使它们不是不可实例化的。

用单例类型注册单例对象

//假设MyThemeModule 已经注册了单例类型

import MyThemeModule 1.0 as Theme

Rectangle {
    color: Theme.color // binding.
}

具体单例类型的定义见https://doc.qt.io/qt-5/qqmlengine.html#qmlRegisterSingletonType

类型修订和版本

许多类型注册功能要求为注册的类型指定版本。类型修订版和版本允许新属性或方法存在于新版本中,同时保持与先前版本的兼容性。

// main.qml
import QtQuick 1.0

Item {
    id: root
    MyType {}
}

// MyType.qml
import MyTypes 1.0

CppType {
    value: root.x
}


//在版本1.1中添加root属性
class CppType : public BaseType
{
    Q_OBJECT
    Q_PROPERTY(int root READ root WRITE setRoot NOTIFY rootChanged REVISION 1)

signals:
    Q_REVISION(1) void rootChanged();
};

qmlRegisterType<CppType,1>("MyTypes", 1, 1, "CppType")

注册扩展对象

https://doc.qt.io/qt-5/qtqml-referenceexamples-extended-example.html

可以在现有的类上添加扩展功能

示例 https://code.qt.io/cgit/qt/qtdeclarative.git/tree/examples/qml/referenceexamples/extended?h=5.14

定义特定于QML的类型和属性

以下示例实现了附加属性

import QtQuick 2.0

Item {
    width: 100; height: 100

    focus: true
    Keys.enabled: false  //附加属性
    Keys.onReturnPressed: console.log("Return key was pressed")
}

实现附加对象的步骤

考虑以上示例时,涉及多个方面:

  • 有一个匿名附加对象类型的实例,带有enabledreturnPressed信号,该实例已附加到Item对象,以使其能够访问和设置这些属性。
  • 将附加对象的实例附加到item上。
  • Keys提供了通过限定符,“Keys”访问的属性附加对象类型

当QML引擎处理此代码时,它将创建附加对象类型的单个实例,并将该实例附加到Item对象,从而为其提供对实例的enabledreturnPressed属性的访问。

C++签名对象使用以下语法:

static <AttachedPropertiesType> *qmlAttachedProperties(QObject *object);

//此方法应返回attached object type(定义了要附加的属性和信号)的实例。

引擎为每个附加对象实例最多调用一次此方法,因为引擎会缓存返回的实例指针以用于后续的附加属性访问。因此,只有在object销毁附件后才能删除附件对象。

通过调用带有QML_HAS_ATTACHED_PROPERTIES标志的QML_DECLARE_TYPEINFO()宏将其声明为附加类型

附加对象例子

在Message添加附加对象

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
    Q_PROPERTY(QDateTime creationDate READ creationDate WRITE setCreationDate NOTIFY creationDateChanged)
public:
    // ...
};

要添加附加对象需要:

  • 一个匿名的attached object type实例,提供published信号和expired 属性的附加对象类型的实例。该类型由MessageBoardAttachedType以下实现
  • 一个Message对象,将成为附加对象
  • MessageBoard类型,这将是附接类型所使用的Message对象来访问附加的属性
//定义一个要附加的对象实例,里面定义了要添加的信号和属性

class MessageBoardAttachedType : public QObject
{
    Q_OBJECT
    //过期属性
    Q_PROPERTY(bool expired READ expired WRITE setExpired NOTIFY expiredChanged)
public:
    MessageBoardAttachedType(QObject *parent);
    bool expired() const;   //
    void setExpired(bool expired);
signals:
    void published();  //发布信号
    void expiredChanged();
};

class MessageBoard : public QObject
{
    Q_OBJECT
public:
    static MessageBoardAttachedType *qmlAttachedProperties(QObject *object)
    {
        return new MessageBoardAttachedType(object);
    }
};
QML_DECLARE_TYPEINFO(MessageBoard, QML_HAS_ATTACHED_PROPERTIES)

 

Message {
    author: "Amelie"
    creationDate: new Date()

    MessageBoard.expired: creationDate < new Date("January 01, 2015 10:45:00")
    MessageBoard.onPublished: console.log("Message by", author, "has been
published!")
}

附加属性分三个步骤:

  1. 创建一个要属性加属性的类,实现所有要添加的属性和信号。例MessageBoardAttachedType 
  2. 要把MessageBoardAttachedType定义为附加属性还需要做MessageBoard 干的事,实现qmlAttachedProperties函数返回MessageBoardAttachedType ,并使用QML_DECLARE_TYPEINFO(MessageBoard, QML_HAS_ATTACHED_PROPERTIES)
  3. 把MessageBoard 定义为附加属性。
  4. 现在就可以在Message使用MessageBoard做为句柄访问MessageBoardAttachedType 中的属性的信号了。

使用C++代码访问附加属性使用

Message *msg = someMessageInstance();
MessageBoardAttachedType *attached =
        qobject_cast<MessageBoardAttachedType*>(qmlAttachedPropertiesObject<MessageBoard>(msg));

qDebug() << "Value of MessageBoard.expired:" << attached->expired();

属性修改器

属性修改器分为两种

  1. 是属性写拦截器,不能注册,现在只有 Behavior在使用拦截器
  2. property value sources,

属性修改器可以使用 "<ModifierType> on <propertyName>"语法实现,例

import QtQuick 2.0

Item {
    width: 400
    height: 50

    Rectangle {
        width: 50
        height: 50
        color: "red"

        NumberAnimation on x {   //属性修改器,循环修改x的值
            from: 0
            to: 350
            loops: Animation.Infinite
            duration: 2000
        }
    }
}

客户端可以注册自己的属性值源类型,但当前不能注册属性值写拦截器。

属性值源

属性值源可以随时间自动更新属性值。使用<PropertyValueSource> on <property> 语法。动画就是使用属性源来实现的

使用C++实现需要继承 QQmlPropertyValueSource 例:

//使用修改器必继承QQmlPropertyValueSource
class RandomNumberGenerator : public QObject, public QQmlPropertyValueSource
{
    Q_OBJECT
    Q_INTERFACES(QQmlPropertyValueSource) //?
    Q_PROPERTY(int maxValue READ maxValue WRITE setMaxValue NOTIFY maxValueChanged);
public:
    RandomNumberGenerator(QObject *parent)
        : QObject(parent), m_maxValue(100)
    {
        QObject::connect(&m_timer, SIGNAL(timeout()), SLOT(updateProperty()));
        m_timer.start(500);  //500毫秒调用一次updateProperty
    }

    int maxValue() const;
    void setMaxValue(int maxValue);

//初始化要修改的属性,具体属性是在qml中使用on指定的
    virtual void setTarget(const QQmlProperty &prop) { m_targetProperty = prop; }

signals:
    void maxValueChanged();

private slots:
    void updateProperty() {
        //将On后的属性值改为随机生成的值
        m_targetProperty.write(QRandomGenerator::global()->bounded(m_maxValue));
    }

private:
    QQmlProperty m_targetProperty;
    QTimer m_timer;
    int m_maxValue;
};

使用示例

import QtQuick 2.0

Item {
    width: 300; height: 300

    Rectangle {
        RandomNumberGenerator on width { maxValue: 300 }  //随机修改rect的宽度(0-300之间的数),

        height: 100
        color: "red"
    }
}

 

为QML对象类型指定默认属性

C++定义默认属性

class MessageBoard : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
    Q_CLASSINFO("DefaultProperty", "messages")  //定义一个默认属性
public:
    QQmlListProperty<Message> messages();

private:
    QList<Message *> m_messages;
};

使用一个默认属性

MessageBoard {
    Message { author: "Naomi" }
    Message { author: "Clancy" }
}

不使用默认属性

MessageBoard {
    messages: [
        Message { author: "Naomi" },
        Message { author: "Clancy" }
    ]
}

使用Qt Quick模块定义可视化组件

C++类需要继承QQuickItem,来开发可视化组件,“ 用C ++编写QML扩展”教程演示了如何在C ++中实现基于QQuickItem的可视项并将其集成到基于Qt Quick的用户界面中。 

 

接收对象初始化通知

如果想在组件加载完成后进行初始化,可以继承QQmlParserStatus ,并使用Q_INTERFACES()进行注册

class MyQmlType : public QObject, public QQmlParserStatus
{
    Q_OBJECT
    Q_INTERFACES(QQmlParserStatus)
public:
    virtual void componentComplete()
    {
        // Perform some initialization here now that the object is fully created
    }
};

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值