QML学习笔记(四十七)QML与C++交互:上下文对象

前言

在之前的学习中,我们学习了上下文属性暴露和Q_PROPERTY,这里先简单复习一下。
上下文属性 暴露,指的是将某个C++类实例对象取别名,暴露给qml全局范围,此后在qml中可以直接引用该别名的接口,进而运行到c++内部的函数。(但要注意条件,C++的槽函数和信号可以直接被引用,公共接口需要添加Q_INVOKABLE标志,而普通的成员函数则无法直接被引用。值得一提的是,普通的QString也可以取别名暴露给qml端,并不是只有我们自定义的c++类可以。总结一下:信号、槽、Q_INVOKABLE 三者可直接调用,其余函数不行。)
Q_PROPERTY宏 ,它可以将C++中的某些成员变量设置成可在qml端引用的变量属性,此时要在c++端实现Q_PROPERTY中包含的设置和取值、更改信号等方法才能生效。它也要搭配上下文属性等暴露方法,把c++类对象取别名。之后在qml端,就可以直接引用类对象别名+变量名的方式,实现值引用或属性绑定等功能了。(要注意Q_PROPERTY定义的属性名和c++中成员变量的名字并不相同,要注意区分。)

而这次要介绍的上下文对象,我们可以将C++类对象直接整个暴露给qml端。此时在qml侧无需使用该C++类对象的名字,而是直接引用它的变量名即可。(前提是这些变量名已经做了Q_PROPERTY的设置,并且要注意根对象只能有一个,而且不能与 QML 自身 id 冲突)

这样说有些抽象,直接看代码:

一、代码实例

先准备一个C++类,名叫PropertyWrapper ,它的功能是每秒钟定时更改姓氏和名字。这里准备将lastname 和firstname 暴露出去。

cpp 复制代码
#ifndef PROPERTYWRAPPER_H
#define PROPERTYWRAPPER_H

#include <QObject>
#include <QTimer>

class PropertyWrapper : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString lastname READ lastname WRITE setLastname NOTIFY lastnameChanged)
    Q_PROPERTY(QString firstname READ firstname WRITE setFirstname NOTIFY firstnameChanged)

public:
    explicit PropertyWrapper(QObject *parent = nullptr);
    QString lastname() const;
    QString firstname() const;
    void setLastname(QString lastname);
    void setFirstname(QString firstname);

signals:
    void lastnameChanged(QString lastname);
    void firstnameChanged(QString firstname);

private:
    QString m_lastname;
    QString m_firstname;
    QTimer * m_timer;
    int m_random_number;

};

#endif // PROPERTYWRAPPER_H
cpp 复制代码
#include "propertywrapper.h"

#include <stdlib.h>
#include <time.h>

PropertyWrapper::PropertyWrapper(QObject *parent) : QObject(parent),
    m_lastname("LastName"),
    m_firstname("FirstName"),
    m_timer(new QTimer(this)),
    m_random_number(0)
{
    srand(static_cast<unsigned int>(time(nullptr)));

    QStringList list;
    list << "Steward"<<"Johanson"<<"Oliver"<<"David"<<"Snawden"<<"Kevin"<<"Mathson"<<"Gray";

    connect(m_timer, &QTimer::timeout, [=](){

        m_random_number = rand() % (list.size());
        setFirstname(list[m_random_number]);
    });

    m_timer->start(1000);
}

QString PropertyWrapper::lastname() const
{
    return m_lastname;
}

QString PropertyWrapper::firstname() const
{
    return m_firstname;
}

void PropertyWrapper::setLastname(QString lastname)
{
    if(m_lastname == lastname)
        return;

    m_lastname = lastname;
    emit lastnameChanged(m_lastname);
}

void PropertyWrapper::setFirstname(QString firstname)
{
    if(m_firstname == firstname)
        return;

    m_firstname = firstname;
    emit firstnameChanged(m_firstname);
}

然后是qml侧:这只是两个显示文本的矩形。可以看到这里设置文字的时候,直接引用的是lastname和firtsname。

cpp 复制代码
import QtQuick 2.14
import QtQuick.Window 2.14

Window {
    visible: true
    width: 640
    height: 480
    title: qsTr("QmlContextObject")

    Rectangle {
        id : mRect1
        width: mText1.implicitWidth+2
        height: mText1.implicitHeight+20
        color: "beige"
        border.color: "yellowgreen"

        Text {
            id: mText1
            anchors.centerIn: parent
            text: lastname
            font.pointSize: 20
        }
    }

    Rectangle {
        id : mRect2
        anchors.left: mRect1.right
        anchors.leftMargin:5
        width: mText2.implicitWidth+20
        height: mText2.implicitHeight+20
        color: "beige"
        border.color: "yellowgreen"

        Text {

            id:mText2
            anchors.centerIn: parent
            text: firstname
            font.pointSize:20
        }
    }
}

最后看main中的设置:

cpp 复制代码
    QQmlApplicationEngine engine;

    PropertyWrapper wrapper;
    wrapper.setLastname("Doe");
    wrapper.setFirstname("John");
    engine.rootContext()->setContextObject(&wrapper);

可以看到,这里直接将类对象设置成上下文对象(setContextObject),此后qml中就可以直接引用该对象中已经用Q_PROPERTY处理过的变量名,就是这么简单。我们可以直接用这个变量名做属性绑定,可以动态地接收到c++端该变量的更改状况,比较灵活。

二、分析优缺点

按理说,对于qml中引用C++类对象中变量这一需求,上下文属性暴露和上下文对象暴露都是可以的,只是方式有些不同。

上下文对象的优点在于,我可以直接将整个类中的数据全部暴露在qml中,无需再引用对象别名,此时qml代码会比较精简,适合于一整包数据想裸写的情况。可随之而来的就是代码阅读可能会有些困难,并且存在命名冲突的风险。更重要的是,我们无法将两个相同类的对象暴露给qml,这一点会比较麻烦。

相比之下,上下文属性暴露虽然每次使用都有引用类对象别名,但使用上也更加清晰,不止在qml端阅读代码清晰明了,也可以实现多个同类对象的暴露,也更符合c++的特性和习惯。

所以,我个人还是更加推荐用上下文属性的方式暴露c++对象。

当然,上下文对象并不意味着适合一些全局工具、参数直接丢进去就好了,

对于一些单例模式的工具类,qml还有qmlRegisterSingletonInstance注册单例这种方式。这个之后也会再学习的。

相关推荐
好奇龙猫1 小时前
【AI学习-comfyUI学习-第三十节-第三十一节-FLUX-SD放大工作流+FLUX图生图工作流-各个部分学习】
人工智能·学习
saoys2 小时前
Opencv 学习笔记:图像掩膜操作(精准提取指定区域像素)
笔记·opencv·学习
电子小白1233 小时前
第13期PCB layout工程师初级培训-1-EDA软件的通用设置
笔记·嵌入式硬件·学习·pcb·layout
恋爱绝缘体13 小时前
2020重学C++重构你的C++知识体系
java·开发语言·c++·算法·junit
唯情于酒3 小时前
Docker学习
学习·docker·容器
Z1Jxxx3 小时前
加密算法加密算法
开发语言·c++·算法
乌萨奇也要立志学C++4 小时前
【洛谷】递归初阶 三道经典递归算法题(汉诺塔 / 占卜 DIY/FBI 树)详解
数据结构·c++·算法
️停云️4 小时前
【滑动窗口与双指针】不定长滑动窗口
c++·算法·leetcode·剪枝·哈希
clorisqqq4 小时前
人工智能现代方法笔记 第1章 绪论(1/2)
人工智能·笔记
charlie1145141914 小时前
嵌入式现代C++教程: 构造函数优化:初始化列表 vs 成员赋值
开发语言·c++·笔记·学习·嵌入式·现代c++