当前位置: 首页 > news >正文

Qt 应用开发之 MVC 架构

在Qt应用开发中,MVC(Model-View-Controller)架构确实是一种常用的设计模式,它通过将应用程序的业务逻辑、数据展示和用户交互分离开来,显著提高了代码的可维护性和可扩展性。以下是MVC架构在Qt应用开发中的原理阐述:

一、MVC架构的基本概念

MVC架构起源于Smalltalk,是Model(模型)、View(视图)和Controller(控制器)的缩写。在Qt应用开发中,MVC架构的具体含义如下:

  1. Model(模型):是应用程序的数据模型部分,负责管理应用程序的数据,提供对数据的增删改查等操作。它是应用程序的核心部分,并与数据源进行通信,为架构中的其他组件(如视图和委托)提供了接口。
  2. View(视图):是应用程序的可视化部分,负责展示数据,将Model维护的数据进行可视化呈现,并提供用户操作界面。它依据模型数据创建,并从模型中获得模型索引(Model Index),该索引用来表示数据项。
  3. Controller(控制器):是应用程序的控制器部分,负责接收和处理View层的用户操作并作出响应,同时还管理Model和View之间的通讯。它是Model和View之间的桥梁。

二、MVC架构在Qt中的实现原理

在Qt中,MVC架构的实现原理主要依赖于模型/视图架构(Model/View Architecture)以及委托(Delegate)的概念。

  1. 模型/视图架构

    • 将数据的存储和数据向用户的展示进行了分离,提供了更为简单的框架。
    • 数据和界面进行分离,使得相同的数据可以在多个不同的视图中显示,而且还可以创建新的视图,而不需要改变底层的数据框架。
  2. 委托(Delegate)

    • 为了对用户输入进行灵活处理,Qt引入了委托(也被称为代理Delegate)的概念。
    • 委托可以定制数据的渲染和编辑方式。在标准的视图中,委托负责渲染数据项;当编辑项目时,委托使用模型索引直接与模型进行通信。

三、MVC架构在Qt中的优势

  1. 分离关注点:MVC模式将数据、用户界面和业务逻辑分离开来,使得代码更易于理解和维护。
  2. 可扩展性:由于模块之间的松耦合性,可以更容易地添加新的功能或修改现有功能。
  3. 可重用性:通过将数据和界面分离,可以更容易地重用模型和视图组件。
  4. 可测试性:由于模块之间的明确分离,可以更容易地对模型、视图和控制器进行单元测试。

四、MVC架构在Qt中的实际应用

在Qt中,MVC架构被广泛应用于各种应用程序的开发中。例如,在一个计算器应用程序中:

  • Model部分维护了所有操作数和运算符的状态,提供了计算功能。
  • View部分提供了用户界面,包括输入框、显示框、按钮等,将Model运算结果可视化呈现。
  • Controller部分负责处理用户操作,包括点击按钮、输入数据等操作,并将其传送给Model进行处理。

五、应用示例

以下是一个简单的示例,包括模型(Model)、视图(View)和控制器(Controller)的实现,以及相应的 CMakeLists.txt 文件来组织项目。

项目结构
CalculatorApp/
├── CMakeLists.txt
├── include/
│   ├── controller.h
│   ├── model.h
│   └── view.h
├── main.cpp
├── src/
│   ├── controller.cpp
│   ├── mainwindow.cpp
│   ├── mainwindow.h
│   ├── mainwindow.ui
│   ├── model.cpp
│   └── view.cpp
└── resources/└── icons/
CMakeLists.txt
cmake_minimum_required(VERSION 3.5)project(CalculatorApp VERSION 1.0 LANGUAGES CXX)set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_STANDARD_REQUIRED ON)# Find the required Qt modules
find_package(Qt5 COMPONENTS Widgets REQUIRED)# Include directories
include_directories(${CMAKE_SOURCE_DIR}/include)# Add the executable
add_executable(CalculatorAppmain.cppsrc/mainwindow.cppsrc/controller.cppsrc/model.cppsrc/view.cpp
)# Link the Qt libraries
target_link_libraries(CalculatorApp Qt5::Widgets)# Add UI and resource files
set_source_files_properties(src/mainwindow.ui PROPERTIES HEADER_FILE_ONLY ON)
qt5_add_resources(RESOURCES ${CMAKE_SOURCE_DIR}/resources/resources.qrc)
Model (model.h)
#ifndef MODEL_H
#define MODEL_H#include <QObject>class Model : public QObject
{Q_OBJECTpublic:explicit Model(QObject *parent = nullptr);double add(double a, double b);double subtract(double a, double b);double multiply(double a, double b);double divide(double a, double b);signals:public slots:
};#endif // MODEL_H
Model (model.cpp)
#include "model.h"Model::Model(QObject *parent) : QObject(parent)
{
}double Model::add(double a, double b)
{return a + b;
}double Model::subtract(double a, double b)
{return a - b;
}double Model::multiply(double a, double b)
{return a * b;
}double Model::divide(double a, double b)
{if (b != 0)return a / b;return 0; // Avoid division by zero
}
View (view.h)
#ifndef VIEW_H
#define VIEW_H#include <QWidget>
#include <QLineEdit>
#include <QPushButton>
#include <QLabel>
#include <QVBoxLayout>class View : public QWidget
{Q_OBJECTpublic:explicit View(QWidget *parent = nullptr);void setResult(double result);signals:void number1Changed(double value);void number2Changed(double value);void operationSelected(int index);private slots:void onNumber1Changed(const QString &text);void onNumber2Changed(const QString &text);private:QLineEdit *number1Edit;QLineEdit *number2Edit;QLabel *resultLabel;QPushButton *addButton;QPushButton *subtractButton;QPushButton *multiplyButton;QPushButton *divideButton;
};#endif // VIEW_H
View (view.cpp)
#include "view.h"
#include <QDoubleValidator>View::View(QWidget *parent) : QWidget(parent)
{QVBoxLayout *layout = new QVBoxLayout(this);number1Edit = new QLineEdit(this);number2Edit = new QLineEdit(this);resultLabel = new QLabel(this);addButton = new QPushButton("Add", this);subtractButton = new QPushButton("Subtract", this);multiplyButton = new QPushButton("Multiply", this);divideButton = new QPushButton("Divide", this);QDoubleValidator *validator = new QDoubleValidator(this);number1Edit->setValidator(validator);number2Edit->setValidator(validator);connect(number1Edit, &QLineEdit::textChanged, this, &View::onNumber1Changed);connect(number2Edit, &QLineEdit::textChanged, this, &View::onNumber2Changed);connect(addButton, &QPushButton::clicked, this, [this]() { emit operationSelected(0); });connect(subtractButton, &QPushButton::clicked, this, [this]() { emit operationSelected(1); });connect(multiplyButton, &QPushButton::clicked, this, [this]() { emit operationSelected(2); });connect(divideButton, &QPushButton::clicked, this, [this]() { emit operationSelected(3); });layout->addWidget(number1Edit);layout->addWidget(number2Edit);layout->addWidget(resultLabel);layout->addWidget(addButton);layout->addWidget(subtractButton);layout->addWidget(multiplyButton);layout->addWidget(divideButton);setLayout(layout);
}void View::setResult(double result)
{resultLabel->setText(QString::number(result));
}void View::onNumber1Changed(const QString &text)
{bool ok;double value = text.toDouble(&ok);if (ok)emit number1Changed(value);
}void View::onNumber2Changed(const QString &text)
{bool ok;double value = text.toDouble(&ok);if (ok)emit number2Changed(value);
}
Controller (controller.h)
#ifndef CONTROLLER_H
#define CONTROLLER_H#include <QObject>
#include "model.h"
#include "view.h"class Controller : public QObject
{Q_OBJECTpublic:Controller(Model *model, View *view, QObject *parent = nullptr);private slots:void handleNumber1Changed(double value);void handleNumber2Changed(double value);void handleOperationSelected(int index);private:Model *model;View *view;
};#endif // CONTROLLER_H
Controller (controller.cpp)
#include "controller.h"Controller::Controller(Model *model, View *view, QObject *parent): QObject(parent), model(model), view(view)
{connect(view, &View::number1Changed, this, &Controller::handleNumber1Changed);connect(view, &View::number2Changed, this, &Controller::handleNumber2Changed);connect(view, &View::operationSelected, this, &Controller::handleOperationSelected);
}void Controller::handleNumber1Changed(double value)
{// Handle number1 change if needed
}void Controller::handleNumber2Changed(double value)
{// Handle number2 change if needed
}void Controller::handleOperationSelected(int index)
{double num1 = view->findChild<QLineEdit*>("number1Edit")->text().toDouble();double num2 = view->findChild<QLineEdit*>("number2Edit")->text().toDouble();double result = 0;switch (index) {case 0:result = model->add(num1, num2);break;case 1:result = model->subtract(num1, num2);break;case 2:result = model->multiply(num1, num2);break;case 3:result = model->divide(num1, num2);break;}view->setResult(result);
}
MainWindow (mainwindow.h)
#ifndef MAINWINDOW_H
#define MAINWINDOW_H#include <QMainWindow>
#include "controller.h"
#include "model.h"
#include "view.h"QT_BEGIN_NAMESPACE
namespace Ui { class MainWindow; }
QT_END_NAMESPACEclass MainWindow : public QMainWindow
{Q_OBJECTpublic:explicit MainWindow(QWidget *parent = nullptr);~MainWindow();private slots:// 这里可以添加槽函数,用于处理信号和槽机制// 例如:void onSomeButtonClicked();private:Ui::MainWindow *ui;Model *model;    // 指向模型对象的指针View *view;      // 指向视图对象的指针Controller *controller; // 指向控制器对象的指针// 初始化函数,用于设置 UI、模型、视图和控制器void initialize();// 其他私有成员函数// 例如:void setupUi();//       void setupModel();//       void setupView();//       void setupController();
};#endif // MAINWINDOW_H
MainWindow (mainwindow.cpp)
#include "mainwindow.h"
#include "ui_mainwindow.h"MainWindow::MainWindow(QWidget *parent) :QMainWindow(parent),ui(new Ui::MainWindow),model(new Model(this)),view(new View(this)),controller(new Controller(model, view, this))
{ui->setupUi(this);initialize();
}MainWindow::~MainWindow()
{delete ui;delete model;delete view;delete controller;
}void MainWindow::initialize()
{// 在这里进行初始化,例如:// setupUi();// setupModel();// setupView();// setupController();// 设置视图和控制器之间的连接等// 例如:connect(someSignal, this, &MainWindow::someSlot);
}// 其他成员函数的具体实现

相关文章:

Qt 应用开发之 MVC 架构

在Qt应用开发中&#xff0c;MVC&#xff08;Model-View-Controller&#xff09;架构确实是一种常用的设计模式&#xff0c;它通过将应用程序的业务逻辑、数据展示和用户交互分离开来&#xff0c;显著提高了代码的可维护性和可扩展性。以下是MVC架构在Qt应用开发中的原理阐述&am…...

python之字符串总结

字符串&#xff08;str&#xff09; 对于字符串的学习&#xff0c;我整理了网上的一些资料&#xff0c;希望可以帮助到各位&#xff01;&#xff01;&#xff01; 概述 由多个字母&#xff0c;数字&#xff0c;特殊字符组成的有限序列 字符串的定义&#xff1a;可以使用一对…...

Flutter鸿蒙next 封装 Dio 网络请求详解:登录身份验证与免登录缓存

✅近期推荐&#xff1a;求职神器 https://bbs.csdn.net/topics/619384540 &#x1f525;欢迎大家订阅系列专栏&#xff1a;flutter_鸿蒙next &#x1f4ac;淼学派语录&#xff1a;只有不断的否认自己和肯定自己&#xff0c;才能走出弯曲不平的泥泞路&#xff0c;因为平坦的大路…...

sql server复制一张表(表结构或表数据)SQL语句整理

1. 复制表结构及数据到新表 CREATE TABLE 新表 SELECT * FROM 旧表;这种方法会复制 旧表 中的所有内容到 新表&#xff0c;但新表不会保留原表的主键、自动递增等属性。为了保持这些属性&#xff0c;需要使用 ALTER 语句进行后续处理 2. 只复制表结构到新表 使用条件始终为假…...

c语言-进位计数制

文章目录 一、进位计数制是什么&#xff1f;二、c语言1.二进制转十进制2.十进制转二进制 一、进位计数制是什么&#xff1f; 进位计数制简称进制&#xff0c;是人类用于计算数量的基本规则。 可使用数字符号的数目称为基数或底数&#xff0c;基数个数为n个&#xff0c;即可称n…...

记本地第一次运行seatunnel示例项目

前置 静态源码编译通过&#xff1a;https://blog.csdn.net/u011924665/article/details/143372464 参考 seatunnel官方的开发环境搭建文档&#xff1a;https://seatunnel.incubator.apache.org/zh-CN/docs/2.3.5/contribution/setup 安装scala 下载scala 去官网下载&…...

Threejs 实现 VR 看房完结

效果&#xff1a; threejs 3d Vr 看房 gitee 地址&#xff1a; threejs-3d-map: 1、threejs 实现3d 地图效果链接&#xff1a;https://blog.csdn.net/qq_57952018/article/details/1430539902、threejs 实现 vr 看房 主要代码&#xff1a; src/views/PanoramicView/index.vu…...

找出目标值在数组中的开始和结束位置(二分查找)

给你一个按照非递减顺序排列的整数数组 nums&#xff0c;和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target&#xff0c;返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示例 1&#xff1a…...

VSCode进阶之路

VSCode进阶之路&#xff1a;从入门到高效率开发 &#x1f680; Hey&#xff0c;朋友们好&#xff01;还在为VSCode的海量功能感到眼花缭乱吗&#xff1f;咱们一起来解锁VSCode的超神技能吧&#xff01; 开篇碎碎念 &#x1f3af; 第一次用VSCode时&#xff0c;就像个闯入魔法世…...

leetcode-21-合并两个有序链表

题解&#xff1a; 1、初始化哑节点dum 2、 3、 代码&#xff1a; 参考&#xff1a;leetcode-88-合并两个有序数组...

SSM项目部署到服务器

将SSM&#xff08;Spring Spring MVC MyBatis&#xff09;项目部署到服务器上&#xff0c;通常需要以下步骤&#xff1a; 打包项目 生成一个WAR文件&#xff0c;通常位于target目录下 配置Tomcat&#xff1a; 将生成的WAR文件复制到Tomcat的webapps目录下。 配置conf/se…...

【Linux】网络编程:初识协议,序列化与反序列化——基于json串实现,网络通信计算器中简单协议的实现、手写序列化与反序列化

目录 一、什么是协议&#xff1f; 二、为什么需要有协议呢&#xff1f; 三、协议的应用 四、序列化与反序列化的引入 什么是序列化和反序列化&#xff1f; 为什么需要序列化和反序列化&#xff1f; 五、序列化推荐格式之一&#xff1a;JSON介绍 六、网络版计算器编程逻…...

Educational Codeforces Round 171 (Rated for Div. 2)(A~D) 题解

Problem - A - Codeforces--PerpendicularSegments 思路:正方形对角线最长,并且相互垂直.直接输出即可. int x,y,k; void solve(){ //Acin>>x>>y>>k;int resmin(x,y);cout<<"0 0"<<" "<<res<<" &q…...

【教程】Git 标准工作流

目录 前言建仓&#xff0c;拉仓&#xff0c;关联仓库修改代码更新本地仓库&#xff0c;并解决冲突提交代码&#xff0c;合入代码其他常用 Git 工作流删除本地仓库和远程仓库中的文件日志打印commit 相关 前言 Git 是日常开发中常用的版本控制工具&#xff0c;配合代码托管仓库…...

Nico,从零开始干掉Appium,移动端自动化测试框架实现

开头先让我碎碎念一波~去年差不多时间发布了一篇《 UiAutomator Nico&#xff0c;一个基于纯 adb 命令实现的安卓自动化测试框》&#xff08;https://testerhome.com/topics/37042&#xff09;&#xff0c; 由于种种原因 (详见此篇帖子) 当时选择了用纯 adb 命令来实现安卓自动…...

PHP合成图片,生成海报图,poster-editor使用说明

之前写过一篇使用Grafika插件生成海报图的文章&#xff0c;但是当我再次使用时&#xff0c;却发生了错误&#xff0c;回看Grafika文档&#xff0c;发现很久没更新了&#xff0c;不兼容新版的GD&#xff0c;所以改用了intervention/image插件来生成海报图。 但是后来需要对海报…...

微信小程序 - 数组 push / unshift 追加后数组返回内容为数字(数组添加后打印结果为 Number 数值类型)

前言 假设一个空数组,通过 push 方法追加了一个项,控制台打印的结果竟然是 Number 数值。 例如,以下微信小程序代码: // 源数组 var arr = [] // 追加数据 var tem = arr.push(数据)...

1、DevEco Studio 鸿蒙仓颉应用创建

1. 仓颉鸿蒙应用简介 因为仓颉是静态编译型语言&#xff0c;使用仓颉开发的应用执行效率更高。而且主打全场景&#xff0c;后续可并入仓颉生态&#xff0c;其和ArkTS都是基于ArkUI进行开发&#xff0c;最大的区别是typescript和仓颉语法间的差异。 2. 应用创建 前置条件&…...

从头开始学PHP之面向对象

首先介绍下最近情况&#xff0c;因为最近入职了且通勤距离较远&#xff0c;导致精力不够了&#xff0c;而且我发现&#xff0c;人一旦上了班&#xff0c;下班之后就不想再进行任何脑力劳动了&#xff08;对大部分牛马来说&#xff0c;精英除外&#xff09;。 话不多说进入今天的…...

C++ | Leetcode C++题解之第519题随机翻转矩阵

题目&#xff1a; 题解&#xff1a; class Solution { public:Solution(int m, int n) {this->m m;this->n n;this->total m * n;srand(time(nullptr));}vector<int> flip() {int x rand() % total;vector<int> ans;total--; // 查找位置 x 对应的…...

手游刚开服就被攻击怎么办?如何防御DDoS?

开服初期是手游最脆弱的阶段&#xff0c;极易成为DDoS攻击的目标。一旦遭遇攻击&#xff0c;可能导致服务器瘫痪、玩家流失&#xff0c;甚至造成巨大经济损失。本文为开发者提供一套简洁有效的应急与防御方案&#xff0c;帮助快速应对并构建长期防护体系。 一、遭遇攻击的紧急应…...

iOS 26 携众系统重磅更新,但“苹果智能”仍与国行无缘

美国西海岸的夏天&#xff0c;再次被苹果点燃。一年一度的全球开发者大会 WWDC25 如期而至&#xff0c;这不仅是开发者的盛宴&#xff0c;更是全球数亿苹果用户翘首以盼的科技春晚。今年&#xff0c;苹果依旧为我们带来了全家桶式的系统更新&#xff0c;包括 iOS 26、iPadOS 26…...

ES6从入门到精通:前言

ES6简介 ES6&#xff08;ECMAScript 2015&#xff09;是JavaScript语言的重大更新&#xff0c;引入了许多新特性&#xff0c;包括语法糖、新数据类型、模块化支持等&#xff0c;显著提升了开发效率和代码可维护性。 核心知识点概览 变量声明 let 和 const 取代 var&#xf…...

微软PowerBI考试 PL300-选择 Power BI 模型框架【附练习数据】

微软PowerBI考试 PL300-选择 Power BI 模型框架 20 多年来&#xff0c;Microsoft 持续对企业商业智能 (BI) 进行大量投资。 Azure Analysis Services (AAS) 和 SQL Server Analysis Services (SSAS) 基于无数企业使用的成熟的 BI 数据建模技术。 同样的技术也是 Power BI 数据…...

R语言AI模型部署方案:精准离线运行详解

R语言AI模型部署方案:精准离线运行详解 一、项目概述 本文将构建一个完整的R语言AI部署解决方案,实现鸢尾花分类模型的训练、保存、离线部署和预测功能。核心特点: 100%离线运行能力自包含环境依赖生产级错误处理跨平台兼容性模型版本管理# 文件结构说明 Iris_AI_Deployme…...

从零实现富文本编辑器#5-编辑器选区模型的状态结构表达

先前我们总结了浏览器选区模型的交互策略&#xff0c;并且实现了基本的选区操作&#xff0c;还调研了自绘选区的实现。那么相对的&#xff0c;我们还需要设计编辑器的选区表达&#xff0c;也可以称为模型选区。编辑器中应用变更时的操作范围&#xff0c;就是以模型选区为基准来…...

ESP32读取DHT11温湿度数据

芯片&#xff1a;ESP32 环境&#xff1a;Arduino 一、安装DHT11传感器库 红框的库&#xff0c;别安装错了 二、代码 注意&#xff0c;DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...

转转集团旗下首家二手多品类循环仓店“超级转转”开业

6月9日&#xff0c;国内领先的循环经济企业转转集团旗下首家二手多品类循环仓店“超级转转”正式开业。 转转集团创始人兼CEO黄炜、转转循环时尚发起人朱珠、转转集团COO兼红布林CEO胡伟琨、王府井集团副总裁祝捷等出席了开业剪彩仪式。 据「TMT星球」了解&#xff0c;“超级…...

使用van-uploader 的UI组件,结合vue2如何实现图片上传组件的封装

以下是基于 vant-ui&#xff08;适配 Vue2 版本 &#xff09;实现截图中照片上传预览、删除功能&#xff0c;并封装成可复用组件的完整代码&#xff0c;包含样式和逻辑实现&#xff0c;可直接在 Vue2 项目中使用&#xff1a; 1. 封装的图片上传组件 ImageUploader.vue <te…...

k8s业务程序联调工具-KtConnect

概述 原理 工具作用是建立了一个从本地到集群的单向VPN&#xff0c;根据VPN原理&#xff0c;打通两个内网必然需要借助一个公共中继节点&#xff0c;ktconnect工具巧妙的利用k8s原生的portforward能力&#xff0c;简化了建立连接的过程&#xff0c;apiserver间接起到了中继节…...