📚 课程目标
学完本课,你将掌握这些核心技能:
🏗️ 交互架构概览
C++与QML的交互是Qt Quick应用的核心基础,核心思路十分清晰:C++作为后端,负责提供数据和业务逻辑支撑;QML作为前端,专注于用户界面和交互效果呈现。
连接两者的核心枢纽是QQmlApplicationEngine,它就像一座桥梁,所有的C++类注册、对象属性设置都要通过它来完成,是C++世界与QML世界的交互通道。
🔧 方法一:注册C++类(Register C++ Types)
这种方法是把C++类注册为QML可直接使用的类型,之后你就能在QML文件里像使用内置类型一样,先导入再实例化它。
具体步骤分四步:
QObject,用Q_PROPERTY暴露可被QML读写的属性,用Q_INVOKABLE或slots关键字暴露可被QML调用的方法;Q_DECLARE_METATYPE;qmlRegisterType或qmlRegisterSingletonType函数完成注册;import语句导入注册时指定的模块名和版本,即可直接声明该类型的对象。C++端:DataProcessor类定义与注册
// dataprocessor.h
#include <QObject>
#include <QString>
class DataProcessor : public QObject
{
Q_OBJECT
// 注册可读写属性到QML,支持信号通知更新
Q_PROPERTY(QString processedData READ processedData WRITE setProcessedData NOTIFY processedDataChanged)
public:
explicit DataProcessor(QObject *parent = nullptr);
QString processedData() const;
void setProcessedData(const QString &data);
// 注册可供QML调用的方法
Q_INVOKABLE QString process(const QString &input);
signals:
void processedDataChanged();
private:
QString m_processedData;
};
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "dataprocessor.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// 注册DataProcessor到QML:模块名"Custom",版本1.0,QML类型名"DataProcessor"
qmlRegisterType<DataProcessor>("Custom", 1, 0, "DataProcessor");
QQmlApplicationEngine engine;
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
QML端:使用注册的类
import QtQuick 2.15
import QtQuick.Window 2.15
// 导入注册的C++模块
import Custom 1.0
Window {
width: 400; height: 300
visible: true
title: qsTr("C++与QML交互示例")
// 实例化C++类型,如同使用原生QML类型
DataProcessor {
id: myProcessor
onProcessedDataChanged: {
console.log("处理后的数据已更新:", processedData);
}
}
Column {
anchors.centerIn: parent
spacing: 10
TextInput {
id: inputField
width: 200
text: "Hello QML"
}
Button {
text: "调用C++方法处理"
onClicked: {
// 直接调用C++对象的Q_INVOKABLE方法
var result = myProcessor.process(inputField.text);
myProcessor.processedData = result;
outputText.text = "结果: " + result;
}
}
Text { id: outputText; }
}
}
🎯 方法二:设置上下文属性(Context Property)
这种方法是把已创建好的C++对象实例,直接设置为QML根上下文的属性,让整个QML组件树都能全局访问该对象。
⚠️ 注意:过度使用会让QML对C++对象产生隐式依赖,降低代码可测试性,需谨慎使用。
C++端:设置全局应用控制器
// appcontroller.h(单例模式的全局控制器)
#include <QObject>
#include <QString>
class AppController : public QObject
{
Q_OBJECT
Q_PROPERTY(QString userName READ userName NOTIFY userNameChanged)
public:
static AppController* instance(); // 单例实例获取
QString userName() const;
Q_INVOKABLE void login(const QString &name);
signals:
void userNameChanged();
private:
AppController(QObject *parent = nullptr);
QString m_userName;
};
// main.cpp
#include "appcontroller.h"
// ... 其他头文件
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// 获取单例实例
AppController *appCtrl = AppController::instance();
QQmlApplicationEngine engine;
// 关键:将C++对象设为根上下文属性,QML中用"appController"访问
engine.rootContext()->setContextProperty("appController", appCtrl);
// 可设置多个上下文属性,如全局配置、工具类等
// engine.rootContext()->setContextProperty("settings", mySettings);
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
QML端:访问上下文属性
import QtQuick 2.15
import QtQuick.Controls 2.15
ApplicationWindow {
visible: true
title: "上下文属性示例"
header: ToolBar {
Label {
// 绑定C++对象属性,属性更新时自动刷新
text: "当前用户: " + (appController.userName || "未登录")
}
}
Column {
anchors.centerIn: parent
TextField { id: nameField; placeholderText: "输入用户名" }
Button {
text: "登录"
onClicked: {
// 直接调用C++对象的方法
appController.login(nameField.text);
}
}
}
}
📊 两种方法对比与选择指南
⚠️ 注意事项与常见陷阱
QThread和异步信号槽;onSignalName处理器,实现双向通信;