使用 Qt QGraphicsView/QGraphicsScene 绘制色轮
本文介绍如何在 Qt 中利用 QGraphicsView
和 QGraphicsScene
实现基础圆形绘制,以及进阶的色轮(Color Wheel)效果。
色轮是色彩选择器的常见控件,广泛应用于图形设计、绘画和 UI 取色等场景。本文将详细讲解两种常见的色轮实现方式,并配以完整代码和效果图。
一、QGraphicsView/QGraphicsScene 绘制圆形
1. 原理说明
Qt 的 QGraphicsView
/QGraphicsScene
提供了强大的 2D 图形视图框架。QGraphicsScene
负责管理所有图形项,QGraphicsView
负责显示场景内容。
绘制圆形时,常用 QGraphicsEllipseItem
,通过设置其矩形区域和填充颜色即可实现。
2. 代码实现
假设我们已经创建了一个 Qt Widgets Application 项目 Scence1。在类的构造函数中创建 QGraphicsScene
和 QGraphicsView
,并添加到 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 的 QGraphicsView
和 QGraphicsScene
实现圆形及色轮的绘制。
通过两种方式实现色轮:一种是通过多个扇形拼接而成,另一种是使用渐变色填充。读者可以根据需求选择合适的实现方式。
附录:完整代码
#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);
}