如何在Qt C++与QML开发中遵循MVC架构(附仪表盘代码)
在Qt C++与QML开发中实现MVC架构(针对仪表盘组件)
我来帮你梳理如何把MVC架构应用到你的Qt项目中,结合你提供的仪表盘组件,我们把代码拆分成清晰的三层:Model(数据/业务逻辑)、View(UI展示)、Controller(中间协调层)。
核心思路
MVC的核心是职责分离:
- Model:只管数据和业务规则,不关心UI;
- View:只管展示和用户交互,不处理业务逻辑;
- Controller:作为桥梁,接收View的交互请求,调用Model更新数据,同时让View能绑定Model的状态。
第一步:实现Model层(数据与业务逻辑)
创建GaugeModel类,负责存储仪表盘数值,处理数值范围校验这类业务逻辑,数值变化时发出信号通知UI更新。
// gaugemodel.h #ifndef GAUGEMODEL_H #define GAUGEMODEL_H #include <QObject> class GaugeModel : public QObject { Q_OBJECT // 暴露给QML的属性,支持绑定 Q_PROPERTY(double gaugeValue READ gaugeValue WRITE setGaugeValue NOTIFY gaugeValueChanged) private: double m_gaugeValue = 0.0; const double MIN_VALUE = 0.0; // 仪表盘最小角度 const double MAX_VALUE = 180.0; // 仪表盘最大角度(可根据你的需求调整) public: explicit GaugeModel(QObject *parent = nullptr); double gaugeValue() const; void setGaugeValue(double newValue); signals: void gaugeValueChanged(); }; #endif // GAUGEMODEL_H
// gaugemodel.cpp #include "gaugemodel.h" #include <QtMath> GaugeModel::GaugeModel(QObject *parent) : QObject(parent) {} double GaugeModel::gaugeValue() const { return m_gaugeValue; } void GaugeModel::setGaugeValue(double newValue) { // 业务逻辑:把数值限制在有效范围内 double clampedValue = qBound(MIN_VALUE, newValue, MAX_VALUE); // 避免重复触发信号 if (qFuzzyCompare(m_gaugeValue, clampedValue)) return; m_gaugeValue = clampedValue; emit gaugeValueChanged(); }
第二步:实现Controller层(中间协调)
创建GaugeController类,作为View和Model的中间层,处理用户交互请求(比如按钮点击),调用Model的方法更新数据,同时让View能访问Model的状态。
// gaugecontroller.h #ifndef GAUGECONTROLLER_H #define GAUGECONTROLLER_H #include <QObject> #include "gaugemodel.h" class GaugeController : public QObject { Q_OBJECT private: GaugeModel* m_model; public: explicit GaugeController(GaugeModel* model, QObject *parent = nullptr); // 供QML调用的方法,处理用户交互 Q_INVOKABLE void increaseGaugeValue(double step); Q_INVOKABLE void decreaseGaugeValue(double step); Q_INVOKABLE void setGaugeValue(double value); // 暴露Model给QML绑定 Q_PROPERTY(GaugeModel* model READ model CONSTANT) GaugeModel* model() const; }; #endif // GAUGECONTROLLER_H
// gaugecontroller.cpp #include "gaugecontroller.h" GaugeController::GaugeController(GaugeModel* model, QObject *parent) : QObject(parent), m_model(model) { Q_ASSERT(model != nullptr); // 确保Model不为空 } void GaugeController::increaseGaugeValue(double step) { m_model->setGaugeValue(m_model->gaugeValue() + step); } void GaugeController::decreaseGaugeValue(double step) { m_model->setGaugeValue(m_model->gaugeValue() - step); } void GaugeController::setGaugeValue(double value) { m_model->setGaugeValue(value); } GaugeModel* GaugeController::model() const { return m_model; }
第三步:改造View层(QML组件)
修改你的Gauge.qml,让它不再自己维护数值,而是绑定Model的属性;同时在main.qml中添加交互控件,调用Controller的方法。
改造后的Gauge.qml
// Gauge.qml Item { width: 100 height: 100 // 接收外部传入的Model,用于绑定数值 property GaugeModel gaugeModel Rectangle { anchors.fill: parent Image { anchors.fill: parent source: "qrc:/gaugebg.png" } Image { x: 50 y: 49 source: "qrc:/needle.png" transform: Rotation { origin.x: 10; origin.y: 10; // 直接绑定Model的数值,自动更新旋转角度 angle: gaugeModel ? gaugeModel.gaugeValue : 0 } } } }
改造后的main.qml
// main.qml Window { id: mainWindow width: 400 height: 400 visible: true title: qsTr("MVC Gauge Demo") Column { anchors.centerIn: parent spacing: 20 // 仪表盘组件,绑定Controller的Model Gauge { gaugeModel: gaugeController.model } // 测试按钮:增加数值 Button { text: qsTr("Increase by 10") onClicked: gaugeController.increaseGaugeValue(10) } // 测试按钮:减少数值 Button { text: qsTr("Decrease by 10") onClicked: gaugeController.decreaseGaugeValue(10) } // 输入框:直接设置数值 TextField { placeholderText: qsTr("Enter value (0-180)") onAccepted: { let inputValue = parseFloat(text) if (!isNaN(inputValue)) { gaugeController.setGaugeValue(inputValue) } } } } }
第四步:在main.cpp中整合各层
注册C++类到QML,创建Model和Controller实例,并把Controller暴露给QML上下文。
// main.cpp #include <QGuiApplication> #include <QQmlApplicationEngine> #include "gaugemodel.h" #include "gaugecontroller.h" int main(int argc, char *argv[]) { #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); #endif QGuiApplication app(argc, argv); // 注册C++类到QML,方便QML中使用 qmlRegisterType<GaugeModel>("com.example", 1, 0, "GaugeModel"); qmlRegisterType<GaugeController>("com.example", 1, 0, "GaugeController"); QQmlApplicationEngine engine; // 创建Model实例 GaugeModel* gaugeModel = new GaugeModel(); // 创建Controller,传入Model GaugeController* gaugeController = new GaugeController(gaugeModel); // 将Controller设置为全局上下文属性,让QML可以直接访问 engine.rootContext()->setContextProperty("gaugeController", gaugeController); const QUrl url(QStringLiteral("qrc:/main.qml")); QObject::connect(&engine, &QQmlApplicationEngine::objectCreated, &app, [url](QObject *obj, const QUrl &objUrl) { if (!obj && url == objUrl) QCoreApplication::exit(-1); }, Qt::QueuedConnection); engine.load(url); return app.exec(); }
关键说明
- 职责清晰:
- Model只处理数据和业务规则,比如数值范围校验,完全独立于UI;
- View只负责展示和交互,所有数据来自Model,所有交互请求交给Controller;
- Controller只做协调,不处理业务逻辑(业务逻辑都在Model里),也不负责UI展示。
- Qt特性利用:
- 使用
Q_PROPERTY让C++属性可以被QML绑定,实现数据自动更新; - 使用
Q_INVOKABLE让C++方法可以被QML直接调用; - 通过上下文属性将Controller暴露给QML,避免在QML中直接创建C++对象。
- 使用
内容的提问来源于stack exchange,提问作者GUI-Novice
相关产品推荐
相关产品推荐

