一文搞懂Qt中的颜色渐变(QGradient Class)

1, 快速开始!

  • Qt中与颜色渐变有关的类是QGradient
  • 其中它又有三个子类:QLinearGradientQRadialGradientQConicalGradient分别对应三种具体的颜色渐变(后面挨个介绍)。
  • 所以它们的关系是:
  • Qt中颜色渐变是怎么用的?它作为一种填充(fill)方式使用,比如说之前我们可以用红色、绿色这些纯色填充一个矩形,现在我们可以用一个渐变颜色(更形象的称呼是色带)来填充,从而可以绘制出更有趣的控件!
  • 先了解一下填充:有两个绕不开的东西——画笔(QPen)和画刷(QBrush)。下面来看一小段代码:
  • 这里仅需要明确勾勒矩形线条时使用画笔,填充矩形内部时使用画刷
  • 因为画刷控制着填充,渐变颜色在Qt中以填充方式使用,所以需要有一种创建渐变颜色画刷的方法。QBrush有一个以QGradient为唯一参数的构造函数:
 QBrush(const QGradient &gradient)
  • 所以想绘制一个有渐变效果的矩形可以这么写:
  • 上面的例子中,我们保持画笔不变,使用QLinearGradient类型的颜色渐变创建了一个画刷。结果是一个很漂亮的线性颜色渐变,它从矩形左上角的白色过渡到矩形右下角的黑色。
  • 让我们更进一步。。。

2, 颜色渐变

  • 颜色渐变表示我们需要将一个渐变颜色集填充到一个区域中。
  • 要获得一个渐变颜色集有一种简单的方式:Color1过渡到Color2过程中的所有颜色所构成的集合。上面的例子我们通过指定Color1为白色,Color2为黑色得到一个渐变颜色集:
    QLinearGradient linearGradient = ...linearGradient.setColorAt(0, Qt::white);    // Color1^^^^^^^^^linearGradient.setColorAt(1, Qt::black);  // Color2^^^^^^^^^

  • 接下来的问题是当填充一个区域时我们还需要一个填充方向,比如是从上到下?从左到右?还是从点A到点B。
  • 填充方向的指定方式不同产生了三种不同的颜色渐变类型:线性(QLinearGradient),径向(QRadialGradient)和锥向(QConicalGradient)。

2。1, 线性颜色渐变(QLinearGradient Class)

  • 如你所料,通过指定一个起点(start)和一个结束点(finalStop)来确定一个线性填充方向。
 //// QLinearGradient ctorQLinearGradient(qreal x1, qreal y1, qreal x2, qreal y2)QLinearGradient(const QPointF &start, const QPointF &finalStop)
  • 上面的例子中就是以矩形的左上角(start)到矩形的右下角(finalStop)作为渐变填充方向。
  • 所以如果我们想要从上到下填充一个矩形可以用下面这段代码:
   // 构造一个线性渐变(start, finalStop)QLinearGradient linearGradient(QPointF(0, -200), QPointF(0, 200));linearGradient.setColorAt(0, Qt::white);linearGradient.setColorAt(1, Qt::black);// 使用线性渐变创建一个画刷,用来填充QBrush brush(linearGradient);painter->setBrush(brush);// x, y, w, hpainter->drawRect(-100, -200, 200, 400);//// axisQPen pen;pen.setColor(Qt::red);painter->setPen(pen);painter->drawLine(-20, 0, 20, 0);painter->drawLine(0, 20, 0, -20);
  • 结果如下:

  • 上图中左边为Qt程序的实际绘制,右边为示意。

  • 需要注意的一点是,Qt程序的绘制坐标系的纵轴与数学上相反。

  • 最后一点,我们在提供颜色的同时还提供了另一个参数:

 // setColorAt(qreal position, const QColor &color)~~~~~~~~~~~~~~linearGradient.setColorAt(0, Qt::white);^linearGradient.setColorAt(1, Qt::black);^
  • 参数position的范围是[0,1],前面已经知道startfinalStop确定出一条带方向的线段,而参数position相当于一个比例参数,它在这条线段上标定一个位置,表示在这个位置处达到后一个参数所代表的颜色值
 达到该颜色的位置 = start + position * (finalStop - start)

  • 上图是拿黑色达到的位置为例,白色到达(开始)的位置也是一样。
  • 当position等于0或1时,达到颜色的位置分别位于两个端点处,所以上面两行代码的意思是:在start处为纯白,在finalStop处为纯黑。
  • 这就是线性渐变的全部。
  • 线性渐变用起来非常简单,但它所有的概念也都适用于后面要说的两种渐变,所以说了这么多。。。

2。2, 锥向颜色渐变(QConicalGradient Class)

  • 锥向渐变几乎和线性渐变一样简单,所以把它作为第二个介绍。
  • 锥向渐变通过一个中心点(center)和一个角度(angle)来确定填充方向。
 // QConicalGradient ctorQConicalGradient(qreal cx, qreal cy, qreal angle)QConicalGradient(const QPointF &center, qreal angle)
  • 先看一个具体的例子:
 // 构造一个锥向渐变(center, angle)QConicalGradient conicalGradient(QPointF(0, 0), 45);conicalGradient.setColorAt(0, Qt::white);conicalGradient.setColorAt(1, Qt::black);// 使用锥向渐变创建一个画刷,用来填充QBrush brush(conicalGradient);painter->setBrush(brush);painter->drawRect(-100, -100, 200, 200);//// axisQPen pen;pen.setColor(Qt::red);pen.setWidth(1);painter->setPen(pen);painter->drawLine(-20, 0, 20, 0);painter->drawLine(0, 20, 0, -20);

  • 使用极坐标系来看待锥向渐变的构造过程也许更自然。
  • 提供一个点(center)和一个角度(angle)可以确定出一条射线,这相当于给出了一条起始线,给定的Color1会从这条线开始,按逆时针方向过渡到Color2
  • 我们在提供颜色的同时仍然提供了一个位置(position)参数,它的含义与线性渐变中的一样,不过在锥向渐变中把它理解成角度的比例而非距离的比例或许更好:
 达到该颜色的位置 = angle + position * 360°

  • 锥向渐变似乎比它看起来更简单,这一点在后面会有体现,go on。。。

2。3, 径向颜色渐变(QRadialGradient Class)

  • 径向渐变有两种:简单径向渐变(simple radial gradient)和扩展径向渐变(extended radial gradient)。其实也没有太大的不同(从它们使用同一个类QRadialGradient来构造就可以看的出),只是它们的确不一样,所以需要不同的名字来区分。
  • 没错,提到径向,这种渐变的确是用圆来确定填充方向。
  • 在开始径向渐变前,我们先停一停,回顾前面两种渐变,我们为了确定填充方向其实引入了几个概念,线性渐变中的起点(start)和结束点(finalStop);锥向渐变中的中心点(center)和角度(angle);这些概念是把握不同渐变区别和用法的一个关键点,所以径向渐变中有圆心(center)、半径(centerRadius)、焦点(focalPoint)和焦半径(focalRadius)。来,让我们将这些烙在心头:

2。3。1, 简单径向渐变

  • 首先看看怎样构造一个简单径向渐变:
 //// simple radial gradient ctorQRadialGradient(qreal cx, qreal cy, qreal radius)QRadialGradient(const QPointF &center, qreal radius)QRadialGradient(qreal cx, qreal cy, qreal radius, qreal fx, qreal fy)QRadialGradient(const QPointF &center, qreal radius, const QPointF &focalPoint)
  • 圆心(center)和半径(radius)确定出一个圆周,所以简单径向渐变是在焦点与所有圆周上的点之间进行颜色插值。
  • 先看一个指定焦点的例子:
    //// 指定焦点(30, -40)QRadialGradient radialGradient(0, 0, 100, 30, -40);^^^^^^^radialGradient.setColorAt(0, Qt::white);radialGradient.setColorAt(1, Qt::black);painter->setBrush(QBrush(radialGradient));QRectF rect(-100, -100, 200, 200);painter->drawEllipse(rect);//// axispainter->setPen(QPen(Qt::red));painter->drawLine(-20, 0, 20, 0);painter->drawLine(0, 20, 0, -20);//// local axispainter->setPen(QPen(Qt::blue));painter->drawLine(30, -30, 30, -50);painter->drawLine(20, -40, 40, -40);painter->setPen(QPen(Qt::black));

  • 当不明确指定焦点时,默认焦点在圆心处。
  • 如下例:
    //// 默认焦点(0, 0)QRadialGradient radialGradient(0, 0, 100);radialGradient.setColorAt(0, Qt::white);radialGradient.setColorAt(1, Qt::black);painter->setBrush(QBrush(radialGradient));QRectF rect(-100, -100, 200, 200);painter->drawEllipse(rect);//// axispainter->setPen(QPen(Qt::red));painter->drawLine(-20, 0, 20, 0);painter->drawLine(0, 20, 0, -20);//// local axispainter->setPen(QPen(Qt::blue));painter->drawLine(-20, 0, 20, 0);painter->drawLine(0, 20, 0, -20);

  • 上面两种都属于简单径向渐变,其中后一种又可以看成前一种的特殊情况。
  • 简单径向渐变中这种确定填充方向的方式(焦点和中心圆),很自然的让我们考虑到点和圆的位置关系:上面都属于点在圆内的情况;点在圆上时也没有什么不同;但当点在圆外时,简单径向渐变会将其对齐到圆上,这一点与扩展径向渐变不同。

2。3。2, 扩展径向渐变

  • 扩展径向渐变与简单径向渐变有两点不同:
  • 一是扩展径向渐变中不再用焦点,取而代之的是一个焦圈,所以在构造扩展径向渐变时我们需要多提供一个焦半径(focusRadius)。
  • 二是在简单径向渐变中焦点在圆外时会被强制对齐到圆上,扩展径向渐变没有这个限制。
  • 下面是扩展径向渐变的构造方法:
 //// extended radial gradient ctor// these functions were introduced in Qt 4.8.QRadialGradient(qreal cx, qreal cy, qreal centerRadius, qreal fx, qreal fy, qreal focalRadius)QRadialGradient(const QPointF &center, qreal centerRadius, const QPointF &focalPoint, qreal focalRadius)
  • 看下面这个例子:
    //// 扩展径向渐变(focusRadius = 50)QRadialGradient radialGradient(0, 0, 200, 60, 80, 50);^^radialGradient.setColorAt(0, Qt::white);radialGradient.setColorAt(1, Qt::black);QBrush brush(radialGradient);painter->setBrush(brush);painter->drawEllipse(-200, -200, 400, 400);//// axispainter->setPen(QPen(Qt::red));painter->drawLine(-30, 0, 30, 0);painter->drawLine(0, 30, 0, -30);//// local axispainter->setPen(QPen(Qt::blue));painter->drawLine(60, 60, 60, 100);painter->drawLine(40, 80, 80, 80);

  • 很明显,这次不再是一个焦点,而是一个焦圈。
  • 和简单径向渐变一样,扩展径向渐变也存在一对位置关系:中心圆和焦点圆之间。
  • 很奇怪,只有内含时(也就是上例的情况)才可以达到预期效果。在评论区留下你对其它位置关系的理解。。。
  • 同样,我们提供两个颜色的同时,也提供了一个位置参数(position),它的含义本质上和前两种渐变一样,只不过这里确定的是不同大小的同心圆。
  • 以默认焦点的简单径向渐变举个例子:

3, 渐变传播(Spread)

  • 好吧,之前我们一直有意掩饰一点,来看看前面三种渐变的构造方式:
    //// 构造一个线性渐变(start, finalStop)QLinearGradient linearGradient(QPointF(0, -200), QPointF(0, 200));^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^linearGradient.setColorAt(0, Qt::white);linearGradient.setColorAt(1, Qt::black);...painter->drawRect(-100, -200, 200, 400);  // x, y, w, h^^^^^^^^^^^^^^^^^^^^//// 构造一个锥向渐变(center, angle)QConicalGradient conicalGradient(QPointF(0, 0), 45);conicalGradient.setColorAt(0, Qt::white);conicalGradient.setColorAt(1, Qt::black);...painter->drawRect(-100, -100, 200, 200);  // x, y, w, h//// 构造一个简单径向渐变(center, radius)QRadialGradient radialGradient(0, 0, 100);^^^^^^^^^radialGradient.setColorAt(0, Qt::white);radialGradient.setColorAt(1, Qt::black);...painter->drawEllipse(-100, -100, 200, 200);  // x, y, w, h^^^^^^^^^^^^^^^^^^^^
  • 把握一点:在创建一个渐变时,我们提供的参数(如startfinalStop)在确定了填充方向的同时,也定义出一个渐变的边界。
  • 不难发现,线性渐变中我们定义的起点到结束点的线段刚好能全部覆盖要填充矩形的宽度,换句话说,我们定义的颜色渐变边界和我们将要填充的区域恰好一样大。
  • 径向渐变也是如此,我们故意让径向渐变的中心圆和要填充的圆一样大。
  • 很自然的一个问题:如果我们构造的颜色渐变边界比要填充的图形小,这时边界之外,图形之间会发生什么?
  • 这之间发生的事便是渐变的传播(spread),具体怎样传播,需要我们设置传播方式(spread method)
  • 这里我们并没有提锥向渐变,因为传播涉及的是渐变边界之外发生的事,而从锥向渐变的构造也可以看出,锥向渐变填充的是全平面,它并没有边界之外这种概念,所以传播的设置对锥向渐变没有影响。
  • 总结一下:
  • Qt默认使用的传播方式是Pad
  • Qt中使用setSpread(QGradient::Spread method)设置使用哪一种传播方式。
  • 如果想使用Repeat方式可以写类似下面的代码:
    QRadialGradient radialGradient = ...radialGradient.setColorAt(0, Qt::white);radialGradient.setColorAt(1, Qt::black);radialGradient.setSpread(QGradient::RepeatSpread);~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~painter->setBrush(QBrush(radialGradient));//// draw ...
  • 下面看看这几种传播方式的样子和区别。
  • 为了显得这篇文章没那么冗长,这部分不再贴代码了。与前面的示例代码相比,这部分的代码只是多了上面那句传播方式的设置和一些位置调整。
  • 当我们将渐变边界缩至中间那一窄条时,传播方式发挥了作用。
  • 在渐变边界以外(待填充区域之内),PadSpread方式延续了起点与结束点各自的颜色;
  • RepeatSpread方式,如其名字所暗示的那样,不断重复渐变边界内的这一颜色渐变模式。
  • ReflectSpread方式将渐变边界内的模式不断往外镜像(反射)。
  • 再来看看径向渐变(焦点与圆心重合)中的传播:
  • 这和线性渐变一样!只是表现形式稍有改变:不再是矩形,而是一个个圆环。
  • 当焦点不在圆心处时,唯一不同在于颜色的变化没有那么均匀:
  • 显而易见,对于RepeatSpread,渐变边界之外的填充区域(如红箭头和绿箭头)一直在重复蓝箭头和黄箭头,对于ReflectSpread,渐变边界之外的填充区域(如红箭头和绿箭头)一直在镜像(反射)蓝箭头和黄箭头。
  • 这就是渐变传播的全部。

4, 渐变颜色

  • 我们之前的例子都难免有些单调。因为只使用了渐变边界的两个端点(position是0或1),而且只使用了黑色和白色。
  • 世界本应该是五彩缤纷的:)
  • 再看看我们提供渐变颜色的代码:
    linearGradient.setColorAt(0, Qt::white);linearGradient.setColorAt(1, Qt::black);
  • 所以我们可以在任意多的比例(position)处插入任何颜色:
     linearGradient.setColorAt(0, QColor("#020024"));linearGradient.setColorAt(0.35, QColor("#090979"));linearGradient.setColorAt(1, QColor("#00d4ff"));
  • 结果是一个漂亮的颜色过渡:
  • 一个漂亮的颜色过渡并不是随意颜色的拼凑,颜色过渡也有一些预制的主题,感谢那些卓越的设计师和开源组织~
  • 推荐两个很棒的网站:
    https://cssgradient.io/
    https://webgradients.com/
  • 基于后一个网站,Qt提供了内置的渐变主题(Preset),这些主题以枚举(enum QGradient::Preset)的方式提供,注意这个枚举是 Qt 5.12引入的。
  • 下面是绘制前几个内置渐变主题的小例子:
 //const qreal radius = 60;const qreal gap = 30;const qreal distance = 2*radius+gap;QRectF circle(0, -1*radius, 2*radius, 2*radius);for (int i = 1; i <= 7; ++i) {  // 前7个// ctor: QGradient(QGradient::Preset preset)painter->setBrush(QBrush(QGradient(static_cast<QGradient::Preset>(i))));~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~painter->drawEllipse(circle);qreal x = (i%5) ? distance : -4*distance;qreal y = (i%5) ? 0 : distance;circle.translate(x, y);}
  • 绘制结果如下:
  • 渐变的实际用例并不难找:

5, Qt例子

  • Qt官方提供了一个不错的例子Gradients:
    https://doc.qt.io/qt-5/qtwidgets-painting-gradients-example.html

6, 参考

  • https://doc.qt.io/qt-5/qgradient.html
  • https://developer.mozilla.org/en-US/docs/Web/CSS/gradient
  • https://xd.adobe.com/ideas/principles/web-design/gradient-color-definition/
  • 本文的文字和图片可以随意使用,不用找我允许。能放个出处当然更好啦~
  • 推荐下个人公众号,会写些好玩的,也可以一块学习~
  • 肝完了。。。

一文搞懂Qt中的颜色渐变(QGradient Class)相关推荐

  1. 一文搞懂matplotlib中的颜色设置

    欢迎关注"生信修炼手册"! 在matplotlib中,颜色设置有以下多种方式 1. 常用颜色的字母表示及缩写 最常用的颜色表示方法,有以下几种常用颜色 1. red,表示红色,  ...

  2. 一文搞懂产品中的搜索设计

    搜索功能是我们日常生活中接触最多的功能之一,它更够很好的提高用户使用产品的效率,用户对搜索功能的依赖性也比较大,所以设计好搜索功能将会很大程度上提高用户体验.本文作者通过分享这篇文章,帮我们搞懂产品中 ...

  3. 一文搞懂Java8中表示当前的时间类Date、Instant、LocalDateTime、ZonedDateTime

    1. 概述 Java8中的时间类主要有:Date.Instant.LocalDateTime(LocalDate.LocalTime).ZonedDateTime,除去Date,java.time包下 ...

  4. 一文搞懂 CSS3 中的渐变到底怎么玩

    古之立大事者,不唯有超世之才,亦必有坚忍不拔之志--苏轼 写在前面 今天在做案例的时候,用到了 CSS3 的渐变,可是用到的时候却发现渐变的语法已经忘了,于是就有了这篇博客. 文章目录 写在前面 什么 ...

  5. html 字号和像素的关系,一文搞懂CSS中的字体单位大小(px,em,rem...)

    在学习的过程中,发现CSS有很多可以形容单位的尺寸.比方px,em,rem,vw等等.平常也没有深究,一来是没时间,二来是在我学习清单中优先级过低.一直想彻底弄明白,一直耽搁到现在.现在花上一点时间来 ...

  6. python中row是什么意思_一文搞懂Python中的yield

    关注公众号「Python七号」,及时 get Python 技能. yield 可以实现生成器,可以实现协程. 什么是生成器,什么是协程,如果还不了解,可以继续往下看,概念可以不懂,只要理解它的作用和 ...

  7. 【NLP】一文搞懂NLP中的对抗训练

    本文主要串烧了FGSM, FGM, PGD, FreeAT, YOPO, FreeLB, SMART这几种对抗训练方法,希望能使各位大佬炼出的丹药更加圆润有光泽,一颗永流传 简介 对抗训练是一种引入噪 ...

  8. 服务器千兆网卡接百兆交换机不通_一文搞懂监控工程中百兆交换机和千兆交换机的区别在哪?...

    安防监控系统工程现在都是用的网络摄像机,那么就肯定会经常和网络设备--交换机打交道,很多人在做监控方案的时候犯难,多少台摄像机该选用百兆交换机还是千兆交换机呢?关于这个问题除了需要掌握理论知识还是结合 ...

  9. 一文搞懂css中精灵图如何使用

    文章目录 前言 一.精灵图是什么? 1.概念: 2.图片示例: 二.为什么使用精灵图? 1.用户体验而言: 2.就开发者而言: 3.就服务器而言: 三.怎样使用精灵图 1.background-pos ...

最新文章

  1. Google Chrome插件导出与安装
  2. 计算机入域时域控用到的端口,【ADDC】域控需要开放的端口
  3. jsp tag 自定义标签实现按钮的显示
  4. OOo-MySpell 一个C++的拼写语法检查开源项目
  5. 简单易用的开源ORM框架SqlSugar v5.0.0.19源码
  6. prim算法_历时两月,终拿字节跳动offer,算法面试题分享「带答案」
  7. 对话DDM:分布式数据库中间件全解析
  8. C#中IL反汇编工具的使用 其具体含义如下文
  9. 2.亿级流量的电商网站---Redis
  10. kvm+libvirt虚拟机快照浅析[转]
  11. Rust : Trait Object safe 问题
  12. 一文读懂什么是绿色工厂以及绿色工厂建设细解
  13. MySQL从删库到跑路(3):神奇的select
  14. 连接数据库显示: Access denied for user ‘root‘@‘locahost‘(using password:YES)解决方式。
  15. 什么专业学计算机编程,计算机编程是什么专业 难学吗
  16. 惠新宸php教程_【转载】惠新宸:PHP在百度的应用现状及展望
  17. Linux常用命令英文全称与中文解释 (pwd、su、df、du等)
  18. 内网环境下的横向移动总结
  19. SRAM电路工作原理
  20. 解决现行m8u3格式的视频下载问题

热门文章

  1. 安卓桌面整理app_【小编分享】APP整理大法!跟杂乱无章的手机桌面说拜拜~
  2. 2013年度对话《3S 新闻周刊》
  3. python3ide安卓版官网下载_python3.9下载-Python下载v3.9.0 官方最新版【x86|x64】-西西软件下载...
  4. 基于matlab的控制系统仿真题,MATLAB与控制系统仿期末考试试卷真
  5. 详解自定义钉钉机器人推送消息(一) PHP篇
  6. 上海迪士尼乐园将于6月30日恢复运营,乐园门票6月29日起重新发售 | 美通社头条...
  7. 用格式工厂旋转手机视频
  8. 面向对象的一些概念及举例说明
  9. Unity3D游戏开发入门学习笔记
  10. 【SpringBoot】 日志框架冲突