目录
0. 前言
我们在专栏的第一篇文章中,提到Delegate
时说:
但现在Model
和View
都已经入门了,该来的总是要来的:本文我们就一起开始进攻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
把数据显示成字符串。
这里先老样子,仿照Model
和View
,来一张UML类图,看看它的家族渊源:
可以发现这位老兄比Model
或是View
要简洁多了,从QObject
出来直接就是标准的Abstract
接口了。
从前文我们已经知道
QAbstractItemModel
和QAbstractItemView
分别定义了Model
类和View
类的标准接口;
标准接口均无法实例化。
图中的两兄弟QStyledItemDelegate
和QItemDelegate
,官方更推荐使用QStyledItemDelegate
,因为它使用了当前程序的风格去绘制对象。所以本文所有操作都将围绕QStyledItemDelegate
进行。
2. 为什么要用Delegate
在学习它之前我们需要知道,为什么我们需要使用它?或者哪些情况使用它能带给我们何种便利?我们不能因为它是Model/View框架里面的重要一员而学习。
使用场景自然从其功能分析,Delegate
负责渲染和编辑数据,那我们使用它必然是因为默认的渲染或编辑方式不及预期。
所以使用Delegate基本上是一个锦上添花的操作,如果只需要实现基本功能的话,似乎不使用Delegate
也无伤大雅。换言之:决心使用Delegate
的朋友都是高品位的 👻。
举2个简单例子阐述:
-
渲染:比如对于百分比可能更想用进度条显示,对于布尔值更想用勾选框显示……甚至对于有较高UI要求人士而言数据的表现形式可以是画星星的数量(
Star Delegate
教程)等等;
-
编辑:比如对于性别这类取值限定的数据,在处理输入时会更希望采用下拉菜单而不是直接输入;
使用Delegate
以后,或许比较重要的是开放思想,发挥创意,它能做的远超我们想象!
3. 渲染方法
3.1 函数重写
查看QStyledItemDelegate
的Subclassing
部分:如果想要自定义绘制对象,需要派生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
如此看来,要重写这两个函数,就得先攻克一下QPainter
和QStyleOptionViewItem
这两兄弟了。
从类名看起来,QPainter
主要是用来画东西的,QStyleOptionViewItem
是提供画东西的一些选项。我们能将数据渲染成怎样基本取决于QPainter
的能力。
3.2 QPainter
根据官方文档,QPainter
能在paint device
上绘制各种简单直线、复杂形状、文字、图片等,而paint device
是指QPaintDevice
或由其派生的子类,是一个以左上角为原点,X向右Y向下的二维平面,常见的paint device
有QWidget、QImage、QPixmap
等。
可以看出来,QPainter
是一个比较底层的类,给了我们很多灵活性。接下来我们看一下它是通过哪些函数来绘制的。
3.2.1 设置
参考QPainter
的Settings
部分,常用的有字体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
函数需要的所有信息。具体见下图:
听起来有些绕,但是我们需要知道的也不用很多:
- 为了高性能,它的变量都是公有的,直接访问进行修改即可;
- 记住它的
rect
参数,最常用的就是它了,它规定了绘制时可用的矩形区域;甚至在官方文档的例子中,都只用了rect
这一个参数:
4. 小结
- 需要使用
Delegate
时,一般建议从QStyledItemDelegate
派生,因为它使用了当前程序的风格去绘制对象; - 实现自定义渲染需要重载
paint()
函数,也建议重载sizeHint()
函数; - 在
paint()
函数中,可以使用QPainter
来绘制各种基础图形,也可以绘制图片; QPainter
支持一堆设置,在指针传递时最好先save()
再改变设置,使用完后restore()
恢复设置;QPainter
只能在QPaintDevice
上绘制内容,后者可看做是一个二维平面,左上角为原点,X
轴向右,Y
轴向下;QWidget
、QPicture
等都是QPaintDevice
的子类;- 还可以在
paint()
函数中采用QStyle::drawControl()
函数绘制控件,以稍微复杂的形式(如进度条)来显示数据; paint()
函数形参有一个QStyleOptionViewItem
对象,记住它的rect
属性,基本操作使用它就够了;- 下一篇我们代码实现一下多种渲染方法~
如有错误欢迎指正,共同进步~
今天你学废了吗?