QML 与 C++ 混合编程是 Qt 开发中的重要技术,它允许开发者结合 QML 的声明式 UI 设计能力和 C++ 的高性能计算能力,构建功能强大、性能优异的应用程序。本文将深入解析 QML 与 C++ 混合编程的核心技术和最佳实践。
一、C++ 与 QML 交互基础
1. 将 C++ 对象暴露给 QML
// person.h
#ifndef PERSON_H
#define PERSON_H
#include <QObject>
#include <QString>
class Person : public QObject
{
Q_OBJECT
Q_PROPERTY(QString name READ name WRITE setName NOTIFY nameChanged)
Q_PROPERTY(int age READ age WRITE setAge NOTIFY ageChanged)
public:
explicit Person(QObject *parent = nullptr);
QString name() const;
void setName(const QString &name);
int age() const;
void setAge(int age);
signals:
void nameChanged();
void ageChanged();
private:
QString m_name;
int m_age;
};
#endif // PERSON_H
// person.cpp
#include "person.h"
Person::Person(QObject *parent) : QObject(parent), m_age(0)
{
}
QString Person::name() const
{
return m_name;
}
void Person::setName(const QString &name)
{
if (m_name != name) {
m_name = name;
emit nameChanged();
}
}
int Person::age() const
{
return m_age;
}
void Person::setAge(int age)
{
if (m_age != age) {
m_age = age;
emit ageChanged();
}
}
2. 在 QML 中使用 C++ 对象
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include <QQmlContext>
#include "person.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// 创建 C++ 对象
Person person;
person.setName("张三");
person.setAge(30);
// 将对象暴露给 QML
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("myPerson", &person);
// 加载 QML 文件
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: mainWindow
visible: true
width: 400
height: 300
title: "QML 与 C++ 交互示例"
Column {
anchors.centerIn: parent
spacing: 20
Text {
text: "姓名: " + myPerson.name
font.pointSize: 14
}
Text {
text: "年龄: " + myPerson.age
font.pointSize: 14
}
Button {
text: "增加年龄"
onClicked: myPerson.age = myPerson.age + 1
}
}
}
二、从 C++ 加载 QML
1. 使用 QQmlComponent 动态加载
#include <QGuiApplication>
#include <QQmlComponent>
#include <QQmlEngine>
#include <QQuickWindow>
#include <QDebug>
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
QQmlEngine engine;
// 创建 QML 组件
QQmlComponent component(&engine, QUrl("qrc:/MyComponent.qml"));
// 检查是否有错误
if (component.isError()) {
qDebug() << "组件错误:" << component.errors();
return -1;
}
// 创建对象实例
QObject *object = component.create();
if (object) {
// 如果是窗口类型,显示窗口
QQuickWindow *window = qobject_cast<QQuickWindow*>(object);
if (window) {
window->show();
}
}
return app.exec();
}
2. 从 C++ 调用 QML 函数
// MyComponent.qml
import QtQuick 2.15
Item {
id: myComponent
width: 200
height: 100
function sayHello(name) {
console.log("你好, " + name + "!");
return "欢迎来到 QML";
}
}
// 从 C++ 调用 QML 函数
QObject *object = component.create();
if (object) {
// 调用 QML 函数
QVariant returnedValue;
QVariant msg = "C++";
QMetaObject::invokeMethod(object, "sayHello",
Q_RETURN_ARG(QVariant, returnedValue),
Q_ARG(QVariant, msg));
qDebug() << "函数返回值:" << returnedValue.toString();
}
三、从 QML 调用 C++ 函数
1. 使用 Q_INVOKABLE 宏
// calculator.h
#ifndef CALCULATOR_H
#define CALCULATOR_H
#include <QObject>
class Calculator : public QObject
{
Q_OBJECT
public:
explicit Calculator(QObject *parent = nullptr);
// 使用 Q_INVOKABLE 声明可从 QML 调用的函数
Q_INVOKABLE int add(int a, int b);
Q_INVOKABLE QString formatNumber(int number);
};
#endif // CALCULATOR_H
// calculator.cpp
#include "calculator.h"
#include <QString>
Calculator::Calculator(QObject *parent) : QObject(parent)
{
}
int Calculator::add(int a, int b)
{
return a + b;
}
QString Calculator::formatNumber(int number)
{
return QString::number(number);
}
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
Window {
id: mainWindow
visible: true
width: 400
height: 200
title: "调用 C++ 函数示例"
// 访问 C++ 对象
property var calculator: Calculator {}
Column {
anchors.centerIn: parent
spacing: 20
Text {
text: "1 + 2 = " + calculator.add(1, 2)
font.pointSize: 14
}
Text {
text: "格式化数字: " + calculator.formatNumber(1234567)
font.pointSize: 14
}
}
}
2. 信号与槽机制
// notifier.h
#ifndef NOTIFIER_H
#define NOTIFIER_H
#include <QObject>
class Notifier : public QObject
{
Q_OBJECT
public:
explicit Notifier(QObject *parent = nullptr);
signals:
// 定义信号
void messageSent(const QString &message);
public slots:
// 定义槽函数
void sendNotification(const QString &title, const QString &content);
};
#endif // NOTIFIER_H
// notifier.cpp
#include "notifier.h"
#include <QDebug>
Notifier::Notifier(QObject *parent) : QObject(parent)
{
}
void Notifier::sendNotification(const QString &title, const QString &content)
{
qDebug() << "发送通知:" << title << content;
emit messageSent("[" + title + "] " + content);
}
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
Window {
id: mainWindow
visible: true
width: 400
height: 250
title: "信号与槽示例"
// 创建 C++ 对象实例
property var notifier: Notifier {}
Column {
anchors.centerIn: parent
spacing: 20
width: parent.width * 0.8
TextField {
id: titleField
placeholderText: "通知标题"
}
TextField {
id: contentField
placeholderText: "通知内容"
}
Button {
text: "发送通知"
onClicked: notifier.sendNotification(titleField.text, contentField.text)
}
Text {
id: messageText
text: "等待通知..."
color: "blue"
font.pointSize: 14
}
}
// 连接信号和槽
Connections {
target: notifier
onMessageSent: {
messageText.text = message
}
}
}
四、QML 与 C++ 数据模型交互
1. C++ 实现数据模型
// personmodel.h
#ifndef PERSONMODEL_H
#define PERSONMODEL_H
#include <QAbstractListModel>
#include "person.h"
class PersonModel : public QAbstractListModel
{
Q_OBJECT
public:
enum PersonRoles {
NameRole = Qt::UserRole + 1,
AgeRole
};
explicit PersonModel(QObject *parent = nullptr);
// 实现抽象方法
int rowCount(const QModelIndex &parent = QModelIndex()) const override;
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override;
QHash<int, QByteArray> roleNames() const override;
// 添加人员
Q_INVOKABLE void addPerson(const QString &name, int age);
private:
QList<Person*> m_persons;
};
#endif // PERSONMODEL_H
// personmodel.cpp
#include "personmodel.h"
PersonModel::PersonModel(QObject *parent) : QAbstractListModel(parent)
{
}
int PersonModel::rowCount(const QModelIndex &parent) const
{
Q_UNUSED(parent);
return m_persons.size();
}
QVariant PersonModel::data(const QModelIndex &index, int role) const
{
if (index.row() < 0 || index.row() >= m_persons.size())
return QVariant();
Person *person = m_persons.at(index.row());
if (role == NameRole)
return person->name();
else if (role == AgeRole)
return person->age();
return QVariant();
}
QHash<int, QByteArray> PersonModel::roleNames() const
{
QHash<int, QByteArray> roles;
roles[NameRole] = "name";
roles[AgeRole] = "age";
return roles;
}
void PersonModel::addPerson(const QString &name, int age)
{
beginInsertRows(QModelIndex(), rowCount(), rowCount());
Person *person = new Person(this);
person->setName(name);
person->setAge(age);
m_persons.append(person);
endInsertRows();
}
2. 在 QML 中使用 C++ 数据模型
// main.cpp
#include <QGuiApplication>
#include <QQmlApplicationEngine>
#include "personmodel.h"
int main(int argc, char *argv[])
{
QGuiApplication app(argc, argv);
// 创建数据模型
PersonModel personModel;
personModel.addPerson("张三", 30);
personModel.addPerson("李四", 25);
personModel.addPerson("王五", 40);
// 注册类型
qmlRegisterType<Person>("com.example", 1, 0, "Person");
// 将模型暴露给 QML
QQmlApplicationEngine engine;
engine.rootContext()->setContextProperty("personModel", &personModel);
// 加载 QML
engine.load(QUrl(QStringLiteral("qrc:/main.qml")));
return app.exec();
}
// main.qml
import QtQuick 2.15
import QtQuick.Window 2.15
import QtQuick.Controls 2.15
import com.example 1.0
Window {
id: mainWindow
visible: true
width: 400
height: 300
title: "C++ 数据模型示例"
Column {
anchors.fill: parent
spacing: 10
padding: 10
// 显示数据模型
ListView {
id: personList
anchors {
top: parent.top
left: parent.left
right: parent.right
bottom: addButton.top
bottomMargin: 10
}
model: personModel
delegate: Item {
width: parent.width
height: 40
Rectangle {
anchors.fill: parent
color: index % 2 === 0 ? "lightgray" : "white"
Text {
text: "姓名: " + name + ", 年龄: " + age
anchors.verticalCenter: parent.verticalCenter
anchors.left: parent.left
anchors.leftMargin: 10
}
}
}
}
// 添加新人员的控件
Row {
id: addButton
anchors {
left: parent.left
right: parent.right
bottom: parent.bottom
}
spacing: 10
TextField {
id: nameField
placeholderText: "姓名"
width: 120
}
TextField {
id: ageField
placeholderText: "年龄"
width: 60
validator: IntValidator { bottom: 0; top: 150 }
}
Button {
text: "添加"
onClicked: {
personModel.addPerson(nameField.text, parseInt(ageField.text))
nameField.text = ""
ageField.text = ""
}
}
}
}
}
五、总结
QML 与 C++ 混合编程提供了强大的开发能力:
- 对象暴露:通过
QQmlContext::setContextProperty()将 C++ 对象暴露给 QML。 - 类型注册:使用
qmlRegisterType()注册 C++ 类,使其可以在 QML 中实例化。 - 属性系统:通过
Q_PROPERTY宏实现 C++ 对象属性在 QML 中的双向绑定。 - 函数调用:使用
Q_INVOKABLE宏或信号槽机制实现 QML 与 C++ 的互相调用。 - 数据模型:通过继承
QAbstractListModel实现 C++ 数据模型,并在 QML 中使用。
这种混合编程模式让开发者能够充分发挥 QML 的 UI 设计优势和 C++ 的性能优势,同时保持代码的模块化和可维护性,是开发复杂 Qt 应用的理想选择。
1万+

被折叠的 条评论
为什么被折叠?



