Qt 学习之路 2(50):自定义可编辑模型
Qt 学习之路 2(50):自定义可编辑模型
上一章我们了解了如何自定义只读模型。顾名思义,只读模型只能够用于展示只读数据,用户不能对其进行修改。如果允许用户修改数据,则应该提供可编辑的模型。可编辑模型与只读模型非常相似,至少在展示数据方面几乎是完全一样的,所不同的是可编辑模型需要提供用户编辑数据后,应当如何将数据保存到实际存储值中。
我们还是利用上一章的CurrencyModel,在此基础上进行修改。相同的代码这里不再赘述,我们只列出增加以及修改的代码。相比只读模型,可编辑模型需要增加以下两个函数的实现:
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole);
|
1
2
3
|
Qt::ItemFlags flags(const QModelIndex &index) const;
bool setData(const QModelIndex &index, const QVariant &value,
int role = Qt::EditRole);
|
还记得之前我们曾经介绍过,在 Qt 的 model/view 模型中,我们使用委托 delegate 来实现数据的编辑。在实际创建编辑器之前,委托需要检测这个数据项是不是允许编辑。模型必须让委托知道这一点,这是通过返回模型中每个数据项的标记 flag 来实现的,也就是这个 flags() 函数。这本例中,只有行和列的索引不一致的时候,我们才允许修改(因为对角线上面的值恒为 1.0000,不应该对其进行修改):
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.row() != index.column()) {
flags |= Qt::ItemIsEditable;
}
return flags;
}
|
1
2
3
4
5
6
7
8
|
Qt::ItemFlags CurrencyModel::flags(const QModelIndex &index) const
{
Qt::ItemFlags flags = QAbstractItemModel::flags(index);
if (index.row() != index.column()) {
flags |= Qt::ItemIsEditable;
}
return flags;
}
|
注意,我们并不是在判断了index.row() != index.column()之后直接返回Qt::ItemIsEditable,而是返回QAbstractItemModel::flags(index) | Qt::ItemIsEditable。这是因为我们不希望丢弃原来已经存在的那些标记。
我们不需要知道在实际编辑的过程中,委托究竟做了什么,只需要提供一种方式,告诉 Qt 如何将委托获得的用户输入的新的数据保存到模型中。这一步骤是通过setData()函数实现的:
const QVariant &value, int role)
{
if (index.isValid()
&& index.row() != index.column()
&& role == Qt::EditRole) {
QString columnCurrency = headerData(index.column(),
Qt::Horizontal, Qt::DisplayRole)
.toString();
QString rowCurrency = headerData(index.row(),
Qt::Vertical, Qt::DisplayRole)
.toString();
currencyMap.insert(columnCurrency,
value.toDouble() * currencyMap.value(rowCurrency));
emit dataChanged(index, index);
return true;
}
return false;
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
bool CurrencyModel::setData(const QModelIndex &index,
const QVariant &value, int role)
{
if (index.isValid()
&& index.row() != index.column()
&& role == Qt::EditRole) {
QString columnCurrency = headerData(index.column(),
Qt::Horizontal, Qt::DisplayRole)
.toString();
QString rowCurrency = headerData(index.row(),
Qt::Vertical, Qt::DisplayRole)
.toString();
currencyMap.insert(columnCurrency,
value.toDouble() * currencyMap.value(rowCurrency));
emit dataChanged(index, index);
return true;
}
return false;
}
|
回忆一下我们的业务逻辑:我们的底层数据结构中保存的是各个币种相对美元的汇率,显示的时候,我们使用列与行的比值获取两两之间的汇率。例如,当我们修改currencyMap["CNY"]/currencyMap["HKD"]的值时,我们认为人民币相对美元的汇率发生了变化,而港币相对美元的汇率保持不变,因此新的数值应当是用户新输入的值与原来currencyMap["HKD"]的乘积。这正是上面的代码所实现的逻辑。另外注意,在实际修改之前,我们需要检查 index 是否有效,以及从业务来说,行列是否不等,最后还要检查角色是不是Qt::EditRole。这里为方便起见,我们使用了Qt::EditRole,也就是编辑时的角色。但是,对于布尔类型,我们也可以选择使用设置Qt::ItemIsUserCheckable标记的Qt::CheckStateRole,此时,Qt 会显示一个选择框而不是输入框。注意这里的底层数据都是一样的,只不过显示方式的区别。当数据重新设置是,模型必须通知视图,数据发生了变化。这要求我们必须发出dataChanged()信号。由于我们只有一个数据发生了改变,因此这个信号的两个参数是一致的(dataChanged()的两个参数是发生改变的数据区域的左上角和右下角的索引值,由于我们只改变了一个单元格,所以二者是相同的)。最后,如果数据修改成功就返回 true,否则返回 false。
当我们完成以上工作时,还需要修改一下data()函数:
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::TextAlignmentRole) {
return int(Qt::AlignRight | Qt::AlignVCenter);
} else if (role == Qt::DisplayRole || role == Qt::EditRole) {
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);
return QString("%1").arg(amount, 0, 'f', 4);
}
return QVariant();
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
|
QVariant CurrencyModel::data(const QModelIndex &index, int role) const
{
if (!index.isValid()) {
return QVariant();
}
if (role == Qt::TextAlignmentRole) {
return int(Qt::AlignRight | Qt::AlignVCenter);
} else if (role == Qt::DisplayRole || role == Qt::EditRole) {
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);
return QString("%1").arg(amount, 0, 'f', 4);
}
return QVariant();
}
|
我们的修改很简单:仅仅是增加了role == Qt::EditRole这么一行判断。这意味着,当是EditRole时,Qt 会提供一个默认值。我们可以试着删除这个判断来看看其产生的效果。
最后运行一下程序,修改一下数据就会发现,当我们修改过一个单元格后,Qt 会自动刷新所有受影响的数据的值。这也正是 model / view 模型的强大之处:对数据模型的修改会直接反映到视图上。
Qt 学习之路 2(50):自定义可编辑模型的更多相关文章
- Qt 学习之路 2(45):模型
Home / Qt 学习之路 2 / Qt 学习之路 2(45):模型 Qt 学习之路 2(45):模型 豆子 2013年2月26日 Qt 学习之路 2 23条评论 在前面两章的基础之上,我们 ...
- Qt 学习之路 2(41):model/view 架构
Qt 学习之路 2(41):model/view 架构 豆子 2013年1月23日 Qt 学习之路 2 50条评论 有时,我们的系统需要显示大量数据,比如从数据库中读取数据,以自己的方式显示在自己的应 ...
- Qt 学习之路 2(53):自定义拖放数据
Qt 学习之路 2(53):自定义拖放数据 豆子 2013年5月26日 Qt 学习之路 2 13条评论上一章中,我们的例子使用系统提供的拖放对象QMimeData进行拖放数据的存储.比如使用QM ...
- Qt 学习之路 2(5):自定义信号槽
Home / Qt 学习之路 2 / Qt 学习之路 2(5):自定义信号槽 Qt 学习之路 2(5):自定义信号槽 豆子 2012年8月24日 Qt 学习之路 2 131条评论 上一节我们详 ...
- Qt 学习之路 2(49):自定义只读模型
Qt 学习之路 2(49):自定义只读模型 豆子 2013年5月5日 Qt 学习之路 2 18条评论 model/view 模型将数据与视图分割开来,也就是说,我们可以为不同的视图,QListView ...
- Qt 学习之路 2(23):自定义事件
Qt 学习之路 2(23):自定义事件 豆子 2012年10月23日 Qt 学习之路 2 21条评论 尽管 Qt 已经提供了很多事件,但对于更加千变万化的需求来说,有限的事件都是不够的.例如, ...
- Qt 学习之路 2(51):布尔表达式树模型
Qt 学习之路 2(51):布尔表达式树模型 豆子 2013年5月15日 Qt 学习之路 2 17条评论 本章将会是自定义模型的最后一部分.原本打算结束这部分内容,不过实在不忍心放弃这个示例.来自于 ...
- Qt 学习之路 2(25):画刷和画笔
Home / Qt 学习之路 2 / Qt 学习之路 2(25):画刷和画笔 Qt 学习之路 2(25):画刷和画笔 豆子 2012年11月5日 Qt 学习之路 2 17条评论 前面一章我们提 ...
- Qt 学习之路 2(24):Qt 绘制系统简介
Qt 学习之路 2(24):Qt 绘制系统简介 豆子 2012年10月30日 Qt 学习之路 2 77条评论 Qt 的绘图系统允许使用相同的 API 在屏幕和其它打印设备上进行绘制.整个绘图系统基于Q ...
随机推荐
- Linux的基本指令--目录和文件和文件属性和文件用户组
目录和文件 一 . ls:列出目录的内容,未给出目录名或是文件名时,就显示当前目录的信息. -a 列出隐藏文件,文件中以”.”开头的均为隐藏文件,如:~/.bashrc -l 列出文件的详细信息 ...
- Centos7.2 下搭建LNMP环境(终极版)Yum安装
PHP7.1+Nginx+MySQL5.7 安装PHP //安装源只要遇到选择的全是Y rpm -Uvh https://dl.fedoraproject.org/pub/epel/epel-rele ...
- 【总结整理】KANO 模型
c 基本(必备)型需求——Must-beQuality/ Basic Quality. 期望(意愿)型需求——One-dimensional Quality/ Performance Quality. ...
- fastcgi_finish_request
本问原地址 http://www.phpddt.com/php/fastcgi_finish_request.html 某些操作,如用户注册后邮件发送,记录日志等一些耗时操作可以转化为异步操作!当PH ...
- SQLAlchemy(ORM框架)
SQLAlchemy SQLAlchemy概述 2 3 4 5 6 7 8 9 10 11 12 13 MySQL-Python mysql+mysqldb://<user>:&l ...
- Vue.js路由组件
1.如果在创建项目中,没有自动安装vue router,那就自行安装.cnpm install vue-router --save vue-router两种模式 hash模式和history模式. 默 ...
- 数据结构 Merge合并排序
using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.T ...
- Extjs Hello extjs
<html > <head runat="server"> <title></title> <link rel="s ...
- RabbitMQ单机集群搭建出现Error: unable to perform an operation on node 'rabbit1@ClusterNode1'
参考链接:https://www.cnblogs.com/daryl/archive/2017/10/13/7645749.html 全部步骤和参考链接相同. 前八部都正常,在第九步会报错Error: ...
- CentOS Vi编辑器
vim:通过vim a.cfg进入文档 i:编辑状态 ESC:返回不可编辑状态 dd:在不可编辑状态下,dd可删除光标所在的行,2dd删除两行,以此类推 u:在不可编辑状态下,u可恢复删除的行 yy: ...