使用 Qt QGraphicsView/QGraphicsScene 绘制色轮
使用 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);
}
相关文章:

使用 Qt QGraphicsView/QGraphicsScene 绘制色轮
使用 Qt QGraphicsView/QGraphicsScene 绘制色轮 本文介绍如何在 Qt 中利用 QGraphicsView 和 QGraphicsScene 实现基础圆形绘制,以及进阶的色轮(Color Wheel)效果。 色轮是色彩选择器的常见控件,广泛应用于图形设计、绘画和 UI …...
游戏开发实战(三):Python复刻「崩坏星穹铁道」嗷呜嗷呜事务所---源码级解析该小游戏背后的算法与设计模式【纯原创】
文章目录 奇美拉类摸鱼仔,负能量,真老实,小坏蛋,压力怪治愈师小团体画饼王平凡王坏脾气抗压包请假狂请假王内卷王受气包跑路侠看乐子背锅侠抢功劳急先锋说怪话帮倒忙小夸夸工作狂职业经理严酷恶魔职场清流 开始工作吧小奇美拉没想…...

使用glsl 来做视频矫正
描述、优点 使用glsl来代替opencv的undistort 和 鱼眼矫正,并且最后使用opencv的LUT给glsl 来使用,来达到加速的目的,并且做到和opencv 一模一样的效果,达到实时视频的加速矫正。 优点: 没有cuda,也可以做到实时视频矫正,包含各类板子和amd的cpu,intel核显 矫正的基本作…...

03-Web后端基础(Maven基础)
1. 初始Maven 1.1 介绍 Maven 是一款用于管理和构建Java项目的工具,是Apache旗下的一个开源项目 。 Apache 软件基金会,成立于1999年7月,是目前世界上最大的最受欢迎的开源软件基金会,也是一个专门为支持开源项目而生的非盈利性…...
LLM驱动下的软件工程再造:驾驭调试、测试与工程化管理的智能新范式
摘要: 大语言模型(LLM)驱动的软件开发正以前所未有的力量重塑整个行业,从以人为中心的编码模式迅速转向意图驱动和AI编排的智能生成。这场变革带来了生产力的指数级飞跃,但也对传统软件工程中调试、测试和代码工程化管理的核心支柱发起了深刻挑战。本文将剖析这些根本性转…...
大语言模型与人工智能:技术演进、生态重构与未来挑战
目录 技术演进:从专用AI到通用智能的跃迁核心能力:LLM如何重构AI技术栈应用场景:垂直领域的技术革命生态关系:LLM与AI技术矩阵的协同演进挑战局限:智能天花板与伦理困境未来趋势:从语言理解到世界模型1. 技术演进:从专用AI到通用智能的跃迁 1.1 三次技术浪潮的跨越 #me…...
SpringSecurity授权、认证
引入依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency><groupId>org.springframework.boot</groupId><artifactI…...

蓝桥杯19682 完全背包
问题描述 有 N 件物品和一个体积为 M 的背包。第 i 个物品的体积为 vi,价值为 wi。每件物品可以使用无限次。 请问可以通过什么样的方式选择物品,使得物品总体积不超过 M 的情况下总价值最大,输出这个最大价值即可。 输入格式 第一行…...

DeepSeek源码解构:从MoE架构到MLA的工程化实现
文章目录 **一、代码结构全景:从模型定义到分布式训练****二、MoE架构:动态路由与稀疏激活的工程化实践****1. 专家路由机制(带负载均衡)****数学原理:负载均衡损失推导** **三、MLA注意力机制:低秩压缩与解…...
leetcode 3355. 零数组变换 I 中等
给定一个长度为 n 的整数数组 nums 和一个二维数组 queries,其中 queries[i] [li, ri]。 对于每个查询 queries[i]: 在 nums 的下标范围 [li, ri] 内选择一个下标 子集。将选中的每个下标对应的元素值减 1。 零数组 是指所有元素都等于 0 的数组。 …...

【VLNs篇】02:NavGPT-在视觉与语言导航中使用大型语言模型进行显式推理
方面 (Aspect)内容总结 (Content Summary)论文标题NavGPT: 在视觉与语言导航中使用大型语言模型进行显式推理 (NavGPT: Explicit Reasoning in Vision-and-Language Navigation with Large Language Models)核心问题探究大型语言模型 (LLM) 在复杂具身场景(特别是视…...

(T_T),不小心删掉RabbitMQ配置文件数据库及如何恢复
一、不小心删除 今天是2025年5月15日,非常沉重的一天,就在今早8点左右的时候我打算继续做我的毕业设计,由于开机的过程十分缓慢(之前没有),加上刚开机电脑有卡死的迹象,再加上昨天晚上关电脑前…...
创建react工程并集成tailwindcss
1. 创建工程 npm create vite admin --template react 2.集成tailwndcss 打开官网跟着操作一下就行。 Installing Tailwind CSS with Vite - Tailwind CSS...

TDengine 安全部署配置建议
背景 TDengine 的分布式、多组件特性导致 TDengine 的安全配置是生产系统中比较关注的问题。本文档旨在对 TDengine 各组件及在不同部署方式下的安全问题进行说明,并提供部署和配置建议,为用户的数据安全提供支持。 安全配置涉及组件 TDengine 包含多…...
Axure全链路交互设计:快速提升实现能力(基础交互+高级交互)
想让你的设计稿像真实App一样丝滑?本专栏带你玩转Axure交互,从选中高亮到动态面板骚操作,再到中继器表单花式交互,全程动图教学,一看就会! 本专栏系统讲解多个核心交互效果,是你的Axure交互急救…...
为什么wifi有信号却连接不上?
WiFi有信号,无法连接WiFi网络的可能原因及解决方法: 1.长时间使用路由器,路由器可能会出现假死现象。重启无线路由器即可。 2.认证类型不合适。尝试更改路由器的认证类型,选择安全的 “WPA2-PSK” 类型模式要好,下面…...

蓝桥杯框架-LED蜂鸣器继电器
蓝桥杯框架-LED蜂鸣器继电器 一,新建工程文件二,配置keil三,完善框架 一,新建工程文件 在桌面上新建一个文件夹:用于存放所有工程文件 在文件夹中再建立一个文件夹DEMO_01:这是我们的第一个工程文件 在第…...

uniapp-商城-64-后台 商品列表(商品修改---页面跳转,深浅copy应用,递归调用等)
完成了商品的添加和展示,下面的文字将继续进行商品页面的处理,主要为商品信息的修改的页面以及后天逻辑的处理。 本文主要介绍了商品信息修改页面的实现过程。首先,页面布局包括编辑和删除功能,未来还可添加上架和下架按钮。通过c…...

Dify的大语言模型(LLM) AI 应用开发平台-本地部署
前言 今天闲着,捣鼓一下 Dify 这个开源平台,在 mac 系统上,本地部署并运行 Dify 平台,下面记录个人在本地部署Dify 的过程。 Dify是什么? Dify是一个开源的大语言模型(LLM)应用开发平台&#…...

使用教程:8x16模拟开关阵列可级联XY脚双向导通自动化接线
以下通过点亮LED进行基本使用流程演示,实际可以连接复杂外设(SPI、CAN、ADC等) 单模块使用 RX、TX、5V和GND接到串口模块;X5接5V;Y2接LED;LED-接GND 串口模块插上电脑后,LED没有亮;因为此时模…...
移动端前端调试调研纪实:从痛点出发,到 WebDebugX 的方案落地
这个月我接到一个内部调研任务:为公司的新一代 Hybrid 框架选型合适的前端调试解决方案。初衷其实很简单——以前的调试方式效率太低,影响开发和测试协同,产品问题总是复现难、修复慢。 于是我花了两周时间,试用了包括 Eruda、Re…...

8 种快速易用的Python Matplotlib数据可视化方法
你是否曾经面对一堆复杂的数据,却不知道如何让它们变得直观易懂?别慌,Python 的 Matplotlib 库是你数据可视化的最佳伙伴!它简单易用、功能强大,能将枯燥的数字变成引人入胜的图表。无论是学生、数据分析师还是程序员&…...
【android bluetooth 协议分析 02】【bluetooth hal 层详解 3】【高通蓝牙hal主要流程介绍-上】
1. 背景 本节主要讨论 高通 蓝牙 hal 中,的一些流程。 看看你是否都清楚如下问题: 高通芯片电如何控制?串口是在哪里控制的?固件如何下载?初始化流程是怎么样的? 如果你已经对上述讨论的问题,…...

C# 深入理解类(实例构造函数)
实例构造函数 实例构造函数是一个特殊的方法,它在创建类的每个新实例时执行。 构造函数用于初始化类实例的状态。如果希望能从类的外部创建类的实例,需要将构造函数声明为public。 图7-2阐述了构造函数的语法。除了下面这几点,构造函数看起…...

RabbitMQ——消息确认
一、消息确认机制 生产者发送的消息,可能有以下两种情况: 1> 消息消费成功 2> 消息消费失败 为了保证消息可靠的到达消费者(!!!注意:消息确认机制和前面的工作模式中的publisher confi…...

测试W5500的第2步_使用ioLibrary库创建TCP客户端
ioLibrary库下载地址:文件下载地址:https://gitee.com/wiznet-hk/STM32F10x_W5500_Examples 源文件下载地址:https://gitee.com/wiznet-hk 没有注册的,只能复制粘贴了。 本文介绍了如何初始化STM32的硬件资源,配置W5500的网络参数ÿ…...

深度学习之用CelebA_Spoof数据集搭建一个活体检测-训练好的模型用MNN来推理
一、模型转换准备 首先确保已完成PyTorch到ONNX的转换:深度学习之用CelebA_Spoof数据集搭建活体检测系统:模型验证与测试。这里有将PyTorch到ONNX格式的模型转换。 二、ONNX转MNN 使用MNN转换工具进行格式转换:具体的编译过程可以参考MNN的…...
【Java】泛型在 Java 中是怎样实现的?
先说结论 , Java 的泛型是伪泛型 , 在运行期间不存在泛型的概念 , 泛型在 Java 中是 编译检查 运行强转 实现的 泛型是指 允许在定义类 , 接口和方法时使用的类型参数 , 使得代码可以在不指定具体类型的情况下操作不同的数据类型 , 从而实现类型安全的代码复用 的语言机制 . …...

开源安全大模型Foundation-Sec-8B实操
一、兴奋时刻 此时此刻,晚上22点55分,从今天早上6点左右开始折腾,花费了接近10刀的环境使用费,1天的休息时间,总算是把Foundation-Sec-8B模型跑起来了,中间有两次胜利就在眼前,但却总在远程端口转发环节出问题,让人难受。直到晚上远程Jupyter访问成功那一刻,眉开眼笑,…...

【JavaWeb】MySQL
1 引言 1.1 为什么学? 在学习SpringBootWeb基础知识(IOC、DI等)时,在web开发中,为了应用程序职责单一,方便维护,一般将web应用程序分为三层,即:Controller、Service、Dao 。 之前的案例中&am…...