使用 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)
{
// 设置画笔为蓝色,宽度为1
QPen 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)
{
// 设置画笔为蓝色,宽度为1
QPen 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);
}