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

本文深入讲解了如何在Qt中使用Delegate,包括自定义QStyledItemDelegate的派生、paint()函数的重写、QPainter与QStyleOptionViewItem的应用,以及如何利用QStyle和控件绘制复杂数据展示。了解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. 下一篇我们代码实现一下多种渲染方法~

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


今天你学废了吗?

评论 10
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值