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 ...
随机推荐
- 对象序列化中transient关键字的用途
- 专利系统数据库连接出现 base-64字符串中的无效字符 错误
错误提示如图: 解决方法: 1.进注册表修改如下 2.进入系统配置页面http://10.10.0.70/eaf/init 对数据库进行重新配置 3.若不行再将如下密码修改一下 重启IIS生效
- 【原创】10. MYSQL++ 之 DbDriver
1. 综述 DbDriver只是对于MYSQL C API的一个非常简单的封装,作者原句是This class does as little as possible to adapt between ...
- VS快捷键小收集
1. Ctrl-M-O 折叠所有方法 Ctrl-M-M 折叠或展开当前方法 Ctrl-M-L 展开所有方法 2. 行编辑(复制,剪切,删除,交换) 当你在光标停留行使用快捷键Ctrl ...
- Chrome,firefox,ie等浏览器空格 宽度不一样
方案一:使用其他字符代替空格 使用( :)空格浏览器之间,显示的不一样,对不齐等现象. 解决方案: 用半角空格&ensp:或者全角空格&emsp:就可以了,&e ...
- 453D Little Pony and Elements of Harmony
传送门 分析 我们可以将所有的b[i^j]直接对应到b[f(i^j)]上 于是显然可以fwt 我们对b进行t次fwt之后直接将答案与e0卷起来即可 注意由于模数不确定,我们可以将模数扩大$2^m$然后 ...
- PostgreSQL9.3+PostGIS2.1安装配置
Postgresql——Postgresql是一种对象关系型数据库.下载地址:http://www.postgresql.org/download/ postgis (可选)——是一个空间数据库,它扩 ...
- 高级软件测试技术(测试管理工具实践day4)
今天是截止日期,有胡俊辉的指导下小组成员都了解使用了Bugzilla的基本使用.大家都在晚上之前把各自的文档汇总给汪鸿,由他撰写了操作手册.并且在下午杨瑞丰完成了视频的录制工作.但是在转化为MP4 格 ...
- System.Web.UI.Page事件执行顺序
#region OnPreInit 第一步(显式重写,文章下面有隐式重写) protected override void OnPreInit(EventArgs e) { //检查 IsPostBa ...
- Java学习第六篇:集合类
一.Java集合类框架 Java集合大致可分为Set.List和Map三种体系,其中Set代表无序.不可重复的集合:List代表有序.重复的集合:而Map则代表具有映射关系的集合:从Java5以后,J ...