周末在图书馆不经意间翻阅了《Qt高级编程》后就爱释手,作者Mark Summerfield高瞻远瞩、思路清晰,刚好满足我提升Qt知识的需要,于是借出此书,好好学习一番。


一、MVC简介

        QT4.0版本引用了模型/视图架构,此框架实现了数据与表现层的分离,从而使同一个程序可以使用不同的表现形式。MVC拆分来讲,M是指业务模型(Model),V是指用户界面(View),C则是指控制器(Control),在应用中实际使用委托(Delegate)。

MVC框架主要有以下三点优势:

  1. 耦合性低:由于模型与控制器和视图相分离,业务规则改变后只需要改动MVC的模型层,同样,更改视图层代码而不用重新编译模型和控制器代码。
  2. 重用性高:由于多个视图能共享一个模型,所以允许使用不同样式的视图来访问同一个服务器端的代码。
  3. 操作简单:开发和维护用户接口的技术含量降低,后台程序员集中精力于业务逻辑,界面程序员集中精力于表现形式上。

MVC框架也不是万能的,主要有以下两方面缺点:

  1. 复杂度高:严格遵循MVC会增加代码结构的复杂性,降低运行效率。
  2. 调试复杂:因为模型和视图要严格的分离,每个构件在使用之前都需要经过彻底的测试,这会给调试应用程序带来了一定的困难。

二、MVC架构

        模型用于存储数据项,Qt提供了五种标准视图(QListView、QTableView、QColumnView、QTreeView和QComboBox)来显示存储模型中的数据项。所有的标准视图都提供了一个默认的QStyleItemDelegate委托,用于显示视图中的各个项并为可编辑的项提供一个合适的编辑器。

2.1 MVC层级关系图

模型、视图、委托及其后台数据集之间的关系,如下图所示:

2.2 MVC类结构图

Qt模型类的层次结构,如下图所示:

Qt标准视图类的层次结构,如下图所示: 

  Qt委托类的层次结构,如下图所示:

三、模型/视图表格

        表格模型类似二维矩阵,按照行、列进行工作。每一项数据保存在QStandardItem对象中,它的父项都是一个无效的QModelIndex对象。

标准表格模型与自定义表格模型对比,标准表格模型有两个明显的缺点:

1、加载大数据集比自定义表格模型慢;

2、提供的API没有自定义表格模型丰富;

3.1 标准表格模型

3.1.1 应用场景

        针对Qt中表现的数据而言,使用QStandardItemModel是最简单、便捷的方式,通过其类或子类来实现标准表格模型,QStandardItemModel类提供了操作表格数据及与视图交互的所有功能。例如,通过按钮事件来实现数据的加载、保存、添加行、删除行、上移行和下移行等操作。

3.1.2 数据过滤

        使用QSortFilterProxyModel或派生类来操作视图的选集模型(selection model)实现数据过滤功能。QSortFilterProxyModel类的保护方法filterAcceptsRow、filterAcceptsColumn和lessThan分别实现过滤行、过滤列和排序操作。

3.1.3 QStandardItemModel类的API

        基于Qt 5.15 LTS的QStandardItemModel类的API清单如下

FunctionsDescription
QStandardItemModel(int rows, int columns, QObject *parent = nullptr)
QStandardItemModel(QObject *parent = nullptr)
virtual~QStandardItemModel()
voidappendColumn(const QList<QStandardItem *> &items)
voidappendRow(const QList<QStandardItem *> &items)
voidappendRow(QStandardItem *item)
voidclear()
boolclearItemData(const QModelIndex &index)
QList<QStandardItem *>findItems(const QString &text, Qt::MatchFlags flags = Qt::MatchExactly, int column = 0) const
QStandardItem *horizontalHeaderItem(int column) const
QModelIndexindexFromItem(const QStandardItem *item) const
voidinsertColumn(int column, const QList<QStandardItem *> &items)
boolinsertColumn(int column, const QModelIndex &parent = QModelIndex())
voidinsertRow(int row, const QList<QStandardItem *> &items)
voidinsertRow(int row, QStandardItem *item)
boolinsertRow(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
voidsetColumnCount(int columns)
voidsetHorizontalHeaderItem(int column, QStandardItem *item)
voidsetHorizontalHeaderLabels(const QStringList &labels)
voidsetItem(int row, int column, QStandardItem *item)
voidsetItem(int row, QStandardItem *item)
voidsetItemPrototype(const QStandardItem *item)
voidsetItemRoleNames(const QHash<int, QByteArray> &roleNames)
voidsetRowCount(int rows)
voidsetSortRole(int role)
voidsetVerticalHeaderItem(int row, QStandardItem *item)
voidsetVerticalHeaderLabels(const QStringList &labels)
intsortRole() const
QList<QStandardItem *>takeColumn(int column)
QStandardItem *takeHorizontalHeaderItem(int column)
QStandardItem *takeItem(int row, int column = 0)
QList<QStandardItem *>takeRow(int row)
QStandardItem *takeVerticalHeaderItem(int row)
QStandardItem *verticalHeaderItem(int row) const

3.1.4 示例代码

        zipcode1应用程序的主要功能是加载和保存邮编数据文件,并通过县、州或邮编范围来过滤邮编数据。程序设计方面,它使用了一个简单的QStandardItemModel派生类来加载和保存邮编二进制数据,数据显示使用的是QTableView控件,通过自定义的QSortFilterProxyModel子类填充数据模型来实现数据过滤/选择。程序运行效果如下:

zipcode1应用程序的源码下载地址:

链接:https://pan.baidu.com/s/1FiHqjR9M2miyz_tcZ0vfhQ
提取码:mdal

3.2 自定义表格模型

3.2.1 应用场景

        QStandardItemModel是为通用目的而设计, 针对特定的数据来说,QStandardItemModel类使用的QStandardItem对象需要消耗更多的内存资源且运行低效,自定义表格模型应运而生。

        自定义表格模型的实现方案概括来说是创建一个QAbstractTableModel派生类,并实现其父类QAbstractItemModel某些特定的API函数,以使得自定义模型派生类的API与架构兼容。

        Qt表格模型的每一项数据都有一个相关的模型索引(QModelIndex),每个项都有许多与之相关的数据元素(Qt::ItemDataRole)。如果提供给我们的模型索引是有效的,则可设置相应项的flags以使该项能够被选择和编辑。表格项的数据使用容器类来存储(QList或QMap),数据序列化可使用QDataStream::operator <<重载实现,数据反序列化可使用QDataStream::operator >>重载实现。

3.2.2 QAbstractItemModel类的API

        基于Qt 5.15 LTS的QAbstractItemModel类的API清单如下

FunctionsDescription
QAbstractItemModel(QObject *parent = nullptr)
virtual~QAbstractItemModel()
virtual QModelIndexbuddy(const QModelIndex &index) const
virtual boolcanDropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) const
virtual boolcanFetchMore(const QModelIndex &parent) const
boolcheckIndex(const QModelIndex &index, QAbstractItemModel::CheckIndexOptions options = CheckIndexOption::NoOption) const
virtual intcolumnCount(const QModelIndex &parent = QModelIndex()) const = 0
virtual QVariantdata(const QModelIndex &index, int role = Qt::DisplayRole) const = 0
virtual booldropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent)
virtual voidfetchMore(const QModelIndex &parent)
virtual Qt::ItemFlagsflags(const QModelIndex &index) const
virtual boolhasChildren(const QModelIndex &parent = QModelIndex()) const
boolhasIndex(int row, int column, const QModelIndex &parent = QModelIndex()) const
virtual QVariantheaderData(int section, Qt::Orientation orientation, int role = Qt::DisplayRole) const
virtual QModelIndexindex(int row, int column, const QModelIndex &parent = QModelIndex()) const = 0
boolinsertColumn(int column, const QModelIndex &parent = QModelIndex())
virtual boolinsertColumns(int column, int count, const QModelIndex &parent = QModelIndex())
boolinsertRow(int row, const QModelIndex &parent = QModelIndex())
virtual boolinsertRows(int row, int count, const QModelIndex &parent = QModelIndex())
virtual QMap<int, QVariant>itemData(const QModelIndex &index) const
virtual QModelIndexListmatch(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 QStringListmimeTypes() const
boolmoveColumn(const QModelIndex &sourceParent, int sourceColumn, const QModelIndex &destinationParent, int destinationChild)
virtual boolmoveColumns(const QModelIndex &sourceParent, int sourceColumn, int count, const QModelIndex &destinationParent, int destinationChild)
boolmoveRow(const QModelIndex &sourceParent, int sourceRow, const QModelIndex &destinationParent, int destinationChild)
virtual boolmoveRows(const QModelIndex &sourceParent, int sourceRow, int count, const QModelIndex &destinationParent, int destinationChild)
virtual QModelIndexparent(const QModelIndex &index) const = 0
boolremoveColumn(int column, const QModelIndex &parent = QModelIndex())
virtual boolremoveColumns(int column, int count, const QModelIndex &parent = QModelIndex())
boolremoveRow(int row, const QModelIndex &parent = QModelIndex())
virtual boolremoveRows(int row, int count, const QModelIndex &parent = QModelIndex())
virtual QHash<int, QByteArray>roleNames() const
virtual introwCount(const QModelIndex &parent = QModelIndex()) const = 0
virtual boolsetData(const QModelIndex &index, const QVariant &value, int role = Qt::EditRole)
virtual boolsetHeaderData(int section, Qt::Orientation orientation, const QVariant &value, int role = Qt::EditRole)
virtual boolsetItemData(const QModelIndex &index, const QMap<int, QVariant> &roles)
virtual QModelIndexsibling(int row, int column, const QModelIndex &index) const
virtual voidsort(int column, Qt::SortOrder order = Qt::AscendingOrder)
virtual QSizespan(const QModelIndex &index) const
virtual Qt::DropActionssupportedDragActions() const
virtual Qt::DropActionssupportedDropActions() const

3.2.3 Qt::ItemDataRole数据角色

ConstantValueDescription
Qt::DisplayRole0The key data to be rendered in the form of text. (QString)
Qt::DecorationRole1The data to be rendered as a decoration in the form of an icon. (QColor, QIcon or QPixmap)
Qt::EditRole2The data in a form suitable for editing in an editor. (QString)
Qt::ToolTipRole3The data displayed in the item's tooltip. (QString)
Qt::StatusTipRole4The data displayed in the status bar. (QString)
Qt::WhatsThisRole5The data displayed for the item in "What's This?" mode. (QString)
Qt::SizeHintRole13The size hint for the item that will be supplied to views. (QSize)
Qt::FontRole6The font used for items rendered with the default delegate. (QFont)
Qt::TextAlignmentRole7The alignment of the text for items rendered with the default delegate. (Qt::Alignment)
Qt::BackgroundRole8The background brush used for items rendered with the default delegate. (QBrush)
Qt::BackgroundColorRoleBackgroundRoleThis role is obsolete. Use BackgroundRole instead.
Qt::ForegroundRole9The foreground brush (text color, typically) used for items rendered with the default delegate. (QBrush)
Qt::TextColorRoleForegroundRoleThis role is obsolete. Use ForegroundRole instead.
Qt::CheckStateRole10This role is used to obtain the checked state of an item. (Qt::CheckState)
Qt::InitialSortOrderRole14This 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::AccessibleTextRole11The text to be used by accessibility extensions and plugins, such as screen readers. (QString)
Qt::AccessibleDescriptionRole12A description of the item for accessibility purposes. (QString)

3.2.4 示例代码

zipcode2使用一个自定义模型来代替zipcode1应用程序所使用的简单QStandardItemModel派生类,它们都有相同的外观和行为表现,应用程序的源码下载地址如下:

链接:https://pan.baidu.com/s/18ddcMcNbk2KeWOXuAzI6Mw
提取码:93we

四、模型/视图委托

        我们通过委托对象,实现视图中显示项的外观控制和自定义的编辑器,目前有三种委托方式:Qt内置委托、自定义数据类型相关的委托和模型相关的委托。

4.1 与数据类型相关的编辑器

        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<MySpinBox>());
pFactory->registerEditor(QVariant::Double, new QStandardItemEditorCreator<MySpinBox>());

// set a new default factory
QItemEditorFactory::setDefaultFactory(factory);

示例“numbergrid”为特定数据类型的项注册了一个编辑器控件,它的使用方法可参考示例源码:

链接:https://pan.baidu.com/s/1OoIXDuhtAgdaMhKx6Alcwg
提取码:7ubf

4.2 与数据类型相关的委托

         为表格的多个特定行或列创建数据类型相关的委托,可以避免代码重复和提高代码利用性。例如,创建一个针对日期时间类型的委托,来为一个或几个日期列的模型设置这个列委托,还针对其它列创建其它数据类型相关的委托(颜色、角度、精度等)。

4.2.1 只读的列/行委托

       若要创建只读的列/行的委托来处理表格项的外观,只需要两步:

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 &center, const qreal &length, const qreal &degrees) 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<const QFileSystemModel*>(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

4.2.2 可编辑的列/行委托

        待学习。

4.2 与模型相关的委托

        为了更灵活的应用委托,我们可以在需要时动态创建自定义的模型相关的委托,而不是提前创建一组通用的列/行数据相关的委托。

实现模型相关的委托,主要有以下两步:

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 <QtDebug>

/*
 * 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、不同模型间不能复用,以独特的方式显示特定模型中的数据;

5.1 继承Qt标准模型的自定义视图

        此类视图继承QAbstractItemView类,提供了与Qt内置视图相同的API,支持模型间的复用。 作者编写了“tiledlistview”示例,用于展示平铺的列表视图。窗口分成左右两部分区域,左边是一个标准的QListView,右边是一个继承QAbstractItemView类的自定义视图(TiledListView类),它们使用同一个数据模型,两个窗口大小和字体相同,但自定义视图没有使用多个列,却可以每行显示尽可能多的项,效果图如下:

视图类TiledListView继承QAbstractItemView,实现了QAbstractItemView类部分API,其类如下:

// tiledlistview.h

#include <QAbstractItemView>
#include <QHash>
#include <QRectF>

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<QItemSelectionModel::SelectionFlag> 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<int, QRectF> 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

5.2 与特定模型相关的自定义视图

        此类视图直接继承QWidget,提供自己特定的API且包含一个setModel()方法,它只能用于特定模型,不支持复用。

        作者创建了一个可视化部件用于显示一个人口普查资料表,数据是一个表格模型,逐行显示年份、男性人口数量、女性人口数量和人口总数。窗口中有两个数据视图,左边是一个标准的QTableView,右边是一个CensusVisualizer视图,它将男、女人口按比例使用渐变色来表示,效果图如下:

 censusvisualizer源码下载地址:

链接:https://pan.baidu.com/s/1Oy6QBv9PK6GpDdgLXC_ciQ
提取码:2rqk


总结

         Qt的MVC架构有助于界面和业务对象的分离,一定程序上降低了应用程序的复杂度,增加了代码的灵活性和可重用性,是一个优秀程序员的必备知识点。本人对于MVC架构学习的也不是很深入,只能在实践中不断运用、总结和提升。

Logo

开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!

更多推荐