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. 事件通知/异步 → 使用信号槽机制
相关推荐
人机与认知实验室9 小时前
2026年,“交互”会出现新的定义
交互
cn_mengbei9 小时前
鸿蒙PC原生应用开发避坑指南:Qt 6.6与Electron 28兼容性问题全解析
qt·electron·harmonyos
cn_mengbei9 小时前
鸿蒙PC上Qt原生应用开发:从零搭建开发环境到部署实战,附HarmonyOS SDK配置与避坑指南(C++实现)
c++·qt·harmonyos
cn_mengbei9 小时前
鸿蒙PC跨端开发实战:从Qt环境配置到Electron应用鸿蒙化的完整指南
qt·electron·harmonyos
脏脏a9 小时前
手撕 vector:从 0 到 1 模拟实现 STL 容器
开发语言·c++·vector
郝学胜-神的一滴9 小时前
Linux 读写锁深度解析:原理、应用与性能优化
linux·服务器·c++·程序人生·性能优化
六点的晨曦10 小时前
Qt常用的开发架构模式与UI组件
qt·ui·架构
闻缺陷则喜何志丹10 小时前
【图论 DFS 换根法】3772. 子图的最大得分|2235
c++·算法·深度优先·力扣·图论·换根法
开开心心就好10 小时前
音频格式互转工具,支持Mp3ApeWavFlac互转
java·网络·c++·windows·qt·电脑·excel