You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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();
}

关键说明

  1. 职责清晰:
    • Model只处理数据和业务规则,比如数值范围校验,完全独立于UI;
    • View只负责展示和交互,所有数据来自Model,所有交互请求交给Controller;
    • Controller只做协调,不处理业务逻辑(业务逻辑都在Model里),也不负责UI展示。
  2. Qt特性利用:
    • 使用Q_PROPERTY让C++属性可以被QML绑定,实现数据自动更新;
    • 使用Q_INVOKABLE让C++方法可以被QML直接调用;
    • 通过上下文属性将Controller暴露给QML,避免在QML中直接创建C++对象。

内容的提问来源于stack exchange,提问作者GUI-Novice

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.21 04:29:36