当前位置: 首页 > news >正文

使用 Qt QGraphicsView/QGraphicsScene 绘制色轮

使用 Qt QGraphicsView/QGraphicsScene 绘制色轮

本文介绍如何在 Qt 中利用 QGraphicsViewQGraphicsScene 实现基础圆形绘制,以及进阶的色轮(Color Wheel)效果。
色轮是色彩选择器的常见控件,广泛应用于图形设计、绘画和 UI 取色等场景。本文将详细讲解两种常见的色轮实现方式,并配以完整代码和效果图。


一、QGraphicsView/QGraphicsScene 绘制圆形

1. 原理说明

Qt 的 QGraphicsView/QGraphicsScene 提供了强大的 2D 图形视图框架。QGraphicsScene 负责管理所有图形项,QGraphicsView 负责显示场景内容。
绘制圆形时,常用 QGraphicsEllipseItem,通过设置其矩形区域和填充颜色即可实现。

2. 代码实现

假设我们已经创建了一个 Qt Widgets Application 项目 Scence1。在类的构造函数中创建 QGraphicsSceneQGraphicsView,并添加到 QVBoxLayout 布局中:

Scence1::Scence1(QWidget *parent): QWidget(parent)
{// 创建场景QGraphicsScene* scene = new QGraphicsScene(this);// 创建视图并设置场景QGraphicsView* view = new QGraphicsView(scene, this);paintEllipse(view, scene); // 绘制椭圆//paintColorWheel(view, scene); // 绘制色轮// 使用布局管理器添加视图到窗口QVBoxLayout* layout = new QVBoxLayout(this);layout->addWidget(view);setLayout(layout);ui.setupUi(this);
}void Scence1::paintEllipse(QGraphicsView* view, QGraphicsScene* scene)
{// 设置画笔为蓝色,宽度为1QPen pen;pen.setColor(QColor(0, 0, 255));pen.setWidth(1);// 圆的半径int circleR = 160;// 创建一个椭圆QGraphicsEllipseItem* myEllipseItem = new QGraphicsEllipseItem();myEllipseItem->setRect(0, 0, 2 * circleR, 2 * circleR);myEllipseItem->setPen(pen);myEllipseItem->setBrush(QColor(0, 0, 255));// 添加到场景scene->addItem(myEllipseItem);// 设置视图的场景view->setScene(scene);
}

这样我们可以得到一个蓝色的圆形,完成了第一步:

二、QGraphicsView/QGraphicsScene 实现色轮

色轮是色彩学中常用的工具,通常以 HSV 色彩空间为基础。HSV 色彩空间将颜色分为色相(Hue)、饱和度(Saturation)、明度(Value),非常适合用于色轮的实现。

原理说明

色轮的本质是将色相(Hue)映射到圆周上,不同的实现方式可以带来不同的视觉效果和性能表现。
常见的两种实现方式如下:

方式一:多个扇形拼接色轮

实现思路
将圆分成若干个扇形,每个扇形代表一种色相(Hue)。
通过 HSV 颜色模型,改变色相值,生成不同颜色。
使用 QPainterPath 绘制扇形,并填充对应颜色。
每个扇形作为一个 QGraphicsPathItem 添加到 scene,便于后续交互。
代码实现

void Scence1::paintColorWheel(QGraphicsView* view, QGraphicsScene* scene)
{// 设置圆的半径int radius = 150;// 设置扇形数量int numSegments = 360;// 遍历每个扇形for (int i = 0; i < numSegments; ++i) {// 计算当前扇形的起始角度和结束角度qreal startAngle = i * 16;qreal endAngle = (i + 1) * 16;// 创建一个 QPainterPath 对象QPainterPath path;// 移动到圆心path.moveTo(0, 0);// 绘制扇形路径path.arcTo(-radius, -radius, 2 * radius, 2 * radius, startAngle, 16);// 填充颜色QColor color = QColor::fromHsv(i, 255, 255);QBrush brush(color);// 创建一个 QGraphicsPathItem 对象QGraphicsPathItem* item = new QGraphicsPathItem(path);item->setBrush(brush);// 添加到场景scene->addItem(item);}// 设置视图的场景view->setScene(scene);
}

这样我们就实现了一个简单的色轮效果:

方式二:使用渐变色填充色轮

实现思路
通过 QConicalGradient 创建圆锥形渐变,渐变的颜色从中心向外辐射。
使用 QGraphicsEllipseItem 绘制一个完整的圆形作为色轮的底图。
代码实现

void Scence1::paintColorWheel(QGraphicsView* view, QGraphicsScene* scene)
{// 设置圆的半径int radius = 150;// 创建一个 QConicalGradient 渐变对象QConicalGradient gradient(0, 0, 0);// 添加颜色停靠点for (int i = 0; i < 360; i += 10) {gradient.setColorAt(i / 360.0, QColor::fromHsv(i, 255, 255));}// 创建一个 QGraphicsEllipseItem 对象QGraphicsEllipseItem* item = new QGraphicsEllipseItem();item->setRect(-radius, -radius, 2 * radius, 2 * radius);// 设置渐变填充item->setBrush(gradient);// 添加到场景scene->addItem(item);// 设置视图的场景view->setScene(scene);
}

这种方式实现的色轮更加平滑,过渡自然:

三、总结

本文介绍了如何使用 Qt 的 QGraphicsViewQGraphicsScene 实现圆形及色轮的绘制。
通过两种方式实现色轮:一种是通过多个扇形拼接而成,另一种是使用渐变色填充。读者可以根据需求选择合适的实现方式。


附录:完整代码

#include "scence1.h"
#include "ui_scence1.h"Scence1::Scence1(QWidget *parent): QWidget(parent)
{// 创建场景QGraphicsScene* scene = new QGraphicsScene(this);// 创建视图并设置场景QGraphicsView* view = new QGraphicsView(scene, this);paintEllipse(view, scene); // 绘制椭圆//paintColorWheel(view, scene); // 绘制色轮// 使用布局管理器添加视图到窗口QVBoxLayout* layout = new QVBoxLayout(this);layout->addWidget(view);setLayout(layout);ui.setupUi(this);
}void Scence1::paintEllipse(QGraphicsView* view, QGraphicsScene* scene)
{// 设置画笔为蓝色,宽度为1QPen pen;pen.setColor(QColor(0, 0, 255));pen.setWidth(1);// 圆的半径int circleR = 160;// 创建一个椭圆QGraphicsEllipseItem* myEllipseItem = new QGraphicsEllipseItem();myEllipseItem->setRect(0, 0, 2 * circleR, 2 * circleR);myEllipseItem->setPen(pen);myEllipseItem->setBrush(QColor(0, 0, 255));// 添加到场景scene->addItem(myEllipseItem);// 设置视图的场景view->setScene(scene);
}void Scence1::paintColorWheel(QGraphicsView* view, QGraphicsScene* scene)
{// 设置圆的半径int radius = 150;// 设置扇形数量int numSegments = 360;// 遍历每个扇形for (int i = 0; i < numSegments; ++i) {// 计算当前扇形的起始角度和结束角度qreal startAngle = i * 16;qreal endAngle = (i + 1) * 16;// 创建一个 QPainterPath 对象QPainterPath path;// 移动到圆心path.moveTo(0, 0);// 绘制扇形路径path.arcTo(-radius, -radius, 2 * radius, 2 * radius, startAngle, 16);// 填充颜色QColor color = QColor::fromHsv(i, 255, 255);QBrush brush(color);// 创建一个 QGraphicsPathItem 对象QGraphicsPathItem* item = new QGraphicsPathItem(path);item->setBrush(brush);// 添加到场景scene->addItem(item);}// 设置视图的场景view->setScene(scene);
}void Scence1::paintColorWheel(QGraphicsView* view, QGraphicsScene* scene)
{// 设置圆的半径int radius = 150;// 创建一个 QConicalGradient 渐变对象QConicalGradient gradient(0, 0, 0);// 添加颜色停靠点for (int i = 0; i < 360; i += 10) {gradient.setColorAt(i / 360.0, QColor::fromHsv(i, 255, 255));}// 创建一个 QGraphicsEllipseItem 对象QGraphicsEllipseItem* item = new QGraphicsEllipseItem();item->setRect(-radius, -radius, 2 * radius, 2 * radius);// 设置渐变填充item->setBrush(gradient);// 添加到场景scene->addItem(item);// 设置视图的场景view->setScene(scene);
}void Scence1::paintAxis(QGraphicsScene* scene, int hueCircleR)
{// 画三条分割线QPen pen;pen.setWidth(1);pen.setColor(QColor(0, 0, 0));pen.setStyle(Qt::DashDotLine);pen.setWidth(1);// 分割线1:210度到30度QGraphicsLineItem* splitLine1 = new QGraphicsLineItem();splitLine1->setLine(QLineF(hueCircleR + hueCircleR * cos(210 * 3.14159 / 180), hueCircleR - hueCircleR * sin(210 * 3.14159 / 180),hueCircleR + hueCircleR * cos(30 * 3.14159 / 180), hueCircleR - hueCircleR * sin(30 * 3.14159 / 180)));splitLine1->setPen(pen);// 分割线2:270度到90度QGraphicsLineItem* splitLine2 = new QGraphicsLineItem();splitLine2->setLine(QLineF(hueCircleR + hueCircleR * cos(270 * 3.14159 / 180), hueCircleR - hueCircleR * sin(270 * 3.14159 / 180),hueCircleR + hueCircleR * cos(90 * 3.14159 / 180), hueCircleR - hueCircleR * sin(90 * 3.14159 / 180)));splitLine2->setPen(pen);// 分割线3:330度到150度QGraphicsLineItem* splitLine3 = new QGraphicsLineItem();splitLine3->setLine(QLineF(hueCircleR + hueCircleR * cos(330 * 3.14159 / 180), hueCircleR - hueCircleR * sin(330 * 3.14159 / 180),hueCircleR + hueCircleR * cos(150 * 3.14159 / 180), hueCircleR - hueCircleR * sin(150 * 3.14159 / 180)));splitLine3->setPen(pen);// 添加分割线到场景scene->addItem(splitLine1);scene->addItem(splitLine2);scene->addItem(splitLine3);// 设置字体QFont font("Arial", 8);font.setBold(true);// 添加文字标注QGraphicsTextItem* textItem1 = new QGraphicsTextItem();textItem1->setPlainText(QString("CB\n210"));textItem1->setFont(font);textItem1->setPos(hueCircleR + hueCircleR * cos(210 * 3.14159 / 180) - 30, hueCircleR - hueCircleR * sin(210 * 3.14159 / 180) - 20);QGraphicsTextItem* textItem2 = new QGraphicsTextItem();textItem2->setPlainText(QString("30\nRY"));textItem2->setFont(font);textItem2->setPos(hueCircleR + hueCircleR * cos(30 * 3.14159 / 180) + 5, hueCircleR - hueCircleR * sin(30 * 3.14159 / 180) - 20);QGraphicsTextItem* textItem3 = new QGraphicsTextItem();textItem3->setPlainText(QString("BM 270"));textItem3->setFont(font);textItem3->setPos(hueCircleR + hueCircleR * cos(270 * 3.14159 / 180) - 30, hueCircleR - hueCircleR * sin(270 * 3.14159 / 180) + 0);QGraphicsTextItem* textItem4 = new QGraphicsTextItem();textItem4->setPlainText(QString("90 YG"));textItem4->setFont(font);textItem4->setPos(hueCircleR + hueCircleR * cos(90 * 3.14159 / 180) - 20, hueCircleR - hueCircleR * sin(90 * 3.14159 / 180) - 20);QGraphicsTextItem* textItem5 = new QGraphicsTextItem();textItem5->setPlainText(QString("330\nMR"));textItem5->setFont(font);textItem5->setPos(hueCircleR + hueCircleR * cos(330 * 3.14159 / 180) + 5, hueCircleR - hueCircleR * sin(330 * 3.14159 / 180) - 20);QGraphicsTextItem* textItem6 = new QGraphicsTextItem();textItem6->setPlainText(QString("GC\n150"));textItem6->setFont(font);textItem6->setPos(hueCircleR + hueCircleR * cos(150 * 3.14159 / 180) - 30, hueCircleR - hueCircleR * sin(150 * 3.14159 / 180) - 20);// 添加文字到场景scene->addItem(textItem1);scene->addItem(textItem2);scene->addItem(textItem3);scene->addItem(textItem4);scene->addItem(textItem5);scene->addItem(textItem6);
}Pos(hueCircleR + hueCircleR * cos(150 * 3.14159 / 180) - 30, hueCircleR - hueCircleR * sin(150 * 3.14159 / 180) - 20);// 添加文字到场景scene->addItem(textItem1);scene->addItem(textItem2);scene->addItem(textItem3);scene->addItem(textItem4);scene->addItem(textItem5);scene->addItem(textItem6);
}

相关文章:

  • 智能体应用如何重塑未来生活?全面解析技术场景与实在Agent突破
  • Linux权限练习题
  • 3.安卓逆向2-安卓文件目录
  • BUUCTF PWN刷题笔记(持续更新!!)
  • Unreal Engine: Windows 下打包 AirSim项目 为 Linux 平台项目
  • Chromium 回调设计实战:BindOnce 与 BindRepeating 的最佳实践
  • stm32如何触摸屏设置显示按钮
  • JetpackCompose基础学习2.2
  • C++ 与 Python 内存分配策略对比
  • C# 枚举 详解
  • LeetCode 217.存在重复元素
  • C++:因子问题
  • 【TTS回顾】Bert-VITS2深度解析:融合BERT的多语言语音合成模型
  • Python爬虫实战:获取国家统计网最新消费数据并分析,为从业者做参考
  • Spring Boot入门案例(Spring Initializr方式,IDEA版)
  • FANUC发那科焊接机器人智能气阀
  • Windows环境使用NVM高效管理多个Node.js版本
  • 可重入(Reentrant) vs 线程安全(Thread-Safe)
  • AI Agent开发第71课-一个完善的可落地企业AI Agent全架构
  • 视觉-语言导航:综述与类别
  • 俄罗斯哈巴罗夫斯克市首次举办“俄中论坛”
  • 李公明 | 一周画记:德里达哲学还是接地气的
  • 网警打谣:传播涉刘国梁不实信息,2人被处罚
  • 首次公布!我国空间站内发现微生物新物种
  • 国家统计局公布2024年城镇单位就业人员年平均工资情况
  • 马上评|清理“滥竽充数者”,为医者正名