目录
前言
一、MVC简介
二、MVC架构
2.1 MVC层级关系图
2.2 MVC类结构图
三、模型/视图表格
3.1 标准表格模型
3.1.1 应用场景
3.1.2 数据过滤
3.1.3 QStandardItemModel类的API
3.1.4 示例代码
3.2 自定义表格模型
3.2.1 应用场景
3.2.2 QAbstractItemModel类的API
3.2.3 Qt::ItemDataRole数据角色
3.2.4 示例代码
四、模型/视图委托
4.1 与数据类型相关的编辑器
4.2 与数据类型相关的委托
4.2.1 只读的列/行委托
4.2.2 可编辑的列/行委托
4.2 与模型相关的委托
五、模型/视图中的视图
5.1 继承Qt标准模型的自定义视图
5.2 与特定模型相关的自定义视图
总结
周末在图书馆不经意间翻阅了《Qt高级编程》后就爱释手,作者Mark Summerfield高瞻远瞩、思路清晰,刚好满足我提升Qt知识的需要,于是借出此书,好好学习一番。
QT4.0版本引用了模型/视图架构,此框架实现了数据与表现层的分离,从而使同一个程序可以使用不同的表现形式。MVC拆分来讲,M是指业务模型(Model),V是指用户界面(View),C则是指控制器(Control),在应用中实际使用委托(Delegate)。
MVC框架主要有以下三点优势:
MVC框架也不是万能的,主要有以下两方面缺点:
模型用于存储数据项,Qt提供了五种标准视图(QListView、QTableView、QColumnView、QTreeView和QComboBox)来显示存储模型中的数据项。所有的标准视图都提供了一个默认的QStyleItemDelegate委托,用于显示视图中的各个项并为可编辑的项提供一个合适的编辑器。
模型、视图、委托及其后台数据集之间的关系,如下图所示:
Qt模型类的层次结构,如下图所示:
Qt标准视图类的层次结构,如下图所示:
Qt委托类的层次结构,如下图所示:
表格模型类似二维矩阵,按照行、列进行工作。每一项数据保存在QStandardItem对象中,它的父项都是一个无效的QModelIndex对象。
标准表格模型与自定义表格模型对比,标准表格模型有两个明显的缺点:
1、加载大数据集比自定义表格模型慢;
2、提供的API没有自定义表格模型丰富;
针对Qt中表现的数据而言,使用QStandardItemModel是最简单、便捷的方式,通过其类或子类来实现标准表格模型,QStandardItemModel类提供了操作表格数据及与视图交互的所有功能。例如,通过按钮事件来实现数据的加载、保存、添加行、删除行、上移行和下移行等操作。
使用QSortFilterProxyModel或派生类来操作视图的选集模型(selection model)实现数据过滤功能。QSortFilterProxyModel类的保护方法filterAcceptsRow、filterAcceptsColumn和lessThan分别实现过滤行、过滤列和排序操作。
基于Qt 5.15 LTS的QStandardItemModel类的API清单如下:
Functions | Description |
QStandardItemModel(int rows, int columns, QObject *parent = nullptr) | |
QStandardItemModel(QObject *parent = nullptr) | |
virtual | ~QStandardItemModel() |
void | appendColumn(const QList |
void | appendRow(const QList |
void | appendRow(QStandardItem *item) |
void | clear() |
bool | clearItemData(const QModelIndex &index) |
QList |
findItems(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly, int column = 0) const |
QStandardItem * | horizontalHeaderItem(int column) const |
QModelIndex | indexFromItem(const QStandardItem *item) const |
void | insertColumn(int column, const QList |
bool | insertColumn(int column, const QModelIndex &parent = QModelIndex()) |
void | insertRow(int row, const QList |
void | insertRow(int row, QStandardItem *item) |
bool | insertRow(int row, const QModelIndex &parent = QModelIndex()) |
QStandardItem * | invisibleRootItem() const |
QStandardItem * | item(int row, int column = 0) const |
QStandardItem * | itemFromIndex(const QModelIndex &index) const |
const QStandardItem * | itemPrototype() const |
void | setColumnCount(int columns) |
void | setHorizontalHeaderItem(int column, QStandardItem *item) |
void | setHorizontalHeaderLabels(const QStringList &labels) |
void | setItem(int row, int column, QStandardItem *item) |
void | setItem(int row, QStandardItem *item) |
void | setItemPrototype(const QStandardItem *item) |
void | setItemRoleNames(const QHash |
void | setRowCount(int rows) |
void | setSortRole(int role) |
void | setVerticalHeaderItem(int row, QStandardItem *item) |
void | setVerticalHeaderLabels(const QStringList &labels) |
int | sortRole() const |
QList |
takeColumn(int column) |
QStandardItem * | takeHorizontalHeaderItem(int column) |
QStandardItem * | takeItem(int row, int column = 0) |
QList |
takeRow(int row) |
QStandardItem * | takeVerticalHeaderItem(int row) |
QStandardItem * | verticalHeaderItem(int row) const |
zipcode1应用程序的主要功能是加载和保存邮编数据文件,并通过县、州或邮编范围来过滤邮编数据。程序设计方面,它使用了一个简单的QStandardItemModel派生类来加载和保存邮编二进制数据,数据显示使用的是QTableView控件,通过自定义的QSortFilterProxyModel子类填充数据模型来实现数据过滤/选择。程序运行效果如下:
zipcode1应用程序的源码下载地址:
链接:https://pan.baidu.com/s/1FiHqjR9M2miyz_tcZ0vfhQ
提取码:mdal
QStandardItemModel是为通用目的而设计, 针对特定的数据来说,QStandardItemModel类使用的QStandardItem对象需要消耗更多的内存资源且运行低效,自定义表格模型应运而生。
自定义表格模型的实现方案概括来说是创建一个QAbstractTableModel派生类,并实现其父类QAbstractItemModel某些特定的API函数,以使得自定义模型派生类的API与架构兼容。
Qt表格模型的每一项数据都有一个相关的模型索引(QModelIndex),每个项都有许多与之相关的数据元素(Qt::ItemDataRole)。如果提供给我们的模型索引是有效的,则可设置相应项的flags以使该项能够被选择和编辑。表格项的数据使用容器类来存储(QList或QMap),数据序列化可使用QDataStream::operator <<重载实现,数据反序列化可使用QDataStream::operator >>重载实现。
基于Qt 5.15 LTS的QAbstractItemModel类的API清单如下:
Functions | Description |
QAbstractItemModel(QObject *parent = nullptr) | |
virtual | ~QAbstractItemModel() |
virtual QModelIndex | buddy(const QModelIndex &index) const |
virtual bool | canDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const |
virtual bool | canFetchMore(const QModelIndex &parent) const |
bool | checkIndex(const QModelIndex &index, QAbstractItemModel::CheckIndexOptions options = CheckIndexOption::NoOption) const |
virtual int | columnCount(const QModelIndex &parent = QModelIndex()) const = 0 |
virtual QVariant | data(const QModelIndex &index, int role = Qt::DisplayRole) const = 0 |
virtual bool | dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) |
virtual void | fetchMore(const QModelIndex &parent) |
virtual Qt::ItemFlags | flags(const QModelIndex &index) const |
virtual bool | hasChildren(const QModelIndex &parent = QModelIndex()) const |
bool | hasIndex(int row, int column, const QModelIndex &parent = QModelIndex()) const |
virtual QVariant | headerData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const |
virtual QModelIndex | index(int row, int column, const QModelIndex &parent = QModelIndex()) const = 0 |
bool | insertColumn(int column, const QModelIndex &parent = QModelIndex()) |
virtual bool | insertColumns(int column, int count, const QModelIndex &parent = QModelIndex()) |
bool | insertRow(int row, const QModelIndex &parent = QModelIndex()) |
virtual bool | insertRows(int row, int count, const QModelIndex &parent = QModelIndex()) |
virtual QMap |
itemData(const QModelIndex &index) const |
virtual QModelIndexList | match(const QModelIndex &start, int role, const QVariant &value, int hits = 1, Qt::MatchFlags flags = Qt::MatchFlags(Qt::MatchStartsWith|Qt::MatchWrap)) const |
virtual QMimeData * | mimeData(const QModelIndexList &indexes) const |
virtual QStringList | mimeTypes() const |
bool | moveColumn(const QModelIndex &sourceParent, int sourceColumn, const QModelIndex &destinationParent, int destinationChild) |
virtual bool | moveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild) |
bool | moveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild) |
virtual bool | moveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild) |
virtual QModelIndex | parent(const QModelIndex &index) const = 0 |
bool | removeColumn(int column, const QModelIndex &parent = QModelIndex()) |
virtual bool | removeColumns(int column, int count, const QModelIndex &parent = QModelIndex()) |
bool | removeRow(int row, const QModelIndex &parent = QModelIndex()) |
virtual bool | removeRows(int row, int count, const QModelIndex &parent = QModelIndex()) |
virtual QHash |
roleNames() const |
virtual int | rowCount(const QModelIndex &parent = QModelIndex()) const = 0 |
virtual bool | setData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole) |
virtual bool | setHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole) |
virtual bool | setItemData(const QModelIndex &index, const QMap |
virtual QModelIndex | sibling(int row, int column, const QModelIndex &index) const |
virtual void | sort(int column, Qt::SortOrder order = Qt::AscendingOrder) |
virtual QSize | span(const QModelIndex &index) const |
virtual Qt::DropActions | supportedDragActions() const |
virtual Qt::DropActions | supportedDropActions() const |
Constant | Value | Description |
Qt::DisplayRole |
0 |
The key data to be rendered in the form of text. (QString) |
Qt::DecorationRole |
1 |
The data to be rendered as a decoration in the form of an icon. (QColor, QIcon or QPixmap) |
Qt::EditRole |
2 |
The data in a form suitable for editing in an editor. (QString) |
Qt::ToolTipRole |
3 |
The data displayed in the item's tooltip. (QString) |
Qt::StatusTipRole |
4 |
The data displayed in the status bar. (QString) |
Qt::WhatsThisRole |
5 |
The data displayed for the item in "What's This?" mode. (QString) |
Qt::SizeHintRole |
13 |
The size hint for the item that will be supplied to views. (QSize) |
Qt::FontRole |
6 |
The font used for items rendered with the default delegate. (QFont) |
Qt::TextAlignmentRole |
7 |
The alignment of the text for items rendered with the default delegate. (Qt::Alignment) |
Qt::BackgroundRole |
8 |
The background brush used for items rendered with the default delegate. (QBrush) |
Qt::BackgroundColorRole |
BackgroundRole |
This role is obsolete. Use BackgroundRole instead. |
Qt::ForegroundRole |
9 |
The foreground brush (text color, typically) used for items rendered with the default delegate. (QBrush) |
Qt::TextColorRole |
ForegroundRole |
This role is obsolete. Use ForegroundRole instead. |
Qt::CheckStateRole |
10 |
This role is used to obtain the checked state of an item. (Qt::CheckState) |
Qt::InitialSortOrderRole |
14 |
This role is used to obtain the initial sort order of a header view section. (Qt::SortOrder). This role was introduced in Qt 4.8. |
Qt::AccessibleTextRole |
11 |
The text to be used by accessibility extensions and plugins, such as screen readers. (QString) |
Qt::AccessibleDescriptionRole |
12 |
A description of the item for accessibility purposes. (QString) |
zipcode2使用一个自定义模型来代替zipcode1应用程序所使用的简单QStandardItemModel派生类,它们都有相同的外观和行为表现,应用程序的源码下载地址如下:
链接:https://pan.baidu.com/s/18ddcMcNbk2KeWOXuAzI6Mw
提取码:93we
我们通过委托对象,实现视图中显示项的外观控制和自定义的编辑器,目前有三种委托方式:Qt内置委托、自定义数据类型相关的委托和模型相关的委托。
Qt内置委托使用特定的窗口部件来对特定的数据类型进行编辑,QStyledItemDelegate是Qt默认内置委托,编辑器使用QItemEditorFactory类创建,在视图上编辑数据时,编辑器由委托创建和显示。它的作用域是全局的,将影响所有视图中相关数据类型的可编辑项。
实现与数据类型相关的编辑器,可分成三步:
1、创建一个QItemEditorFactgory对象;
2、注册一个特定窗口控件作为某些特定类型的编辑器;
3、所有委托关联到QItemEditorFactgory工厂对象;
// create a custom editor class
class MySpinBox : public QDoubleSpinBox
{
...
};
// create a factory object
QItemEditorFactory *pFactory = new QItemEditorFactory;
// registering specific data types for the editor
pFactory->registerEditor(QVariant::Int, new QStandardItemEditorCreator());
pFactory->registerEditor(QVariant::Double, new QStandardItemEditorCreator());
// set a new default factory
QItemEditorFactory::setDefaultFactory(factory);
示例“numbergrid”为特定数据类型的项注册了一个编辑器控件,它的使用方法可参考示例源码:
链接:https://pan.baidu.com/s/1OoIXDuhtAgdaMhKx6Alcwg
提取码:7ubf
为表格的多个特定行或列创建数据类型相关的委托,可以避免代码重复和提高代码利用性。例如,创建一个针对日期时间类型的委托,来为一个或几个日期列的模型设置这个列委托,还针对其它列创建其它数据类型相关的委托(颜色、角度、精度等)。
若要创建只读的列/行的委托来处理表格项的外观,只需要两步:
1、创建QStyledItemDelegate子类;
2、子类中重写paint()方法;
核心代码的使用示例如下:
// datetimedelegate.h
class DateTimeDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
explicit DateTimeDelegate(QObject *parent=0) : QStyledItemDelegate(parent) {}
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
private:
QRectF clockRect(const QRectF &rect, const qreal &diameter) const;
void drawClockFace(QPainter *painter, const QRectF &rect, const QDateTime &lastModified) const;
void drawClockHand(QPainter *painter, const QPointF ¢er, const qreal &length, const qreal °rees) const;
void drawDate(QPainter *painter, const QStyleOptionViewItem &option, const qreal &size, const QDateTime &lastModified) const;
};
// datetimedelegate.cpp
void DateTimeDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
const QFileSystemModel *model = qobject_cast(index.model());
Q_ASSERT(model);
const QDateTime &lastModified = model->lastModified(index);
painter->save();
painter->setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
if (option.state & QStyle::State_Selected) {
painter->fillRect(option.rect, option.palette.highlight());
}
const qreal diameter = qMin(option.rect.width(), option.rect.height());
const QRectF rect = clockRect(option.rect, diameter);
drawClockFace(painter, rect, lastModified);
drawClockHand(painter, rect.center(), diameter / 3.5, ((lastModified.time().hour() + (lastModified.time().minute() / 60.0))) * 30);
drawClockHand(painter, rect.center(), diameter / 2.5, lastModified.time().minute() * 6);
drawDate(painter, option, diameter, lastModified);
painter->restore();
}
示例“folderview”使用两个QTreeView控件,左边不使用委托,右边的QTreeView使用自定义的DateTimeDelegate委托,把时间数据显示为一个时钟,日期数据显示为ISO 8601格式的字符串。详细使用可参考示例源码:
链接:https://pan.baidu.com/s/133xxX1oF3VEw8SvIdJhP0w
提取码:ei65
待学习。
为了更灵活的应用委托,我们可以在需要时动态创建自定义的模型相关的委托,而不是提前创建一组通用的列/行数据相关的委托。
实现模型相关的委托,主要有以下两步:
1、创建QStyledItemDelegate子类;
创建QStyledItemDelegate子类后,在构造函数中调用基类构造函数,因为我们必须处理自己创建的窗口控件,而那些基类负责的窗口控件交由基类处理。
2、子类中重写paint()方法;
重写paint()方法是用于渲染项,实现自定义控件的绘制和数据多样化展示。
3、子类中重写createEditor()、setEditorData()和setModelData()方法;
在createEditor()方法中,判断某列使用哪一个编辑器,创建、设置并返回编辑器。一旦编辑器创建完毕,Qt模型/视图架构调用setEditorData()方法,在编辑器显示前给程序员一个填充编辑器数据的机会,数据填充项从QModelIndex项中获取。用户完成数据编辑后,架构会调用setModelData()方法来更新模型索引所关联的项的值。
核心代码的使用示例如下:
// step_delete.h
class StepDelegate : public QStyledItemDelegate
{
Q_OBJECT
public:
// The default constructor
explicit StepDelegate(QObject *pParent = 0);
// The default destructor
virtual ~StepDelegate();
// Custom rendering
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
// Reads data from the model and writes it to the editor widget
void setEditorData(QWidget *editor, const QModelIndex &index) const override;
// Create an editor to be used for editing the data item
QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override;
// Reads the contents of the editor widget and writes it to the model
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override;
}
// step_delegate.cpp
#include "step_delegate.h"
// add necessary includes here
#include
/*
* Function: StepDelegate
* Description: The default constructor
* Paramer: void
* Return:
*/
StepDelegate::StepDelegate(QObject *pParent) : QStyledItemDelegate(pParent)
{
}
/*
* Function: ~StepDelegate
* Description: The default destructor
* Paramer: void
* Return:
*/
StepDelegate::~StepDelegate()
{
}
/*
* Function: paint
* Description: Custom rendering
* Paramer: QPainter *painter - painter point
* const QStyleOptionViewItem &option - QStyleOptionViewItem object pointer
* const QModelIndex &index - model index
* Return: void
*/
void StepDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
if(!index.isValid()) return;
QStyleOptionViewItem viewOption(option);
initStyleOption(&viewOption, index);
if (option.state.testFlag(QStyle::State_HasFocus)) {
viewOption.state = viewOption.state ^ QStyle::State_HasFocus;
}
QStyledItemDelegate::paint(painter, viewOption, index);
}
/*
* Function: createEditor
* Description: Create an editor to be used for editing the data item
* Paramer: QWidget *parent - the editor widget
* const QStyleOptionViewItem &option - QStyleOptionViewItem object pointer
* const QModelIndex &index - model index
* Return: QWidget - the editor pointer
*/
QWidget* StepDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
{
return QStyledItemDelegate::createEditor(parent, option, index);
}
/*
* Function: setEditorData
* Description: Reads data from the model and writes it to the editor widget
* Paramer: QWidget *editor - the editor widget
* const QModelIndex &index - model index
* Return: void
*/
void StepDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
{
QStyledItemDelegate::setEditorData(editor, index);
}
/*
* Function: setModelData
* Description: Reads the contents of the editor widget and writes it to the model
* Paramer: QWidget *editor - the editor widget
* QAbstractItemModel *model - QAbstractItemModel object pointer
* const QModelIndex &index - model index
* Return: void
*/
void StepDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
{
QStyledItemDelegate::setModelData(editor, model, index);
}
示例“zipcodes1”使用了与模型相关的委托,详细使用方法可参考示例:
链接:https://pan.baidu.com/s/1FiHqjR9M2miyz_tcZ0vfhQ
提取码:mdal
Qt中模型视图分两类,一类是Qt标准模型视图(例如:QListView、QColumnView、QTableView和QTreeView)基本能满足日常使用需要;另一类是自定义视图,它可以呈现与Qt标准视图不一样的数据显示方式。
本章节主要讲解自定义视图的实现方式,主要有两种实现方式:
1、不同模型间可复用,并且与Qt的模型/视图架构相适应;
2、不同模型间不能复用,以独特的方式显示特定模型中的数据;
此类视图继承QAbstractItemView类,提供了与Qt内置视图相同的API,支持模型间的复用。 作者编写了“tiledlistview”示例,用于展示平铺的列表视图。窗口分成左右两部分区域,左边是一个标准的QListView,右边是一个继承QAbstractItemView类的自定义视图(TiledListView类),它们使用同一个数据模型,两个窗口大小和字体相同,但自定义视图没有使用多个列,却可以每行显示尽可能多的项,效果图如下:
视图类TiledListView继承QAbstractItemView,实现了QAbstractItemView类部分API,其类如下:
// tiledlistview.h
#include
#include
#include
class TiledListView : public QAbstractItemView
{
Q_OBJECT
public:
explicit TiledListView(QWidget *parent=0);
void setModel(QAbstractItemModel *model);
QRect visualRect(const QModelIndex &index) const;
void scrollTo(const QModelIndex &index, QAbstractItemView::ScrollHint);
QModelIndex indexAt(const QPoint &point_) const;
protected slots:
void dataChanged(const QModelIndex &topLeft, const QModelIndex &bottomRight);
void rowsInserted(const QModelIndex &parent, int start, int end);
void rowsAboutToBeRemoved(const QModelIndex &parent, int start, int end);
void updateGeometries();
protected:
QModelIndex moveCursor(QAbstractItemView::CursorAction cursorAction, Qt::KeyboardModifiers modifiers);
bool isIndexHidden(const QModelIndex&) const { return false; }
int horizontalOffset() const;
int verticalOffset() const;
void scrollContentsBy(int dx, int dy);
void setSelection(const QRect &rect, QFlags flags);
QRegion visualRegionForSelection( const QItemSelection &selection) const;
void paintEvent(QPaintEvent*);
void resizeEvent(QResizeEvent*);
void mousePressEvent(QMouseEvent *event);
private:
void calculateRectsIfNecessary() const;
QRectF viewportRectForRow(int row) const;
void paintOutline(QPainter *painter, const QRectF &rectangle);
mutable int idealWidth;
mutable int idealHeight;
mutable QHash rectForRow;
mutable bool hashIsDirty;
};
成员变量都与后台的状态记录相关联,且它们都是在const方法中使用,所以在它们的前面添加了mutable关键字。值得注意的是变量rectForRow,它是一个哈希表,缓存列表中指定行的矩形区域,如果视图调整了尺寸和大小,则需要重新计算哈希表内所有项的矩形区域并刷新。
void TiledListView::calculateRectsIfNecessary() const
{
if (!hashIsDirty)
return;
const int ExtraWidth = 10;
QFontMetrics fm(font());
const int RowHeight = fm.height() + ExtraHeight;
const int MaxWidth = viewport()->width();
int minimumWidth = 0;
int x = 0;
int y = 0;
for (int row = 0; row < model()->rowCount(rootIndex()); ++row) {
QModelIndex index = model()->index(row, 0, rootIndex());
QString text = model()->data(index).toString();
int textWidth = fm.width(text);
if (!(x == 0 || x + textWidth + ExtraWidth < MaxWidth)) {
y += RowHeight;
x = 0;
}
else if (x != 0)
x += ExtraWidth;
rectForRow[row] = QRectF(x, y, textWidth + ExtraWidth,
RowHeight);
if (textWidth > minimumWidth)
minimumWidth = textWidth;
x += textWidth;
}
idealWidth = minimumWidth + ExtraWidth;
idealHeight = y + RowHeight;
hashIsDirty = false;
viewport()->update();
}
calculateRectsIfNecessary函数是此应用程序的核心方法,它遍历模型中的每一项,获取项的文本内容,并计算出该项的矩形区域信息。然后,把行号作为键,矩形区域信息作为值填充到哈希表中并更新视图。
tiledlistview源码下载地址:
链接:https://pan.baidu.com/s/1hHbNfVN9C6b1-HwWucGKxw
提取码:h97m
此类视图直接继承QWidget,提供自己特定的API且包含一个setModel()方法,它只能用于特定模型,不支持复用。
作者创建了一个可视化部件用于显示一个人口普查资料表,数据是一个表格模型,逐行显示年份、男性人口数量、女性人口数量和人口总数。窗口中有两个数据视图,左边是一个标准的QTableView,右边是一个CensusVisualizer视图,它将男、女人口按比例使用渐变色来表示,效果图如下:
censusvisualizer源码下载地址:
链接:https://pan.baidu.com/s/1Oy6QBv9PK6GpDdgLXC_ciQ
提取码:2rqk
Qt的MVC架构有助于界面和业务对象的分离,一定程序上降低了应用程序的复杂度,增加了代码的灵活性和可重用性,是一个优秀程序员的必备知识点。本人对于MVC架构学习的也不是很深入,只能在实践中不断运用、总结和提升。