前言
用PyQt写工具,初期用QListWidget
很方便。但随着数据属性增多,界面和数据逻辑混在一起,维护变得困难。是时候重新审视并实践Qt的Model/View/Controller(MVC)架构了。本文旨在梳理这一模式,搞清楚其核心思想。
MVC:数据与界面的解耦
MVC的核心是分工,将应用程序拆分为三个独立但协作的部分。
1. Model(模型):数据的容器和管理者
- 是什么:模型是数据的抽象。它不关心数据如何显示,只负责存储、管理数据,并提供统一的接口进行增删改查。
- 核心职责:
- 持有数据。
- 实现数据操作逻辑(如排序、过滤)。
- 当数据变化时,发出信号(
dataChanged
)通知外界。
- 在Qt中:通常继承自
QAbstractItemModel
或其子类。模型对视图一无所知,这是解耦的关键。
2. View(视图):数据的展示器
- 是什么:视图是模型数据的可视化呈现。
- 核心职责:
- 向模型请求数据(通过
data()
方法)。 - 将获取的数据以特定形式(列表、表格、树)绘制在界面上。
- 监听模型的信号,在数据变化时自动刷新显示。
- 向模型请求数据(通过
- 在Qt中:
QListView
,QTableView
,QTreeView
等。视图本身不存储任何数据。
3. Controller/Delegate(控制器/代理):连接桥梁
在Qt的Model/View架构中,传统的“Controller”角色被分散了。
- 用户交互(Controller):主要通过信号与槽机制实现。例如,点击视图中的一个按钮,视图发出信号,应用程序的某个槽函数接收信号并调用模型的方法去修改数据。这个连接过程就是控制逻辑。
- 渲染与编辑(Delegate):这是Qt M/V的精髓。代理(Delegate)负责两件事:
- 如何渲染:默认情况下,视图只是把模型返回的文本画出来。如果你想画一个进度条、一个下拉框或者一张图片,就需要自定义代理。
- 如何编辑:当用户双击一个项时,代理会创建一个编辑器(如
QLineEdit
)让用户修改数据,修改完成后再将数据写回模型。
这种分离使得你可以用一个模型,搭配多个不同的视图和代理,实现多样化的展示和交互,而无需改动数据本身。
QListWidget 与 QListView:从便利到灵活
这两者都用来显示列表,但定位不同。
QListWidget:一体化便利组件
- 特点:它是一个高级封装,内部自带一个简易模型。你直接操作的是
QListWidgetItem
,而不是数据本身。 - 优点:简单、直接、快速上手。
- 缺点:数据和视图强耦合。数据存储在
QListWidgetItem
对象中,不方便进行复杂的数据操作(如排序、过滤、数据库同步),性能在大量数据下也较差。
QListView:纯粹的视图
- 特点:它只是一个“框”,本身不存储任何数据。必须给它设置一个模型(Model)才能工作。
- 优点:
- 彻底解耦:数据逻辑在模型中,显示逻辑在视图中。
- 高性能:模型可以根据视图的需要按需加载数据,轻松应对百万级数据量。
- 灵活性:可搭配任何
QAbstractItemModel
子类,实现复杂的数据结构和展示。
何时选择
场景 | 推荐使用 | 原因 |
---|---|---|
简单的设置项、日志显示 | QListWidget | 快速开发,代码量少 |
大量数据、动态更新 | QListView + Model | 高性能、易维护 |
数据源是数据库、文件系统 | QListView + Model | 模型负责与数据源交互,逻辑清晰 |
需要自定义显示样式(如图文混排) | QListView + Delegate | 强大的自定义能力 |
常用数据模型:开箱即用的工具箱
Qt提供了一些预置的模型,可以直接使用。
1. QStandardItemModel
- 定位:通用型模型。
- 结构:基于项(
QStandardItem
),每个项都可以独立设置文本、图标、复选框等。可以轻松构建列表、表格和树状结构。 - 适用:需要灵活控制每个单元格内容,但数据结构不复杂的场景。
2. QStringListModel
- 定位:字符串列表专用。
- 结构:直接包装一个
QStringList
。 - 适用:最简单的场景,只显示一个字符串列表。
3. QSqlQueryModel
- 定位:数据库查询结果模型。
- 结构:执行一个SQL查询,并将结果集作为模型数据。通常是只读的。
- 适用:直接在
QTableView
中展示数据库查询结果。
4. 终极方案:自定义模型
当预置模型无法满足需求时(例如,你的数据源是一个复杂的C++对象列表,或者需要极致的性能优化),就需要自定义模型。
- 方法:继承
QAbstractListModel
(用于列表)或QAbstractTableModel
(用于表格)。 - 核心:至少需要重写以下几个关键的纯虚函数:
rowCount()
:返回数据行数。columnCount()
:返回数据列数(列表模型通常为1)。data(index, role)
:视图通过此函数获取数据。index
指定位置,role
指定需要的数据类型(如Qt.DisplayRole
表示文本,Qt.DecorationRole
表示图标)。flags(index)
:返回项的标志,如是否可选、可编辑等。
自定义模型虽然前期工作量稍大,但提供了对数据操作的最终控制权。
总结
QListWidget
是便利工具,适用于简单场景。- 当数据和展示需要分离时,转向Model/View架构是必然选择。
- Model管数据,View管展示,Delegate管渲染和编辑,信号/槽管交互。各司其职,代码清晰,易于扩展。
- 根据数据源和复杂度,选择合适的预置模型或直接自定义模型,是高效开发的关键。
发表回复