跳转到内容

第 8 课:C++ 与 QML 集成入门 ​

更新: 10/23/2026 字数: 0 字 时长: 0 分钟

这一课学三件事:

  1. 写一个 C++ 类,暴露属性、方法和信号
  2. 把它注册成 QML 类型
  3. 在 QML 里使用它

Qt 6.9 推荐用 QML_ELEMENT 宏 + CMake 的 qt_add_qml_module,我们按这个方式来。

C++ 与 QML 集成:注册 Backend 类型并在界面中使用 ​

第一步:创建 C++ 类 ​

在项目里新建一个 C++ 类,叫 Backend。

backend.h:

cpp
#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;
};

#endif

backend.cpp:

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,确保有类似这样的内容:

cmake
qt_add_qml_module(appMyApp
    URI MyApp
    VERSION 1.0
    QML_FILES
        Main.qml
    SOURCES
        backend.h
        backend.cpp
)

关键点:

  • SOURCES 里加上 backend.h 和 backend.cpp
  • backend.h 里的 QML_ELEMENT 宏会让这个类自动注册到 MyApp 模块下
  • URI MyApp 是模块名,QML 里 import MyApp 就能用

如果你的项目是 Qt Creator 自动生成的,通常已经有 qt_add_qml_module,只需要把 backend.h 和 backend.cpp 加到 SOURCES 里。


第三步:在 QML 里使用 ​

Main.qml:

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 ​

cpp
QML_ELEMENT

这个宏把 Backend 类注册成 QML 类型,类型名就是类名 Backend。

配合 CMake 的 qt_add_qml_module,QML 里 import MyApp 后就能直接用。

2. Q_PROPERTY ​

cpp
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 ​

cpp
Q_INVOKABLE void increment();

让 QML 可以直接调用这个方法。

普通 C++ 方法不加 Q_INVOKABLE 或不是 slot,QML 调不到。

4. 信号 ​

cpp
signals:
    void countChanged();

QML 里可以这样监听:

qml
Backend {
    id: backend
    onCountChanged: console.log("计数变成: " + count)
}

信号名首字母大写,QML 里加 on 前缀。

5. 属性绑定自动更新 ​

qml
text: "当前计数: " + backend.count

因为 count 有 NOTIFY countChanged,C++ 里 m_count 一变,QML 这个绑定会自动重新计算。

另一种方式:setContextProperty(了解即可) ​

老项目里常见:

cpp
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:

cpp
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)

QML 里用一个 TextField 绑定它:

qml
TextField {
    text: backend.name
    onTextChanged: backend.name = text
}

观察 C++ 和 QML 双向同步。

练习 3 ​

在 C++ 里加一个信号:

cpp
signals:
    void limitReached();

当 count 达到 10 时发出。QML 里监听:

qml
Backend {
    onLimitReached: console.log("到 10 了")
}

练习 4(思考题) ​

如果 Q_PROPERTY 里不写 NOTIFY countChanged,QML 里 text: "计数: " + backend.count 还会自动更新吗?为什么?