使用 Qt QGraphicsView/QGraphicsScene 绘制色轮

发布于:2025-05-21 ⋅ 阅读:(19) ⋅ 点赞:(0)

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


网站公告

今日签到

点亮在社区的每一天
去签到