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注册一个只包含枚举、不提供实例化的类。
- 技巧 :在 Qt 6 中,可以使用
8、附加属性
这是 QML 的高级特性。例如 Keys.onPressed 或 ListView.isCurrentItem。
- 原理 :允许一个对象通过另一个"附加类"获取额外的属性或信号,而无需继承该类。C++ 中通过
qmlAttachedPropertiesObject()实现。
9、数据模型
对于复杂列表数据,不要传递 QList。
- 推荐方案 :继承
QAbstractListModel。这允许 QML 的ListView或GridView高效地进行局部刷新,而不是全量重新加载。
总结
- 简单数据传递 → 使用上下文属性
- 工具类/辅助对象 → 使用上下文对象
- UI组件/业务对象 → 使用可实例化类型
- 全局管理器/配置 → 使用单例模式
- 复杂业务逻辑 → 使用Q_INVOKABLE方法
- 事件通知/异步 → 使用信号槽机制