第 8 课:C++ 与 QML 集成入门
更新: 10/23/2026 字数: 0 字 时长: 0 分钟
这一课学三件事:
- 写一个 C++ 类,暴露属性、方法和信号
- 把它注册成 QML 类型
- 在 QML 里使用它
Qt 6.9 推荐用 QML_ELEMENT 宏 + CMake 的 qt_add_qml_module,我们按这个方式来。
C++ 与 QML 集成:注册 Backend 类型并在界面中使用
第一步:创建 C++ 类
在项目里新建一个 C++ 类,叫 Backend。
backend.h:
#ifndef BACKEND_H
#define BACKEND_H
#include <QObject>
#include <QML_ELEMENT>
class Backend : public QObject
{
Q_OBJECT
QML_ELEMENT
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)
public:
explicit Backend(QObject *parent = nullptr);
int count() const;
void setCount(int value);
Q_INVOKABLE void increment();
signals:
void countChanged();
private:
int m_count = 0;
};
#endifbackend.cpp:
#include "backend.h"
Backend::Backend(QObject *parent)
: QObject(parent)
{
}
int Backend::count() const
{
return m_count;
}
void Backend::setCount(int value)
{
if (m_count == value)
return;
m_count = value;
emit countChanged();
}
void Backend::increment()
{
setCount(m_count + 1);
}第二步:修改 CMakeLists.txt
Qt 6 的 QML 模块需要在 CMakeLists.txt 里用 qt_add_qml_module 注册。
找到你的 CMakeLists.txt,确保有类似这样的内容:
qt_add_qml_module(appMyApp
URI MyApp
VERSION 1.0
QML_FILES
Main.qml
SOURCES
backend.h
backend.cpp
)关键点:
SOURCES里加上backend.h和backend.cppbackend.h里的QML_ELEMENT宏会让这个类自动注册到MyApp模块下URI MyApp是模块名,QML 里import MyApp就能用
如果你的项目是 Qt Creator 自动生成的,通常已经有 qt_add_qml_module,只需要把 backend.h 和 backend.cpp 加到 SOURCES 里。
第三步:在 QML 里使用
Main.qml:
import QtQuick
import QtQuick.Controls
import MyApp
ApplicationWindow {
width: 360
height: 240
visible: true
title: "第八课"
Backend {
id: backend
}
Column {
anchors.centerIn: parent
spacing: 12
Label {
text: "当前计数: " + backend.count
font.pixelSize: 24
}
Button {
text: "加一"
onClicked: backend.increment()
}
}
}运行后:
- 标签显示
当前计数: 0 - 点击按钮,
backend.increment()被调用 - C++ 里
m_count加一,发出countChanged - QML 里
backend.count的属性绑定自动更新,标签文字变化
关键点讲解
1. QML_ELEMENT
QML_ELEMENT这个宏把 Backend 类注册成 QML 类型,类型名就是类名 Backend。
配合 CMake 的 qt_add_qml_module,QML 里 import MyApp 后就能直接用。
2. Q_PROPERTY
Q_PROPERTY(int count READ count WRITE setCount NOTIFY countChanged)把 C++ 属性暴露给 QML。
READ count:读属性调用count()WRITE setCount:写属性调用setCount()NOTIFY countChanged:值变化时发这个信号,QML 的绑定才会更新
没有 NOTIFY 信号,QML 里的绑定不会自动刷新,这是初学者最容易踩的坑。
3. Q_INVOKABLE
Q_INVOKABLE void increment();让 QML 可以直接调用这个方法。
普通 C++ 方法不加 Q_INVOKABLE 或不是 slot,QML 调不到。
4. 信号
signals:
void countChanged();QML 里可以这样监听:
Backend {
id: backend
onCountChanged: console.log("计数变成: " + count)
}信号名首字母大写,QML 里加 on 前缀。
5. 属性绑定自动更新
text: "当前计数: " + backend.count因为 count 有 NOTIFY countChanged,C++ 里 m_count 一变,QML 这个绑定会自动重新计算。
另一种方式:setContextProperty(了解即可)
老项目里常见:
Backend backend;
engine.rootContext()->setContextProperty("backend", &backend);然后 QML 里直接用 backend.count,不需要 import。
这种方式简单,但类型不安全、不利于模块化,Qt 6 推荐用 QML_ELEMENT。知道有这回事即可,新项目别用。
你的动手练习
练习 1
给 Backend 加一个 reset() 方法,把计数归零。
要求:
- C++ 里加
Q_INVOKABLE void reset(); - QML 里加一个“归零”按钮调用它
练习 2
给 Backend 加一个字符串属性 name:
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)QML 里用一个 TextField 绑定它:
TextField {
text: backend.name
onTextChanged: backend.name = text
}观察 C++ 和 QML 双向同步。
练习 3
在 C++ 里加一个信号:
signals:
void limitReached();当 count 达到 10 时发出。QML 里监听:
Backend {
onLimitReached: console.log("到 10 了")
}练习 4(思考题)
如果 Q_PROPERTY 里不写 NOTIFY countChanged,QML 里 text: "计数: " + backend.count 还会自动更新吗?为什么?