使用 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);
}