Qt Model/View 学习(6) - Delegate教程之——渲染


0. 前言

我们在专栏的第一篇文章中,提到Delegate时说:

在这里插入图片描述

但现在ModelView都已经入门了,该来的总是要来的:本文我们就一起开始进攻Delegate吧!


系列文章回顾
Qt Model/View 学习(1) - 是什么和为什么?
Qt Model/View 学习(2) - QModelIndex索引模型数据
Qt Model/View 学习(3) - 索引来一堆东西,究竟取谁(ItemDataRole)?
Qt Model/View 学习(4) - 实现自己的QAbstractTableModel类(支持显示与修改)
Qt Model/View 学习(5) - QTableView(优雅)使用教程(附源码)
Qt Model/View 学习(5.5) - 使用QTableView(优雅地)实现翻页效果(附源码)


1. Delegate简介

第一篇文章我们已经知道了,Delegate提供View上对数据进行渲染和修改的功能,以及提供了一个默认Delegate把数据显示成字符串

这里先老样子,仿照ModelView,来一张UML类图,看看它的家族渊源:
在这里插入图片描述

可以发现这位老兄比Model或是View简洁多了,从QObject出来直接就是标准的Abstract接口了。

从前文我们已经知道 QAbstractItemModelQAbstractItemView分别定义了Model类和View类的标准接口;

标准接口均无法实例化

图中的两兄弟QStyledItemDelegateQItemDelegate官方更推荐使用QStyledItemDelegate,因为它使用了当前程序的风格去绘制对象。所以本文所有操作都将围绕QStyledItemDelegate进行。
在这里插入图片描述


2. 为什么要用Delegate

在学习它之前我们需要知道,为什么我们需要使用它?或者哪些情况使用它能带给我们何种便利?我们不能因为它是Model/View框架里面的重要一员而学习。

使用场景自然从其功能分析,Delegate负责渲染和编辑数据,那我们使用它必然是因为默认的渲染或编辑方式不及预期

所以使用Delegate基本上是一个锦上添花的操作,如果只需要实现基本功能的话,似乎不使用Delegate也无伤大雅。换言之:决心使用Delegate的朋友都是高品位的 👻。

举2个简单例子阐述:

  1. 渲染:比如对于百分比可能更想用进度条显示,对于布尔值更想用勾选框显示……甚至对于有较高UI要求人士而言数据的表现形式可以是画星星的数量Star Delegate教程)等等;
    在这里插入图片描述

  2. 编辑:比如对于性别这类取值限定的数据,在处理输入时会更希望采用下拉菜单而不是直接输入;
    在这里插入图片描述

使用Delegate以后,或许比较重要的是开放思想,发挥创意,它能做的远超我们想象!


3. 渲染方法

3.1 函数重写

查看QStyledItemDelegateSubclassing部分:如果想要自定义绘制对象,需要派生QStyledItemDelegate,并重写paint()以及或许需要sizeHint()。并且提示了:对不想要自定义绘制的类型应该调用基类的实现
在这里插入图片描述
听起来很简单,甚至当场想说一句:就这?

但是别着急,我们先看看paint()的函数原型,QStyledItemDelegate重写了QAbstractItemDelegate的纯虚函数paint()

[virtual] void QStyledItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const

[pure virtual] void QAbstractItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const

事实上,sizeHint()函数也是QStyledItemDelegate重写了QAbstractItemDelegate的纯虚函数sizeHint(),所以官方文档才会说或许需要重写sizeHint()

[virtual] QSize QStyledItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const

[pure virtual] QSize QAbstractItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const

如此看来,要重写这两个函数,就得先攻克一下QPainterQStyleOptionViewItem这两兄弟了。

从类名看起来,QPainter主要是用来画东西的,QStyleOptionViewItem是提供画东西的一些选项。我们能将数据渲染成怎样基本取决于QPainter的能力。

3.2 QPainter

根据官方文档,QPainter能在paint device上绘制各种简单直线、复杂形状、文字、图片等,而paint device是指QPaintDevice或由其派生的子类,是一个以左上角为原点,X向右Y向下的二维平面,常见的paint deviceQWidget、QImage、QPixmap等。

可以看出来,QPainter是一个比较底层的类,给了我们很多灵活性。接下来我们看一下它是通过哪些函数来绘制的。

3.2.1 设置

参考QPainterSettings部分,常用的有字体QFont、笔刷QBrush、钢笔QPen的设置。

在设置某个QPainter之前,最好先调用其save()函数,保存其设置状态,在使用完之后调用restore()函数恢复。

笔者猜想,养成这个好习惯的重要意义在于:很多函数中QPainter指针传递

3.2.2 绘制能力

给出的绘制内容的函数如下表:

函数图形
drawPoint
drawArc
drawChord
drawEllipse椭圆
drawLine直线
drawPath路径
drawPie扇形
drawPolygon多边形
drawPolyline多段线
drawRect矩形
drawRoundedRect圆角矩形
eraseRect擦除矩形
fillRect填充矩形
drawText文字
drawImage图片
drawPicture图片
drawPixmap图片

所以我们可以猜想,Star Delegate应该是使用了drawPolygon函数实现的画星星功能。

虽然我们要从直线、多边形、椭圆、扇形等等基本图形组合出优美的渲染效果或许很难,但是,我们既然看到了画图片的几个函数,可以直接PS出各种图片再画上去

其它思路就靠大家自己打开了~

3.3 QStyle

若不是从QAbstractItemDelegate的介绍中看到了下图的效果,谁能想到还有这种操作?
在这里插入图片描述
这种效果是调用了QStyle::drawControl()函数:
在这里插入图片描述
附上其函数原型:

[static] QStyle *QApplication::style();

[pure virtual] void QStyle::drawControl(ControlElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget = Q_NULLPTR) const

这和上一小节的QPainter完全是两种路子,而且绘制的效果要复杂多了。不过也是十分底层的函数。最后绘制出来的内容应该没有事件处理,只是一副图像。

支持绘制的控件类型为QStyle::ControlElement的枚举类型,而且绘制时需要提供QStyleOption类型的绘制选项。下面的表格中列举了常见的枚举值:

控件选项类型
QStyle::CE_PushButton按键QStyleOptionButton
QStyle::CE_CheckBox勾选框QStyleOptionButton
QStyle::CE_ProgressBar进度条QStyleOptionProgressBar

支持的控件类型还有很多很多,但应该说能用于显示数据的或许较少,需要大家自行发觉~在Assistant中搜索drawControl函数即可。

3.4 QStyleOptionViewItem

QStyleOptionViewItem描述了在一个View控件中绘制对象的所有参数,包含了QStyle函数需要的所有信息。具体见下图:
在这里插入图片描述

听起来有些绕,但是我们需要知道的也不用很多:

  1. 为了高性能,它的变量都是公有的,直接访问进行修改即可;
  2. 记住它的rect参数,最常用的就是它了,它规定了绘制时可用的矩形区域;甚至在官方文档的例子中,都只用了rect这一个参数:
    在这里插入图片描述

4. 小结

  1. 需要使用Delegate时,一般建议从QStyledItemDelegate派生,因为它使用了当前程序的风格去绘制对象;
  2. 实现自定义渲染需要重载paint()函数,也建议重载sizeHint()函数;
  3. paint()函数中,可以使用QPainter来绘制各种基础图形,也可以绘制图片
  4. QPainter支持一堆设置,在指针传递时最好先save()再改变设置,使用完后restore()恢复设置;
  5. QPainter只能在QPaintDevice上绘制内容,后者可看做是一个二维平面,左上角为原点,X轴向右,Y轴向下QWidgetQPicture等都是QPaintDevice的子类;
  6. 还可以在paint()函数中采用QStyle::drawControl()函数绘制控件,以稍微复杂的形式(如进度条)来显示数据;
  7. paint()函数形参有一个QStyleOptionViewItem对象,记住它的rect属性,基本操作使用它就够了;
  8. 下一篇我们代码实现一下多种渲染方法~

如有错误欢迎指正,共同进步~


今天你学废了吗?

  • 23
    点赞
  • 43
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
Qt Model/View/DelegateQt 提供的一种用于实现 MVC 设计模式的框架,其中 Model 负责存储数据,View 负责显示数据,Delegate 负责定制数据的显示方式。 在 Model 中,我们可以通过实现 `roleNames()` 函数来定义数据的角色名称。这些角色名称可以被 ViewDelegate 使用来访问 Model 中的数据。 `roleNames()` 函数返回一个 `QHash<int, QByteArray>` 类型的值,其中键是角色的编号,值是角色的名称。对于一个标准的 ModelQt 提供了一些已经定义好的角色,例如 `Qt::DisplayRole` 用于显示数据,`Qt::EditRole` 用于编辑数据等。 我们也可以自定义角色,例如在一个存储学生信息的 Model 中,我们可以自定义一个 `Qt::UserRole` 角色,用于存储学生的分数信息。在 `roleNames()` 中,我们可以这样定义: ```cpp QHash<int, QByteArray> StudentModel::roleNames() const { QHash<int, QByteArray> roles; roles[Qt::DisplayRole] = "display"; roles[Qt::UserRole] = "score"; return roles; } ``` 在 View 中,我们可以使用 `data()` 函数来访问 Model 中的数据,同时指定要访问的角色。例如: ```cpp QVariant score = model->data(modelIndex, Qt::UserRole); ``` 在 Delegate 中,我们可以重写 `setModelData()` 函数来更新 Model 中的数据,同时指定要更新的角色。例如: ```cpp void ScoreDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const { QLineEdit *lineEdit = static_cast<QLineEdit*>(editor); QString score = lineEdit->text(); model->setData(index, score, Qt::UserRole); } ``` 通过定义和使用自定义角色,我们可以更加灵活地操作 Model 中的数据,并将数据以更加自然的方式展示给用户。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值