Qt 应用开发之 MVC 架构
在Qt应用开发中,MVC(Model-View-Controller)架构确实是一种常用的设计模式,它通过将应用程序的业务逻辑、数据展示和用户交互分离开来,显著提高了代码的可维护性和可扩展性。以下是MVC架构在Qt应用开发中的原理阐述:
一、MVC架构的基本概念
MVC架构起源于Smalltalk,是Model(模型)、View(视图)和Controller(控制器)的缩写。在Qt应用开发中,MVC架构的具体含义如下:
- Model(模型):是应用程序的数据模型部分,负责管理应用程序的数据,提供对数据的增删改查等操作。它是应用程序的核心部分,并与数据源进行通信,为架构中的其他组件(如视图和委托)提供了接口。
- View(视图):是应用程序的可视化部分,负责展示数据,将Model维护的数据进行可视化呈现,并提供用户操作界面。它依据模型数据创建,并从模型中获得模型索引(Model Index),该索引用来表示数据项。
- Controller(控制器):是应用程序的控制器部分,负责接收和处理View层的用户操作并作出响应,同时还管理Model和View之间的通讯。它是Model和View之间的桥梁。
二、MVC架构在Qt中的实现原理
在Qt中,MVC架构的实现原理主要依赖于模型/视图架构(Model/View Architecture)以及委托(Delegate)的概念。
-
模型/视图架构:
- 将数据的存储和数据向用户的展示进行了分离,提供了更为简单的框架。
- 数据和界面进行分离,使得相同的数据可以在多个不同的视图中显示,而且还可以创建新的视图,而不需要改变底层的数据框架。
-
委托(Delegate):
- 为了对用户输入进行灵活处理,Qt引入了委托(也被称为代理Delegate)的概念。
- 委托可以定制数据的渲染和编辑方式。在标准的视图中,委托负责渲染数据项;当编辑项目时,委托使用模型索引直接与模型进行通信。
三、MVC架构在Qt中的优势
- 分离关注点:MVC模式将数据、用户界面和业务逻辑分离开来,使得代码更易于理解和维护。
- 可扩展性:由于模块之间的松耦合性,可以更容易地添加新的功能或修改现有功能。
- 可重用性:通过将数据和界面分离,可以更容易地重用模型和视图组件。
- 可测试性:由于模块之间的明确分离,可以更容易地对模型、视图和控制器进行单元测试。
四、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应用开发中,MVC(Model-View-Controller)架构确实是一种常用的设计模式,它通过将应用程序的业务逻辑、数据展示和用户交互分离开来,显著提高了代码的可维护性和可扩展性。以下是MVC架构在Qt应用开发中的原理阐述&am…...
python之字符串总结
字符串(str) 对于字符串的学习,我整理了网上的一些资料,希望可以帮助到各位!!! 概述 由多个字母,数字,特殊字符组成的有限序列 字符串的定义:可以使用一对…...
Flutter鸿蒙next 封装 Dio 网络请求详解:登录身份验证与免登录缓存
✅近期推荐:求职神器 https://bbs.csdn.net/topics/619384540 🔥欢迎大家订阅系列专栏:flutter_鸿蒙next 💬淼学派语录:只有不断的否认自己和肯定自己,才能走出弯曲不平的泥泞路,因为平坦的大路…...
sql server复制一张表(表结构或表数据)SQL语句整理
1. 复制表结构及数据到新表 CREATE TABLE 新表 SELECT * FROM 旧表;这种方法会复制 旧表 中的所有内容到 新表,但新表不会保留原表的主键、自动递增等属性。为了保持这些属性,需要使用 ALTER 语句进行后续处理 2. 只复制表结构到新表 使用条件始终为假…...
c语言-进位计数制
文章目录 一、进位计数制是什么?二、c语言1.二进制转十进制2.十进制转二进制 一、进位计数制是什么? 进位计数制简称进制,是人类用于计算数量的基本规则。 可使用数字符号的数目称为基数或底数,基数个数为n个,即可称n…...
记本地第一次运行seatunnel示例项目
前置 静态源码编译通过:https://blog.csdn.net/u011924665/article/details/143372464 参考 seatunnel官方的开发环境搭建文档:https://seatunnel.incubator.apache.org/zh-CN/docs/2.3.5/contribution/setup 安装scala 下载scala 去官网下载&…...
Threejs 实现 VR 看房完结
效果: threejs 3d Vr 看房 gitee 地址: threejs-3d-map: 1、threejs 实现3d 地图效果链接:https://blog.csdn.net/qq_57952018/article/details/1430539902、threejs 实现 vr 看房 主要代码: src/views/PanoramicView/index.vu…...
找出目标值在数组中的开始和结束位置(二分查找)
给你一个按照非递减顺序排列的整数数组 nums,和一个目标值 target。请你找出给定目标值在数组中的开始位置和结束位置。 如果数组中不存在目标值 target,返回 [-1, -1]。 你必须设计并实现时间复杂度为 O(log n) 的算法解决此问题。 示例 1:…...
VSCode进阶之路
VSCode进阶之路:从入门到高效率开发 🚀 Hey,朋友们好!还在为VSCode的海量功能感到眼花缭乱吗?咱们一起来解锁VSCode的超神技能吧! 开篇碎碎念 🎯 第一次用VSCode时,就像个闯入魔法世…...
leetcode-21-合并两个有序链表
题解: 1、初始化哑节点dum 2、 3、 代码: 参考:leetcode-88-合并两个有序数组...
SSM项目部署到服务器
将SSM(Spring Spring MVC MyBatis)项目部署到服务器上,通常需要以下步骤: 打包项目 生成一个WAR文件,通常位于target目录下 配置Tomcat: 将生成的WAR文件复制到Tomcat的webapps目录下。 配置conf/se…...
【Linux】网络编程:初识协议,序列化与反序列化——基于json串实现,网络通信计算器中简单协议的实现、手写序列化与反序列化
目录 一、什么是协议? 二、为什么需要有协议呢? 三、协议的应用 四、序列化与反序列化的引入 什么是序列化和反序列化? 为什么需要序列化和反序列化? 五、序列化推荐格式之一: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 标准工作流
目录 前言建仓,拉仓,关联仓库修改代码更新本地仓库,并解决冲突提交代码,合入代码其他常用 Git 工作流删除本地仓库和远程仓库中的文件日志打印commit 相关 前言 Git 是日常开发中常用的版本控制工具,配合代码托管仓库…...
Nico,从零开始干掉Appium,移动端自动化测试框架实现
开头先让我碎碎念一波~去年差不多时间发布了一篇《 UiAutomator Nico,一个基于纯 adb 命令实现的安卓自动化测试框》(https://testerhome.com/topics/37042), 由于种种原因 (详见此篇帖子) 当时选择了用纯 adb 命令来实现安卓自动…...
PHP合成图片,生成海报图,poster-editor使用说明
之前写过一篇使用Grafika插件生成海报图的文章,但是当我再次使用时,却发生了错误,回看Grafika文档,发现很久没更新了,不兼容新版的GD,所以改用了intervention/image插件来生成海报图。 但是后来需要对海报…...
微信小程序 - 数组 push / unshift 追加后数组返回内容为数字(数组添加后打印结果为 Number 数值类型)
前言 假设一个空数组,通过 push 方法追加了一个项,控制台打印的结果竟然是 Number 数值。 例如,以下微信小程序代码: // 源数组 var arr = [] // 追加数据 var tem = arr.push(数据)...
1、DevEco Studio 鸿蒙仓颉应用创建
1. 仓颉鸿蒙应用简介 因为仓颉是静态编译型语言,使用仓颉开发的应用执行效率更高。而且主打全场景,后续可并入仓颉生态,其和ArkTS都是基于ArkUI进行开发,最大的区别是typescript和仓颉语法间的差异。 2. 应用创建 前置条件&…...
从头开始学PHP之面向对象
首先介绍下最近情况,因为最近入职了且通勤距离较远,导致精力不够了,而且我发现,人一旦上了班,下班之后就不想再进行任何脑力劳动了(对大部分牛马来说,精英除外)。 话不多说进入今天的…...
C++ | Leetcode C++题解之第519题随机翻转矩阵
题目: 题解: 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 对应的…...
idea大量爆红问题解决
问题描述 在学习和工作中,idea是程序员不可缺少的一个工具,但是突然在有些时候就会出现大量爆红的问题,发现无法跳转,无论是关机重启或者是替换root都无法解决 就是如上所展示的问题,但是程序依然可以启动。 问题解决…...
测试markdown--肇兴
day1: 1、去程:7:04 --11:32高铁 高铁右转上售票大厅2楼,穿过候车厅下一楼,上大巴车 ¥10/人 **2、到达:**12点多到达寨子,买门票,美团/抖音:¥78人 3、中饭&a…...
SpringCloudGateway 自定义局部过滤器
场景: 将所有请求转化为同一路径请求(方便穿网配置)在请求头内标识原来路径,然后在将请求分发给不同服务 AllToOneGatewayFilterFactory import lombok.Getter; import lombok.Setter; import lombok.extern.slf4j.Slf4j; impor…...
2023赣州旅游投资集团
单选题 1.“不登高山,不知天之高也;不临深溪,不知地之厚也。”这句话说明_____。 A、人的意识具有创造性 B、人的认识是独立于实践之外的 C、实践在认识过程中具有决定作用 D、人的一切知识都是从直接经验中获得的 参考答案: C 本题解…...
Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
免费数学几何作图web平台
光锐软件免费数学工具,maths,数学制图,数学作图,几何作图,几何,AR开发,AR教育,增强现实,软件公司,XR,MR,VR,虚拟仿真,虚拟现实,混合现实,教育科技产品,职业模拟培训,高保真VR场景,结构互动课件,元宇宙http://xaglare.c…...
C# 表达式和运算符(求值顺序)
求值顺序 表达式可以由许多嵌套的子表达式构成。子表达式的求值顺序可以使表达式的最终值发生 变化。 例如,已知表达式3*52,依照子表达式的求值顺序,有两种可能的结果,如图9-3所示。 如果乘法先执行,结果是17。如果5…...
深度学习之模型压缩三驾马车:模型剪枝、模型量化、知识蒸馏
一、引言 在深度学习中,我们训练出的神经网络往往非常庞大(比如像 ResNet、YOLOv8、Vision Transformer),虽然精度很高,但“太重”了,运行起来很慢,占用内存大,不适合部署到手机、摄…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...
大模型——基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程
基于Docker+DeepSeek+Dify :搭建企业级本地私有化知识库超详细教程 下载安装Docker Docker官网:https://www.docker.com/ 自定义Docker安装路径 Docker默认安装在C盘,大小大概2.9G,做这行最忌讳的就是安装软件全装C盘,所以我调整了下安装路径。 新建安装目录:E:\MyS…...
