将 C++ 类型属性暴露给 QML

目录
  • 一、数据类型处理和所有权
    • 1.1、暴露属性
    • 1.2、使用通知信号的注意事项
    • 1.3、具有对象类型的属性
    • 1.4、具有对象列表类型的属性
    • 1.5、分组属性
  • 二、暴露方法
  • 三、暴露信号

可以使用 C++ 代码中定义的功能轻松扩展 QML。由于 QML 引擎与 Qt 元对象系统的紧密集成,QObject 派生类公开的任何功能都可以从 QML 代码访问。这使得 C++ 数据和函数可以直接从 QML 访问,通常几乎不需要修改。

QML 引擎能够通过元对象系统反射 QObject 实例。这意味着任何 QML 代码都可以访问 QObject 派生类的实例的以下成员:

  • 属性
  • 方法(前提是它们是 public slots 或用 Q_INVOKABLE 标记)
  • 信号

(此外,如果枚举是用 Q_ENUMS 声明的,则也可以访问)

一、数据类型处理和所有权

任何从 C++ 传输到 QML 的数据,无论是作为属性值、方法参数或返回值,还是信号参数值,都必须是 QML 引擎支持的类型。

默认情况下,引擎支持许多 Qt C++ 类型,并且可以在从 QML 使用时自动适当地转换它们。

1.1、暴露属性

可以使用 Q_PROPERTY() 宏为任何 QObject 派生类指定属性。

例如,下面是一个具有作者属性的 Message 类。 正如 Q_PROPERTY 宏调用所指定的,这个属性可以通过 author() 方法读取,通过 setAuthor() 方法写入:

注意:不要使用 typedef 或 using 指定 Q_PROPERTY 类型,因为它们会混淆 moc。 这可能会使某些类型比较失败。

错误用法:

using FooEnum = Foo::Enum;

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(FooEnum enum READ enum WRITE setEnum NOTIFY enumChanged)
};

正确用法:

class Bar : public QObject
{
    Q_OBJECT
    Q_PROPERTY(Foo::Enum enum READ enum WRITE setEnum NOTIFY enumChanged)
};

暴露属性实例:

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString author READ author WRITE setAuthor NOTIFY authorChanged)
public:
    void setAuthor(const QString &a)
    {
        if (a != m_author)
        {
            m_author = a;
            emit authorChanged();
        }
    }
    QString author() const
    {
        return m_author;
    }
signals:
    void authorChanged();
private:
    QString m_author;
};

如果在从 C++ 加载名为 MyItem.qml 的文件时将此类的实例设置为上下文属性:

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    QQuickView view;
    Message msg;
    view.engine()->rootContext()->setContextProperty("msg", &msg);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}
然

可以从 MyItem.qml 中读取 author 属性:

// MyItem.qml
import QtQuick 2.0

Text
{
    width: 100; height: 100
    text: msg.author    // 调用 Message::author() 来获取这个值

    Component.onCompleted:
    {
        msg.author = "Jonah"  // 调用 Message::setAuthor()
    }
}

为了实现C++与 QML 的最大互操作性,任何可写的属性都应该有一个关联的 NOTIFY 信号,该信号在属性值更改时发出。这允许属性与属性绑定一起使用,这是 QML 的一个基本特性,它通过在任何依赖项的值发生变化时自动更新属性来强制执行属性之间的关系。即发出信号会通知 QML 引擎更新任何涉及属性的绑定。

上面示例中如果 author 属性可写但没有关联的 NOTIFY 信号,则文本值将使用 Message::author() 返回的初始值进行初始化,但不会随此属性的任何后续更改而更新。此外,任何从 QML 绑定到属性的尝试都会从引擎产生运行时警告。

建议将 NOTIFY 信号命名为 <property>Changed,其中 <property> 是属性的名称。QML 引擎生成的关联属性更改信号处理程序将始终采用 on<Property>Changed 形式,无论相关 C++ 信号的名称如何,因此建议信号名称遵循此约定以避免任何混淆。

1.2、使用通知信号的注意事项

开发人员应确保仅在属性值实际更改时才发出属性更改信号。此外,如果一个属性或一组属性不经常使用,则允许对多个属性使用相同的 NOTIFY 信号。这应该小心完成以确保性能不会受到影响。

NOTIFY 信号的存在确实会产生很小的开销。在某些情况下,属性的值是在对象构造时设置的,随后不会更改。在这些情况下,可以将 CONSTANT 属性而不是 NOTIFY 信号添加到属性声明中。

CONSTANT 属性应仅用于其值仅在类构造函数中设置和最终确定的属性。

1.3、具有对象类型的属性

对象类型属性可从 QML 访问,前提是对象类型已正确注册到 QML 类型系统。

例如:Message 类型可能具有 MessageBody* 类型的 body 属性

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageBody* body READ body WRITE setBody NOTIFY bodyChanged)
public:
    MessageBody* body() const;
    void setBody(MessageBody* body);
};

class MessageBody : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString text READ text WRITE text NOTIFY textChanged)
// ...
}

假设 Message 类型已在 QML 类型系统中注册,

允许将其用作 QML 代码中的对象类型:

Message
{
    // ...
}

如果 MessageBody 类型也注册到类型系统,则可以将 MessageBody 分配给 Message body 属性:

Message
{
    body: MessageBody
    {
        text: "Hello, world!"
    }
}

1.4、具有对象列表类型的属性

包含 QObject 派生类型列表的属性也可以暴露给 QML。然而,应该使用 QQmlListProperty 而不是 QList<T> 作为属性类型。 这是因为 QList 不是 QObject 派生的类型,因此无法通过 Qt 元对象系统提供必要的 QML 属性特征,例如修改列表时的信号通知。

例如,下面的 MessageBoard 类有一个 QQmlListProperty 类型的消息属性,用于存储 Message 实例列表:

class MessageBoard : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QQmlListProperty<Message> messages READ messages)
public:
    QQmlListProperty<Message> messages()
    {
        return QQmlListProperty<Message>(this, 0, &MessageBoard::append_message);
    }

private:
    static void append_message(QQmlListProperty<Message> *list, Message *msg)
    {
        MessageBoard *msgBoard = qobject_cast<MessageBoard *>(list->object);
        if (msg)
            msgBoard->m_messages.append(msg);
    }
    QList<Message *> m_messages;
};
QQmlListProperty 的模板类类型(在本例中为 Message)必须在 QML 类型系统中注册。

1.5、分组属性

任何只读的对象类型属性都可以作为分组属性从 QML 代码访问。这可用于公开一组相关属性,这些属性描述了类型的一组属性。

例如,假设 Message::author 属性的类型是 MessageAuthor 而不是简单的字符串,具有 name email 的子属性:

class MessageAuthor : public QObject
{
    Q_PROPERTY(QString name READ name WRITE setName)
    Q_PROPERTY(QString email READ email WRITE setEmail)
public:
    ...
};

class Message : public QObject
{
    Q_OBJECT
    Q_PROPERTY(MessageAuthor* author READ author)
public:
    Message(QObject *parent)
        : QObject(parent), m_author(new MessageAuthor(this))
    {
    }
    MessageAuthor *author() const
    {
        return m_author;
    }
private:
    MessageAuthor *m_author;
};

可以使用 QML 中的分组属性语法编写 author 属性:

Message
{
    author.name: "Alexandra"
    author.email: "alexandra@mail.com"
}

分组属性是只读的,并且在构造时由父对象初始化为有效值。

分组属性的子属性可以从 QML 修改,但分组属性对象本身永远不会改变,

二、暴露方法

QObject 派生类型的任何方法都可以从 QML 代码访问,只要它满足其中一项:

  • Q_INVOKABLE() 宏标记的公共方法
  • 作为 public slot 的方法

例如,下面的 MessageBoard 类有一个使用 Q_INVOKABLE 宏标记的 postMessage() 方法,以及一个公共槽的 refresh() 方法:

class MessageBoard : public QObject
{
    Q_OBJECT
public:
    Q_INVOKABLE bool postMessage(const QString &msg)
    {
        qDebug() << "Called the C++ method with" << msg;
        return true;
    }

public slots:
    void refresh()
    {
        qDebug() << "Called the C++ slot";
    }
};

如果将 MessageBoard 的实例设置为文件 MyItem.qml 的上下文数据,则 MyItem.qml 可以调用两个方法,如下例所示:

int main(int argc, char *argv[])
{
    QGuiApplication app(argc, argv);

    MessageBoard msgBoard;
    QQuickView view;
    view.engine()->rootContext()->setContextProperty("msgBoard", &msgBoard);
    view.setSource(QUrl::fromLocalFile("MyItem.qml"));
    view.show();

    return app.exec();
}
// MyItem.qml
import QtQuick 2.0

Item
{
    width: 100; height: 100

    MouseArea
    {
        anchors.fill: parent
        onClicked:
        {
            var result = msgBoard.postMessage("Hello from QML")
            console.log("Result of postMessage():", result)
            msgBoard.refresh();
        }
    }
}

如果 C++ 方法具有 QObject* 类型的参数,则可以使用对象 id 或引用该对象的 JavaScript 变量值从 QML 传递参数值。

QML 支持调用重载的 C++ 函数。 如果存在多个同名但参数不同的 C++ 函数,则会根据提供的参数数量和类型调用正确的函数。

当从 QML 中的 JavaScript 表达式访问时,从 C++ 方法返回的值将转换为 JavaScript 值。

三、暴露信号

QObject 派生类型的任何公共信号都可以从 QML 代码访问。

QML 引擎自动为从 QML 使用的 QObject 派生类型的任何信号创建信号处理程序。信号处理程序始终命名为 on<Signal>,其中 <Signal> 是信号的名称,首字母大写。 信号传递的所有参数都可以通过参数名称在信号处理程序中使用。

例如,假设 MessageBoard 类有一个带有单个参数主题的 newMessagePosted() 信号:

class MessageBoard : public QObject
{
    Q_OBJECT
public:
   // ...
signals:
   void newMessagePosted(const QString &subject);
};

 如果 MessageBoard 类型已注册到 QML 类型系统,则在 QML 中声明的 MessageBoard 对象可以使用名为 onNewMessagePosted 的信号处理程序接收 newMessagePosted() 信号,并检查主题参数值:

MessageBoard
{
    onNewMessagePosted: (subject)=> console.log("New message received:", subject)
}

与属性值和方法参数一样,信号参数必须具有 QML 引擎支持的类型。(使用未注册的类型不会产生错误,但处理程序将无法访问参数值。)

请注意:QML引擎无法区分名称相同但参数不同的信号,类可能有多个同名的信号,但只有最后一个信号可以作为 QML 信号访问。

到此这篇关于将 C++ 类型属性暴露给 QML的文章就介绍到这了,更多相关  C++ 类型属性暴露给 QML内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • 将 C++ 类型属性暴露给 QML

    目录 一.数据类型处理和所有权 1.1.暴露属性 1.2.使用通知信号的注意事项 1.3.具有对象类型的属性 1.4.具有对象列表类型的属性 1.5.分组属性 二.暴露方法 三.暴露信号 可以使用 C++ 代码中定义的功能轻松扩展 QML.由于 QML 引擎与 Qt 元对象系统的紧密集成,QObject 派生类公开的任何功能都可以从 QML 代码访问.这使得 C++ 数据和函数可以直接从 QML 访问,通常几乎不需要修改. QML 引擎能够通过元对象系统反射 QObject 实例.这意味着任何

  • 详解Spring注入集合(数组、List、Map、Set)类型属性

    注入集合(数组.List.Map.Set)类型属性 (1)创建类,定义数组,list,map,set类型属性,并且生成对应的set方法. (2)在spring配置文件中进行配置. Stu类: package com.Keafmd.spring5.collectiontype; import java.util.Arrays; import java.util.List; import java.util.Map; import java.util.Set; /** * Keafmd * * @C

  • Spring IOC容器Bean管理XML注入集合类型属性

    目录 一.定义数组.list.map.set类型属性 二.配置文件中进行对应配置 三.注入对象集合类型 四.提取注入集合的部分 1. 引入名称空间 util 2. 使用 util 标签完成集合注入的提取 一.定义数组.list.map.set类型属性 创建类.定义数组.list.map.set类型属性,生成对应set方法. package com.pingguo.spring5.collectiontype; import java.util.Arrays; import java.util.L

  • Go语言反射获取类型属性和方法示例

    本系列文章,我将会进一步加深对 Go 语言的讲解,更一步介绍 Go 中的包管理.反射和并发等高级特性. 前面一篇文章主要介绍了 reflect.Type 类型对象.本文将会继续介绍 Go 反射 reflect.StructField 和 reflect.Method 相关的内容. reflect.StructField 和 reflect.Method 如果变量是一个结构体,我们还可以通过结构体域类型对象 reflect.StructField 来获取结构体下字段的类型属性.Type 接口下提供

  • jquery自定义属性(类型/属性值)

    复制代码 代码如下: <script src="jquery-1.9.1.js" type="text/javascript"></script> <script type="text/javascript"> $(function () { var ping = ''; switch ($('#Select3 option:selected').text()) { case '图片': ping += '&l

  • Python的代理类实现,控制访问和修改属性的权限你都了解吗

    目录 本篇文章主要内容 代理类的一个简单的实现方式示例 一个实现日志输出的代理类的简化示例 总结 本篇文章主要内容 代理类主要功能是将一个类实例的属性访问和控制代理到代码内部另外一个实例类,将想对外公布的属性的访问和控制权交给代理类来操作,保留不想对外公布的属性的访问或控制权,比如只读访问,日志功能 1.代理类实现被代理类的属性访问和修改权限控制 2.异常捕获代理类的简化示例 代理类的一个简单的实现方式示例 目标:实现类Product的实例属性让另一个类Proxy来代理访问和控制,想将对外公布的

  • 调整CSS类型的顺序改变链接翻滚效果

    你也许已经意识到,你可以通过指定每一键接的不同风格以建立CSS翻滚效果,这些链接包括普通的链接link (normal), 访问,翻转,以及激活.并且,你可能也知道CSS类型的顺序可以产生效果上的差别,CSS代码后顺序的风格将会取代针对于相同元素的前顺序的风格.建立翻滚效果的类型顺序显得相当重要.     现在让我们看看如何安排链接状态的类型在不产生冲突的情况下支持正常的翻滚效果,并且如何重新安排这些类型顺序以获得不同的翻滚效果. 链接状态 典型的CSS翻滚效果依赖于超链接中四个状态之一的独立类

  • java ArrayList集合中的某个对象属性进行排序的实现代码

    开发中有时候需要自己封装分页排序时,List如何对某一属性排序呢,分享一个小实例,大家共勉,希望能对大家有用,请多多指教. 1.Student的Bean如下: public class Student { private int age; private String name; private String weight; public String getWeight() { return weight; } public void setWeight(String weight) { th

  • ASP.NET2.0服务器控件之类型转换器

    类型转换器是实现自定义服务器控件属性过程中比较重要的内容.本文将对类型转换器的基本概念和实现方法进行介绍. 1. 类型转换器基本概念 类型转换器是自定义服务器控件的辅助性功能实现.它主要用于执行从字符串表示形式到指定类型之间的双向转换.例如,以文本形式表示属性值,将用户输入的文本转换为相应数据类型等等,都应用了类型转换器. 对于多数基本数据类型(如Int32.Bool.Char.String.枚举类型等),.net框架已经为它们提供了默认的类型转换器,这些类型转换器完成从字符串到相关值的转换并执

  • 详解Swift编程中的方法与属性的概念

    方法 在 Swift 中特定类型的相关联功能被称为方法.在 Objective C 中类是用来定义方法,其中作为 Swift 语言为用户提供了灵活性,类,结构和枚举中可以定义使用方法. 实例方法 在 Swift 语言,类,结构和枚举实例通过实例方法访问. 实例方法提供的功能 访问和修改实例属性 函数关联实例的需要 实例方法可以写在花括号 {} 内.它隐含的访问方法和类实例的属性.当该类型指定具体实例它调用获得访问该特定实例. 语法 复制代码 代码如下: func funcname(Paramet

随机推荐