qt5-入门-ModelView架构3-自定义Model(只读)

参考:
Qt 自定义 model 之一_w3cschool
https://www.w3cschool.cn/learnroadqt/xmvq1j4q.html

C++ GUI Programming with Qt 4, Second Edition

本地环境:
win10专业版,64位,Qt 5.12


理论基础

model-view架构

一个model中每个数据都有一个model索引(QModelIndex),这个索引指明了这个数据在model中的位置,比如行、列等。每个数据还要有一组属性值,称为角色roles。每个数据项都可以有多个角色,用于指定不同的数据操作。

比如Qt::DisplayRole是其中一个用于获取显示文本的角色。

当视图需要展示模型中的数据时,会通过数据模型的data()函数来获取数据项的值。在调用data()函数时,可以通过第二个参数指定需要获取的数据项的角色。如果角色指定为Qt::DisplayRole,那么返回的值是用于显示的文本。

对于list model,定位其中一个数据只要使用QModelIndex::row()即可;对于table model,除了行号还需要QModelIndex::column();对于tree model,需要用到这个元素的父节点QModelIndex::parent()。但对于所有model,都有自己的角色数据和索引,索引中也都有父节点(list和table model的元素的父节点都是非法的一个索引)。

自定义model

自定义model主要是为了只使用最相关的特性,抛弃不需要的,提升效率。同时可以做一些自定义的操作。

Qt提供了QAbstractListModelQAbstractTableModel两类,前者是一维数据 model,后者是二维数据 model。如果数据很复杂,那么可以直接继承QAbstractItemModel(是前两个的父类,继承自QObject)。如果继承了QAbstractTableModel,那可以只重写几个函数,就完成了model定义。

效果

下面是一个汇率对照表格。假设有5种货币,形成下图所示的表格时,并不需要储存5x5个数据(如果有更多种货币,数据量会变大)。这里设计一个新的模型,只存储一种货币与其他货币的汇率,剩余货币之间的转换让模型自动计算。
在这里插入图片描述
头文件:

#ifndef CURRENCYMODEL_H
#define CURRENCYMODEL_H

#include <QAbstractTableModel>

class CurrencyModel : public QAbstractTableModel {
public:
        CurrencyModel(QObject *parent = 0);
        void setCurrencyMap(const QMap<QString, double> &map);

        // 返回行数
        int rowCount(const QModelIndex &parent) const;

        // 返回列数
        int columnCount(const QModelIndex &parent) const;

        // 返回单元格数据
        // QVariant类似java里的Object,可以封装大多数Qt的数据类型
        // 封装是因为table model里可能放很多种类型,取出的时候用QVariant::toInt()之类的提取就行
        // 放入和取出的类型要一致
        // QVariant也表示在不同的条件下,return的类型可以不一样,只要能被QVariant包裹即可
        QVariant data(const QModelIndex &index, int role) const;

        // 返回列名或者行名
        QVariant headerData(int section, Qt::Orientation orientation, int role) const;
private:
        // 返回偏移量为offset的key
        QString currencyAt(int offset) const;

        QMap<QString, double> currencyMap;
};


#endif // CURRENCYMODEL_H

源文件:

#include "currencymodel.h"

CurrencyModel::CurrencyModel(QObject *parent)
        : QAbstractTableModel(parent)
{
    // 调用父类构造函数即可
}

int CurrencyModel::rowCount(const QModelIndex & parent) const
{
    // 因为使用的是一维的map,所以返回个数就是行数、列数
    return currencyMap.count();
}

int CurrencyModel::columnCount(const QModelIndex & parent) const
{
    return currencyMap.count();
}

QVariant CurrencyModel::data(const QModelIndex &index, int role) const
{
    // 如果index不合法就返回空的QVariant
    if (!index.isValid())
        return QVariant();

    if (role == Qt::TextAlignmentRole) {
        return int(Qt::AlignRight | Qt::AlignVCenter);
    } else if (role == Qt::DisplayRole) {
        QString rowCurrency = currencyAt(index.row());
        QString columnCurrency = currencyAt(index.column());
        if (currencyMap.value(rowCurrency) == 0.0)
            return "####";
        double amount = currencyMap.value(columnCurrency) / currencyMap.value(rowCurrency);
        // arg: 第一个参数:要填充的变量
        // 第二个参数:要填充的宽度。默认是0,表示不填充
        // 第三个参数:使用浮点数格式插入
        // 第四个参数:小数点后保留4位
        return QString("%1").arg(amount, 0, 'f', 4);
    }
    return QVariant();
}

QVariant CurrencyModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if (role != Qt::DisplayRole)
        return QVariant();
    return currencyAt(section);
}

void CurrencyModel::setCurrencyMap(const QMap<QString, double> &map)
{
    beginResetModel();
    currencyMap = map;
    // 此函数在qt5中已经作废,需要使用begin和end通知视图刷新数据
    //reset();
    endResetModel();
}

QString CurrencyModel::currencyAt(int offset) const
{
    return (currencyMap.begin() + offset).key();
}

main中:

QMap<QString, double> data;
data["NOK"] = 1.0000;
data["NZD"] = 0.2254;
data["SEK"] = 1.1991;
data["SGD"] = 0.2592;
data["USD"] = 0.1534;
CurrencyModel *model = new CurrencyModel;
model->setCurrencyMap(data);

QTableView *view = new QTableView;
view->setModel(model);
view->resize(400, 300);
view->show();
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值