【C++基本功】什么是MVC,QT中的MVC如何实现?
MVC(Model-View-Controller)架构是一种经典的软件设计模式,通过将应用程序划分为模型(Model)、视图(View)、控制器(Controller)三个核心组件,实现关注点分离(Separation of Concerns),旨在提升代码的可维护性、可扩展性及团队协作效率。自1979年由Smalltalk提出以来,MVC已成为Web开发、移动应用及桌面应用的主流架构模式,并随着技术演进不断适配现代开发需求。
一、MVC架构的核心组件与职责
MVC的核心逻辑是“数据-界面-控制”的分离,三个组件的职责边界清晰:
-
模型(Model):
负责数据管理与业务逻辑,是应用程序的“大脑”。它封装了数据的存储(如数据库交互)、验证(如输入合法性检查)及处理逻辑(如计算、转换)。模型独立于视图和控制器,可单独测试与修改,确保业务逻辑的一致性。
示例:在电商应用中,模型可能包含
Product类(封装商品的价格、库存等属性)、OrderService类(处理订单生成、支付逻辑)。 -
视图(View):
负责用户界面的展示,是用户与应用程序交互的“窗口”。它从模型获取数据,并将其渲染为用户可见的形式(如HTML页面、移动应用界面)。视图不包含业务逻辑,仅关注数据的呈现,确保界面的简洁性与可维护性。
示例:在Web应用中,视图可能是Razor模板(ASP.NET MVC)、Thymeleaf模板(Spring MVC)或React组件(前端MVC)。
-
控制器(Controller):
负责协调模型与视图的交互,是“桥梁”角色。它接收用户的输入(如按钮点击、表单提交),调用模型的业务逻辑处理数据,然后将结果传递给视图进行渲染。控制器不包含业务逻辑,仅做数据适配与流程控制,确保输入与输出的正确性。
示例:在Spring MVC中,控制器使用
@RestController注解,通过@GetMapping映射请求路径,调用服务层获取数据后返回视图。
二、MVC架构的工作流程
MVC的典型工作流程可概括为“用户输入→控制器处理→模型更新→视图渲染”:
-
用户交互:用户通过视图(如点击按钮、提交表单)发送请求。
-
控制器接收:控制器捕获用户输入,解析请求参数(如路径变量、表单数据)。
-
模型处理:控制器调用模型的业务逻辑(如查询数据库、计算数据),模型返回处理结果。
-
视图渲染:控制器将模型数据传递给视图,视图根据数据生成用户界面(如HTML页面、JSON响应)。
-
响应用户:视图将渲染后的结果返回给用户,完成一次交互循环。
示例:在ASP.NET MVC中,用户请求/products/{id},控制器ProductController通过@GetMapping映射请求,调用ProductService获取商品数据,然后将数据传递给Razor视图ProductDetail.cshtml进行渲染。
三、MVC架构的优势
MVC的核心价值在于“分离关注点”,其优势主要体现在:
-
可维护性:各组件职责清晰,修改视图不影响模型,修改模型不影响控制器,降低了代码的耦合度。
-
可扩展性:新增功能时,只需扩展对应的组件(如添加新视图、修改模型逻辑),无需重构整个应用程序。
-
可测试性:模型可独立进行单元测试(如测试业务逻辑的正确性),控制器可模拟用户输入进行集成测试,视图的测试可通过自动化工具(如Selenium)完成。
-
团队协作:设计师可专注于视图的设计,开发者可专注于模型的实现,测试人员可专注于各组件的测试,提升了团队协作效率。
四、MVC架构的演进与现代应用
随着Web技术的发展(如前端框架、微服务、响应式编程),MVC架构也在不断演进,适配现代开发需求:
-
前端MVC:React、Vue.js等前端框架采用组件化设计,将视图与控制器融合(如React的
Component类包含状态与渲染逻辑),形成“前端MVC”模式。例如,React组件通过state管理数据(模型),通过render方法渲染视图,通过事件处理(如onClick)扮演控制器的角色。 -
后端MVC的强化:Spring MVC 5.0引入类型安全路径变量(如
@PathVariable("id") Long userId)、全局异常处理(如@ControllerAdvice),提升了后端MVC的健壮性;ASP.NET Core MVC加强了依赖注入(DI)与中间件支持,简化了组件的集成。 -
与微服务结合:在微服务架构中,每个微服务可采用MVC模式,将服务拆分为模型(数据处理)、视图(API响应)、控制器(请求路由)。例如,Spring Cloud中的网关模块与MVC深度整合,支持高级限流策略,提升了分布式系统的可扩展性。
五、MVC架构的应用场景
MVC架构适用于需要清晰结构、易于维护的应用程序,尤其是:
-
企业级Web应用:如电商平台、金融系统、ERP系统,这些应用需要处理复杂的业务逻辑与数据交互,MVC的分离特性可有效降低代码复杂度。
-
移动应用:如iOS、Android应用,MVC可将界面(View)与业务逻辑(Model)分离,提升应用的性能与可维护性。
-
前端应用:如React、Vue.js项目,前端MVC模式可有效管理组件的状态与交互,提升用户体验。
六、总结
MVC架构是一种经久不衰的软件设计模式,其核心思想是“分离关注点”,通过模型、视图、控制器的协同工作,实现应用程序的可维护性、可扩展性与可测试性。随着技术的演进,MVC已从传统的后端模式扩展到前端、微服务等场景,成为现代软件开发的基础架构之一。无论是企业级Web应用、移动应用还是前端项目,MVC都能提供有效的架构支持,帮助开发者构建健壮、易维护的应用程序。
Qt中的MVC(Model-View-Controller)架构以模型/视图/委托(Model/View/Delegate)形式实现,核心思想是数据与界面分离:模型负责管理数据,视图负责显示数据,委托负责数据的交互(绘制与编辑)。这种架构提高了代码的重用性和灵活性,允许同一数据通过不同视图展示,且视图修改不影响底层数据逻辑。
一、Qt MVC架构的核心组件
Qt的MVC架构由三大核心组件构成,分别承担不同的职责:
1. 模型(Model)
模型是数据的管理者,负责存储、组织和提供数据,不涉及界面逻辑。它通过抽象接口(如QAbstractItemModel)向视图提供数据,支持数据的增删改查,并通过信号通知视图数据变化。
-
内置模型类:
Qt提供了多种预定义模型,适配不同数据结构:
-
QStringListModel:存储字符串列表(如待办事项列表); -
QStandardItemModel:通用模型,可表示列表、表格、树形结构; -
QFileSystemModel:访问本地文件系统(如文件浏览器); -
QSqlTableModel:操作数据库表(如用户信息管理)。
-
-
自定义模型:若内置模型无法满足需求,可通过继承
QAbstractItemModel实现自定义模型(如处理复杂树形数据)。
2. 视图(View)
视图是数据的展示者,负责将模型中的数据以可视化方式呈现(如列表、表格、树形结构),并接收用户输入(如点击、滚动)。视图不存储数据,仅依赖模型提供的接口获取数据。
-
内置视图类:
Qt提供了多种视图控件,适配不同展示需求:
-
QListView:以列表形式显示数据; -
QTableView:以表格形式显示数据(如Excel表格); -
QTreeView:以树形结构显示层级数据(如文件系统的目录结构)。
-
3. 委托(Delegate)
委托是数据与用户之间的交互桥梁,负责数据的绘制(如单元格样式)和编辑(如弹出输入框)。当用户修改数据时,委托将输入传递给模型更新数据。
-
内置委托:Qt提供了默认委托(如
QStyledItemDelegate),支持基本的文本绘制和编辑; -
自定义委托:若需定制交互(如用滑块编辑数值、用颜色选择器编辑颜色),可通过继承
QAbstractItemDelegate实现自定义委托。
二、Qt MVC架构的工作流程
Qt MVC架构的工作流程围绕数据流动和信号通信展开:
-
模型初始化:创建模型对象(如
QStandardItemModel),并填充数据(如通过setData()方法设置单元格内容); -
视图绑定模型:将视图对象(如
QTableView)与模型关联(通过setModel()方法),视图从模型获取数据并渲染; -
用户交互:用户通过视图进行操作(如点击单元格、滚动页面),视图将交互事件传递给委托;
-
数据处理:委托处理用户输入(如编辑单元格内容),将修改后的数据传递给模型;
-
数据更新通知:模型通过信号(如
dataChanged())通知视图数据变化,视图自动刷新显示。
三、Qt MVC架构的优势
-
数据与界面分离:修改视图不影响模型逻辑,修改模型不影响视图展示,降低了代码耦合度;
-
高重用性:同一模型可通过不同视图展示(如列表数据和表格数据共享同一模型);
-
易扩展性:支持自定义模型和委托,适配复杂业务需求(如树形数据展示、自定义编辑控件);
-
信号通信机制:通过信号与槽实现组件间通信,避免了直接依赖,提高了代码的可维护性。
四、Qt MVC架构的应用场景
Qt MVC架构适用于需要清晰数据与界面分离的应用程序,常见场景包括:
-
数据管理应用:如数据库管理系统(使用
QSqlTableModel操作数据库表)、文件浏览器(使用QFileSystemModel访问文件系统); -
表格/列表应用:如待办事项列表(使用
QStringListModel存储数据,QListView展示)、Excel-like表格(使用QStandardItemModel和QTableView); -
层级数据应用:如组织架构图(使用
QStandardItemModel表示树形结构,QTreeView展示)。
五、Qt MVC架构的示例(简单表格应用)
以下是一个使用Qt MVC架构实现的简单表格应用示例,展示了模型的创建、视图的绑定和数据的显示:
#include <QApplication>
#include <QTableView>
#include <QStandardItemModel>
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// 1. 创建模型(4行2列)
QStandardItemModel model(4, 2);
for (int row = 0; row < 4; ++row) {
for (int col = 0; col < 2; ++col) {
// 设置单元格数据(行号+列号)
model.setData(model.index(row, col), QString("Row%1, Col%2").arg(row).arg(col));
}
}
// 2. 创建视图(表格)
QTableView tableView;
tableView.setModel(&model); // 绑定模型
// 3. 显示视图
tableView.show();
return app.exec();
}
-
代码说明:
-
模型:使用
QStandardItemModel创建一个4行2列的表格模型,并通过setData()方法填充数据; -
视图:使用
QTableView创建表格视图,通过setModel()方法绑定模型; -
运行效果:窗口显示一个4行2列的表格,单元格内容为“RowX, ColY”。
-
六、Qt 6中的MVC架构变化
Qt 6对MVC架构的API基本保持兼容,主要改进在于性能优化(如模型数据更新的效率提升)和新特性支持(如更好的高DPI显示支持)。对于现有Qt 5项目,升级到Qt 6时无需大幅修改MVC相关代码。
总结
Qt的MVC架构(模型/视图/委托)是数据驱动界面的核心实现方式,通过分离数据、视图和交互逻辑,提高了应用程序的可维护性和扩展性。其内置的模型和视图类覆盖了常见需求,自定义模型和委托则支持复杂场景。无论是简单的列表应用还是复杂的数据库管理系统,Qt MVC架构都能提供有效的解决方案。
Qt MVC(实际为Model/View/Delegate范式)与传统MVC架构的核心差异在于组件职责划分与交互机制,但二者均遵循“关注点分离”的核心思想。以下从相同点与不同点展开详细对比:
一、相同点:核心设计思想一致
传统MVC与Qt MVC均以“分离关注点”为核心目标,通过拆分组件职责提升代码的可维护性、可扩展性与可测试性,具体表现为:
-
组件分工明确:
两者均包含模型(Model)、视图(View)两类核心组件,且职责边界一致:
-
模型(Model):负责数据管理与业务逻辑,是应用程序的“数据源”,提供标准化接口(如
rowCount()、data())供外部访问数据,不依赖视图或控制器。 -
视图(View):负责数据可视化,将模型中的数据以用户可感知的形式(如列表、表格、树形结构)呈现,不包含业务逻辑。
-
-
数据与界面解耦:
传统MVC中模型与视图分离,Qt MVC进一步强化了这一特性——模型独立于视图,同一模型可绑定多个视图(如同一份数据同时以表格和图表展示),视图修改不影响模型逻辑。
二、不同点:Qt MVC的“简化与定制化”改进
Qt MVC并未完全遵循传统MVC的“三组件”结构,而是通过合并视图与控制器、引入委托(Delegate)的方式,适配GUI应用的交互密集型需求,具体差异如下:
1. 组件结构:Qt MVC合并“视图与控制器”,引入“委托”
传统MVC的控制器(Controller)是独立组件,负责处理用户输入(如点击、输入)并协调模型与视图的交互。而Qt MVC将视图与控制器的功能合并,同时引入委托(Delegate)替代控制器的数据项交互职责,形成“Model-View-Delegate”三元结构:
-
视图(View):不仅负责展示数据,还承担传统控制器中“用户交互路由”的角色(如接收按钮点击、列表双击等事件),通过信号槽机制将事件传递给模型或委托。
-
委托(Delegate):替代传统控制器的“数据项编辑与渲染”职责,负责:
-
数据项渲染:自定义单元格的绘制方式(如进度条、星级评分);
-
编辑交互:创建编辑控件(如
QLineEdit、QCalendarWidget)并处理用户输入; -
数据验证:对用户输入进行格式校验(如限制数字输入、日期格式)。
-
例如,在Qt中,用户双击表格单元格时,视图接收双击事件,委托创建QLineEdit控件供用户输入,输入完成后委托通过模型接口(如setData())提交修改,模型验证数据后触发dataChanged()信号,视图自动更新。
2. 交互机制:Qt MVC依赖“信号槽”,传统MVC依赖“控制器中介”
传统MVC中,控制器是模型与视图的“中介”:用户输入先传递给控制器,控制器解析后调用模型方法修改数据,再通知视图更新。而Qt MVC通过信号槽机制实现组件间的松耦合,无需控制器作为中介:
-
模型→视图:模型数据修改时,通过
dataChanged()信号通知所有关联视图自动更新; -
视图→委托:用户交互事件(如单元格编辑)通过信号传递给委托,委托处理后通过模型接口修改数据;
-
委托→模型:委托通过模型的
setData()方法提交修改,模型验证数据后触发信号通知视图。
这种机制使得Qt MVC的组件间依赖更小,代码更简洁,尤其适合GUI应用的事件驱动特性。
3. 定制灵活性:Qt MVC的“委托”支持更细粒度的UI定制
传统MVC中,控制器的职责是“协调”,不涉及数据项的具体渲染与编辑,因此定制灵活性有限。而Qt MVC的委托允许开发者对单个数据项进行精细定制:
-
渲染定制:通过重写
paint()方法,自定义单元格的绘制逻辑(如用进度条显示任务进度、用颜色标记状态); -
编辑定制:通过重写
createEditor()方法,为不同数据项创建不同的编辑控件(如日期列用QCalendarWidget、数字列用QSpinBox); -
验证定制:通过重写
setModelData()方法,对用户输入进行实时验证(如限制输入为正整数、邮箱格式)。
例如,在Qt中,若需让表格的“价格”列只能输入数字,可通过自定义委托重写createEditor()方法,返回QLineEdit并设置正则表达式验证,确保用户输入符合要求。
4. 适用场景:Qt MVC更适配“数据密集型”GUI应用
传统MVC适用于Web应用(如Spring MVC、Django),其中控制器负责处理HTTP请求、协调模型与视图。而Qt MVC(Model/View/Delegate)更适配桌面GUI应用(如数据库管理工具、配置文件编辑器、实时监控仪表盘),其“视图+委托”的结构更适合处理大量数据的可视化与交互:
-
数据密集型场景:如数据库应用中,
QSqlTableModel模型可绑定QTableView视图,委托可自定义单元格的编辑方式(如外键列用下拉框),提升数据录入效率; -
实时交互场景:如实时监控仪表盘,视图可实时显示模型中的数据变化(如温度、压力),委托可处理用户的“报警确认”等交互操作。
三、总结:Qt MVC是传统MVC的“GUI适配版”
Qt MVC(Model/View/Delegate)并未脱离传统MVC的核心思想,而是通过合并视图与控制器、引入委托的方式,优化了GUI应用的交互体验与定制灵活性。其本质是:
-
保留传统MVC的“数据与界面分离”:模型负责数据,视图负责展示;
-
简化控制器职责:将控制器的“交互路由”功能融入视图,通过信号槽实现松耦合;
-
增强数据项定制:通过委托支持更细粒度的数据项渲染与编辑,适配GUI应用的复杂需求。
对于开发者而言,Qt MVC的优势在于代码简洁(无需独立控制器)、定制灵活(委托支持精细调整)、交互高效(信号槽机制实时响应),尤其适合开发数据密集型的桌面GUI应用。
如何设计Qt MVC的模型?
设计Qt MVC的模型(Model)时,核心目标是高效管理数据并通过标准接口与视图(View)通信,同时支持数据变化的通知。Qt提供了QAbstractItemModel及其特化类(如QAbstractListModel、QAbstractTableModel)作为基类,开发者需根据数据结构选择合适的基类,并实现关键虚函数。以下是具体设计步骤与实践要点:
一、明确数据结构与需求
模型的设计需紧密贴合数据的实际结构和业务需求。常见的数据结构有三种:
-
列表(List):一维数据(如待办事项列表);
-
表格(Table):二维数据(如Excel表格、数据库表);
-
树形(Tree):层级结构(如文件目录、组织架构)。
同时需明确:
-
是否需要支持数据编辑?
-
是否需要自定义数据展示(如颜色、图标)?
-
数据量大小(是否需要优化性能)?
二、选择合适的模型基类
Qt提供了多层级的抽象基类,根据数据结构选择可简化开发:
| 基类 | 适用场景 | 需实现的虚函数 |
|---|---|---|
QAbstractItemModel |
通用树形结构(最灵活) | index(), parent(), rowCount(), columnCount(), data(), headerData() |
QAbstractListModel |
一维列表(简化版) | rowCount(), data(), headerData()(部分函数已实现) |
QAbstractTableModel |
二维表格(简化版) | rowCount(), columnCount(), data(), headerData()(部分函数已实现) |
建议:优先使用特化类(如QAbstractTableModel),除非数据是复杂树形结构。
三、实现核心虚函数
无论选择哪个基类,都需要实现以下核心函数来定义数据的行为:
1. 数据量与结构:rowCount()、columnCount()、index()、parent()
-
rowCount(const QModelIndex &parent = QModelIndex()) const:返回父节点下的子项数量。对于列表/表格(无层级),
parent无效,直接返回总行数;对于树形结构,需根据parent的行号计算子节点数。示例(表格模型):返回固定行数(如
return m_data.size();)。 -
columnCount(const QModelIndex &parent = QModelIndex()) const:返回父节点下的列数。表格/列表通常返回固定列数(如
return 3;);树形结构的根节点列数可能与其他节点不同。 -
QModelIndex index(int row, int column, const QModelIndex &parent = QModelIndex()) const:生成指定行、列、父节点的
QModelIndex(模型的唯一标识)。树形结构中,parent非空时表示子节点;列表/表格中parent通常无效(返回根节点的子项)。 -
QModelIndex parent(const QModelIndex &child) const:返回子节点的父节点索引。列表/表格中所有节点的父节点是无效索引(
QModelIndex());树形结构需根据child的内部指针找到父节点。
2. 数据访问:data()与headerData()
-
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const:核心函数,返回指定索引、指定角色的数据。
role决定数据的用途:-
Qt::DisplayRole:视图默认显示的文本; -
Qt::EditRole:编辑时使用的原始数据; -
自定义角色(如
Qt::UserRole):存储额外数据(如图标路径、颜色值)。示例:
QVariant MyTableModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) return QVariant(); if (role == Qt::DisplayRole || role == Qt::EditRole) { return m_data[index.row()][index.column()]; // 返回表格数据 } else if (role == Qt::TextAlignmentRole) { return Qt::AlignCenter; // 文本居中对齐 } return QVariant(); } -
-
QVariant headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const:返回表头/列头/行头的数据(如表格的列标题“姓名”“年龄”)。
orientation为Qt::Horizontal(列头)或Qt::Vertical(行头)。
3. 编辑支持:setData()与flags()
若模型需要支持用户编辑数据(如双击单元格修改),需实现以下函数:
-
bool setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole):处理用户输入的数据修改。验证
value的合法性(如数字范围、字符串长度),若合法则更新内部数据,并发出dataChanged()信号通知视图。示例:
bool MyTableModel::setData(const QModelIndex &index, const QVariant &value, int role) { if (!index.isValid() || role != Qt::EditRole) return false; int row = index.row(); int col = index.column(); if (col == 1 && value.toInt() < 0) { // 年龄不能为负数 return false; } m_data[row][col] = value; // 更新数据 emit dataChanged(index, index, {role}); // 通知视图更新 return true; } -
Qt::ItemFlags flags(const QModelIndex &index) const:返回项目的行为标志(如是否可编辑、可选中)。通过
Qt::ItemIsEditable启用编辑。示例:
Qt::ItemFlags MyTableModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractTableModel::flags(index); return defaultFlags | Qt::ItemIsEditable; // 所有单元格可编辑 }
四、数据变化的通知机制
模型的数据变化时,需通过信号通知关联的视图自动更新。Qt模型提供了以下常用信号:
-
dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight, const QVector<int> &roles):当
[topLeft, bottomRight]范围内的数据变化时触发(如修改单个单元格用index, index)。 -
rowsInserted(const QModelIndex &parent, int start, int end):插入新行时触发(如添加一行数据)。
-
rowsRemoved(const QModelIndex &parent, int start, int end):删除行时触发。
-
layoutChanged():数据结构大幅变化时触发(如排序、过滤),强制视图重新加载所有数据。
五、自定义与扩展
1. 自定义数据角色
通过Qt::UserRole及更高值定义自定义角色,存储额外数据(如图标、提示信息):
// data()中处理自定义角色
if (role == Qt::UserRole) {
return m_icons[index.row()]; // 返回图标路径
}
// 视图或委托中通过data(index, Qt::UserRole)获取
2. 支持排序与过滤
可通过继承QSortFilterProxyModel实现排序和过滤,无需修改原模型:
QSortFilterProxyModel *proxyModel = new QSortFilterProxyModel(this);
proxyModel->setSourceModel(myModel); // 关联原模型
proxyModel->setSortCaseSensitivity(Qt::CaseInsensitive); // 不区分大小写排序
ui->tableView->setModel(proxyModel); // 视图绑定代理模型
3. 性能优化
-
对于大数据量,避免在
data()中执行耗时操作(如数据库查询),可缓存数据; -
树形结构中,
index()和parent()应快速计算(如用哈希表存储父节点指针); -
批量修改数据时,使用
beginInsertRows()、endInsertRows()包裹,避免多次发射信号。
六、示例:一个简单的表格模型
以下是一个继承QAbstractTableModel的表格模型实现,管理学生成绩数据:
#include <QAbstractTableModel>
#include <QVector>
#include <QStringList>
class StudentScoreModel : public QAbstractTableModel {
Q_OBJECT
public:
explicit StudentScoreModel(QObject *parent = nullptr) : QAbstractTableModel(parent) {
// 初始化表头
m_headers << "姓名" << "数学" << "语文";
// 初始化数据
m_data = {
{"张三", 90, 85},
{"李四", 88, 92},
{"王五", 95, 89}
};
}
// 必须实现的虚函数
int rowCount(const QModelIndex &parent = QModelIndex()) const override {
Q_UNUSED(parent);
return m_data.size();
}
int columnCount(const QModelIndex &parent = QModelIndex()) const override {
Q_UNUSED(parent);
return m_headers.size();
}
QVariant data(const QModelIndex &index, int role = Qt::DisplayRole) const override {
if (!index.isValid() || index.row() >= m_data.size()) return QVariant();
if (role == Qt::DisplayRole || role == Qt::EditRole) {
return m_data[index.row()][index.column()];
} else if (role == Qt::TextAlignmentRole) {
return Qt::AlignCenter;
}
return QVariant();
}
QVariant headerData(int section, Qt::Orientation orientation, int role) const override {
if (role != Qt::DisplayRole || orientation != Qt::Horizontal)
return QAbstractTableModel::headerData(section, orientation, role);
return m_headers[section];
}
// 支持编辑
bool setData(const QModelIndex &index, const QVariant &value, int role) override {
if (!index.isValid() || role != Qt::EditRole) return false;
int row = index.row();
int col = index.column();
if (col == 1 || col == 2) { // 数学/语文成绩
bool ok;
int score = value.toInt(&ok);
if (!ok || score < 0 || score > 100) return false; // 验证分数范围
}
m_data[row][col] = value;
emit dataChanged(index, index, {role});
return true;
}
Qt::ItemFlags flags(const QModelIndex &index) const override {
return QAbstractTableModel::flags(index) | Qt::ItemIsEditable;
}
private:
QVector<QVector<QVariant>> m_data; // 存储表格数据(行×列)
QStringList m_headers; // 表头
};
总结
设计Qt MVC模型的关键是:
-
根据数据结构选择合适的基类(如
QAbstractTableModel); -
正确实现
rowCount()、columnCount()、data()等核心虚函数; -
通过
setData()和flags()支持编辑; -
利用信号(如
dataChanged())通知视图更新; -
结合自定义角色、代理模型等扩展功能。
通过以上步骤,可设计出高效、灵活、易维护的Qt模型,适配各种数据展示与交互需求。
更多推荐



所有评论(0)