c++和qml交互

C++和qml交互

一个简单demo

DeviceModel.h

c++ 复制代码
#ifndef DEVICEMODEL_H
#define DEVICEMODEL_H

#include <QObject>

#include <QtQml>

// 具体设备类
class DeviceModel : public QObject
{
    Q_OBJECT

    Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged FINAL)
    Q_PROPERTY(bool isOn READ isOn WRITE setIsOn NOTIFY isOnChanged FINAL)

    QML_ELEMENT  // 可以直接在qml实例化
public:
    explicit DeviceModel(QObject *parent = nullptr);



    bool isOn() const;
    void setIsOn(bool newIsOn);

    // Invokable 方法:供 QML 调用执行复杂逻辑
    Q_INVOKABLE void toggle();

    QString name() const;
    void setName(const QString &newName);

signals:

    void isOnChanged();

    void nameChanged();
    void alarmTriggered(QString message); // 信号:用于信号槽演示
private:

    bool m_isOn;
    QString m_name;
};

#endif // DEVICEMODEL_H

DeviceModel.cpp

c++ 复制代码
#include "devicemodel.h"
#include <QDebug>
DeviceModel::DeviceModel(QObject *parent)
    : QObject{parent}
{}




bool DeviceModel::isOn() const
{
    return m_isOn;
}

void DeviceModel::setIsOn(bool newIsOn)
{
    if (m_isOn == newIsOn)
        return;
    m_isOn = newIsOn;
    emit isOnChanged();
    emit alarmTriggered("alarmTriggered..");
}

void DeviceModel::toggle()
{
    setIsOn(!m_isOn);
    qDebug()<<"toggle..";
}

QString DeviceModel::name() const
{
    return m_name;
}

void DeviceModel::setName(const QString &newName)
{
    if (m_name == newName)
        return;
    m_name = newName;
    emit nameChanged();
}

RuntimeInfo.h

c++ 复制代码
#ifndef RUNTIMEINFO_H
#define RUNTIMEINFO_H

#include <QObject>

class RuntimeInfo : public QObject
{
    Q_OBJECT
    Q_PROPERTY(int uptime READ uptime WRITE setUptime NOTIFY uptimeChanged FINAL)
public:
    explicit RuntimeInfo(QObject *parent = nullptr);

    int uptime() const;
    void setUptime(int newUptime);

protected:
    void timerEvent(QTimerEvent *) override;

signals:
    void uptimeChanged();
private:
    int m_uptime;
};

#endif // RUNTIMEINFO_H

RuntimeInfo.cpp

c++ 复制代码
#include "runtimeinfo.h"

RuntimeInfo::RuntimeInfo(QObject *parent)
    : QObject{parent}
    ,m_uptime(0)
{}

int RuntimeInfo::uptime() const
{
    return m_uptime;
}

void RuntimeInfo::setUptime(int newUptime)
{
    if (m_uptime == newUptime)
        return;
    m_uptime = newUptime;
    emit uptimeChanged();
}

void RuntimeInfo::timerEvent(QTimerEvent *)
{
    m_uptime++;
    emit uptimeChanged();
}

SystemSettings.h

c++ 复制代码
#ifndef SYSTEMSETTINGS_H
#define SYSTEMSETTINGS_H

#include <QObject>
#include <QtQml>

class SystemSettings : public QObject
{
    Q_OBJECT
    Q_PROPERTY(QString theme READ theme WRITE setTheme NOTIFY themeChanged)
    QML_ELEMENT
    QML_SINGLETON // 标记为单例
public:
    explicit SystemSettings(QObject *parent = nullptr);

    QString theme() const;
    void setTheme(const QString &newTheme);

    // QML_SINGLETON 必需的方法
    static SystemSettings *create(QQmlEngine *engine, QJSEngine *scriptEngine);
    static SystemSettings *instance();

signals:
    void themeChanged();
private:
    QString m_theme;
};

#endif // SYSTEMSETTINGS_H

SystemSettings.cpp

c++ 复制代码
#include "systemsettings.h"

SystemSettings::SystemSettings(QObject *parent)
    : QObject{parent}
    ,m_theme("Dark")
{}

QString SystemSettings::theme() const
{
    return m_theme;
}

void SystemSettings::setTheme(const QString &newTheme)
{
    if (m_theme == newTheme)
        return;
    m_theme = newTheme;
    emit themeChanged();
}

// QML_SINGLETON 必需的方法
SystemSettings *SystemSettings::create(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)
    return instance();
}

SystemSettings *SystemSettings::instance()
{
    static SystemSettings instance;
    return &instance;
}

main.cpp

c++ 复制代码
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "runtimeinfo.h"
#include "systemsettings.h"
#include "devicemodel.h"
#include "test.h"

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

    QQmlApplicationEngine engine;

    // 1. 上下文属性 (Context Property)
    // 适用场景:简单的全局常量或单向传递的指针
    engine.rootContext()->setContextProperty("APP_Version","1.0.1");

    // 2. 上下文对象 (Context Object)
    // 适用场景:将整个对象的属性直接暴露到根作用域,无需对象名前缀
    RuntimeInfo *runInfo = new RuntimeInfo(&app);
    engine.rootContext()->setContextObject(runInfo);

    // 3. 注册单例与可实例化类型 (现代方式:QML_ELEMENT + qmlRegister...)
    // 这里也可以使用 CMake 的 qt_add_qml_module 来自动处理
    qmlRegisterSingletonType<SystemSettings>(
        "MyApp.Core",      // 模块名
        1, 0,             // 版本号
        "SystemSettings",  // QML 中的类型名
        &SystemSettings::create  // 创建函数
        );

    qmlRegisterType<DeviceModel>("SmartHome.Core", 1, 0, "DeviceModel");

    const QUrl url(u"qrc:/cppWithQml/Main.qml"_qs);
    QObject::connect(
        &engine,
        &QQmlApplicationEngine::objectCreationFailed,
        &app,
        []() { QCoreApplication::exit(-1); },
        Qt::QueuedConnection);
    engine.load(url);

    return app.exec();
}

main.qml

json 复制代码
import QtQuick
import QtQuick.Controls
import MyApp.Core 1.0
import SmartHome.Core 1.0

Window {
    width: 640
    height: 480
    visible: true
    title: qsTr("Hello World")
    Column
    {
        spacing: 20
        // 展示 1: 上下文属性 (Context Property)
        Text{
            text: "Version: " + APP_Version
        }

        // 展示 2: 上下文对象 (Context Object) - 直接访问 uptime
        Text{
            text: uptime
        }

        // 展示 3: 单例 (Singleton) & Q_PROPERTY
        Button{
            text: "当前主题: " + SystemSettings.theme
            onClicked:{
                SystemSettings.theme = (SystemSettings.theme === "Dark" ? "Light" : "Dark")
            }
        }

        // 展示 4: 可实例化类型 (Instantiable Type)
        DeviceModel{
            id: livingRoomLight
            name: "Living Room Light"

            // 展示 5: 信号与槽 (Signals & Slots)
            onAlarmTriggered: (msg) => {
                 console.log("ALARM: " + msg)
             }
        }

        Rectangle {
            width: 200; height: 100
            color: livingRoomLight.isOn ? "yellow" : "gray"
            border.color: "black"

            Text {
                anchors.centerIn: parent
                text: livingRoomLight.name
            }

            MouseArea {
                anchors.fill: parent
                // 展示 6: Invokable 方法
                onClicked: livingRoomLight.toggle()
            }
        }
    }
}

demo中有六种方法:

1、上下文属性

适用场景:简单的全局常量或单向传递的指针

复制代码
engine.rootContext()->setContextProperty("APP_Version","1.0.1");

或者:有个Test类,现创建一个实例类然后声明
Test myTest;
engine.rootContext()->setContextProperty("myTest", &myTest);

2、上下文对象

适用场景:将整个对象的属性直接暴露到根作用域,无需对象名前缀

复制代码
RuntimeInfo *runInfo = new RuntimeInfo(&app);
engine.rootContext()->setContextObject(runInfo);

3、可实例化类型

现代方式 (Qt 6.x)

复制代码
QML_NAMED_ELEMENT(Name)或者QML_ELEMENT  
// 可以直接在qml实例化,加到类中,注意需要添加头文件,并在 CMake 中配置 qt_add_qml_module

传统方式 (Qt 5.x)

复制代码
qmlRegisterType<DeviceModel>("SmartHome.Core", 1, 0, "DeviceModel");

4、Q_INVOKABLE / Slots

复制代码
// Invokable 方法:供 QML 调用执行复杂逻辑
Q_INVOKABLE void toggle();

void alarmTriggered(QString message); // 信号:用于信号槽演示

onAlarmTriggered: (msg) => {
                 console.log("ALARM: " + msg)
             }
             
在需要的位置emit alarmTriggered("alarmTriggered..");

5、单例

复制代码
 QML_SINGLETON // 标记为单例

// QML_SINGLETON 必需的方法
    static SystemSettings *create(QQmlEngine *engine, QJSEngine *scriptEngine);
    static SystemSettings *instance();

// QML_SINGLETON 必需的方法
SystemSettings *SystemSettings::create(QQmlEngine *engine, QJSEngine *scriptEngine)
{
    Q_UNUSED(engine)
    Q_UNUSED(scriptEngine)
    return instance();
}

SystemSettings *SystemSettings::instance()
{
    static SystemSettings instance;
    return &instance;
}

main.cpp

复制代码
    qmlRegisterSingletonType<SystemSettings>(
        "MyApp.Core",      // 模块名
        1, 0,             // 版本号
        "SystemSettings",  // QML 中的类型名
        &SystemSettings::create  // 创建函数
        );

6、Q_PROPERTY

复制代码
Q_PROPERTY(
    type name                     // 属性类型和名称
    READ getFunction              // 读取函数(必须)
    [WRITE setFunction]           // 写入函数(可选)
    [NOTIFY signal]               // 变更通知信号(推荐)
    [RESET resetFunction]         // 重置函数(可选)
    [DESIGNABLE bool]            // 设计器可见性(默认true)
    [SCRIPTABLE bool]            // 脚本可访问性(默认true)
    [STORED bool]                // 是否持久化(默认true)
    [USER bool]                  // 是否为用户可编辑属性(默认false)
    [CONSTANT]                   // 常量属性,不发送NOTIFY
    [FINAL]                      // 不可被子类覆盖
)

然后在main.cpp暴露给qml

复制代码
例如 :
Test myTest;
engine.rootContext()->setContextProperty("myTest", &myTest);

7、枚举注册

  • 在 C++ 中定义的 enum 必须使用 Q_ENUM 宏,并且该类需要注册到 QML 才能在 QML 中使用其枚举值。
    • 技巧 :在 Qt 6 中,可以使用 QML_UNCREATABLE 注册一个只包含枚举、不提供实例化的类。

8、附加属性

这是 QML 的高级特性。例如 Keys.onPressedListView.isCurrentItem

  • 原理 :允许一个对象通过另一个"附加类"获取额外的属性或信号,而无需继承该类。C++ 中通过 qmlAttachedPropertiesObject() 实现。

9、数据模型

对于复杂列表数据,不要传递 QList

  • 推荐方案 :继承 QAbstractListModel。这允许 QML 的 ListViewGridView 高效地进行局部刷新,而不是全量重新加载。

总结

  1. 简单数据传递 → 使用上下文属性
  2. 工具类/辅助对象 → 使用上下文对象
  3. UI组件/业务对象 → 使用可实例化类型
  4. 全局管理器/配置 → 使用单例模式
  5. 复杂业务逻辑 → 使用Q_INVOKABLE方法
  6. 事件通知/异步 → 使用信号槽机制
相关推荐
2301_765703148 小时前
C++代码复杂度控制
开发语言·c++·算法
m0_708830968 小时前
C++中的享元模式实战
开发语言·c++·算法
naruto_lnq8 小时前
分布式计算C++库
开发语言·c++·算法
m0_706653239 小时前
模板编译期排序算法
开发语言·c++·算法
历程里程碑9 小时前
Linxu14 进程一
linux·c语言·开发语言·数据结构·c++·笔记·算法
m0_561359679 小时前
嵌入式C++加密库
开发语言·c++·算法
近津薪荼9 小时前
优选算法——双指针专题7(单调性)
c++·学习·算法
JiL 奥9 小时前
Nexus制品归档(c/c++项目)
c语言·c++
j445566119 小时前
C++中的职责链模式实战
开发语言·c++·算法
m0_686041619 小时前
实时数据流处理
开发语言·c++·算法