QT使用QPainter绘制多边形维度图

news2024/12/25 10:11:08

        多边形统计维度图是一种用于展示多个维度的数据的图表。它通过将各个维度表示为图表中的多边形的边,根据数据的大小和比例来确定各个维度的长度。

一、简述

         本示例实现六边形战力统计维度图,一种将六个维度的战力统计以六边形图形展示的方法。六个维度是:攻击力、防御力、速度、智力、生命值、特殊能力。六边形战力统计维度图将这些维度以六个边长不等的六边形表示,每个边长代表对应维度的数值大小。通过连接这些边,可以得到一个多边形,多边形的形状和大小表示单位的整体战斗能力。一般来说,这个多边形越接近完整的六边形,说明单位在各项能力上都相对均衡和强大。

二、 设计思路        

        要实现多边形统计维度图,基类选择QWidget,使用QPainter根据图形需求画出图形。

设计思路:

  1. 将统计维度分别表示为顶点,分别标注在顶点上。这些统计维度可以根据具体需求进行选择,例如攻击力、防御力、速度等。

  2. 在多边形图的上,可以设置不同的刻度值,表示不同的水平。根据统计数据的大小,可以将数据点标注在相应的位置上。

  3. 使用不同的颜色或图案来表示不同的统计数据点,以便更好地区分不同的数据。

  4. 在六边形图的周围添加标签,用来说明各个战力统计维度的含义,以便用户更好地理解图表。

三、效果 

四、核心代码  
1、头文件
#ifndef DIMENSIONCHARTWIDGET_H
#define DIMENSIONCHARTWIDGET_H

#include <QMap>
#include <QPen>
#include <QObject>
#include <QWidget>

class DimensionInfo;
class DimensionChartWidget : public QWidget
{
    Q_OBJECT

public:
    explicit DimensionChartWidget(QWidget *parent = nullptr);

    inline QColor backgroundColor() const { return m_backgroundColor; }
    inline void setBackgroundColor(const QColor &backgroundColor) { m_backgroundColor = backgroundColor; }

    inline float radius() const { return m_radius; }// 设置维度半径
    inline void setRadius(float radius) { m_radius = radius; }

    inline int sidesNumber() const { return m_sidesNumber; } // 设置维度网格数量
    inline void setSidesNumber(int sidesNumber) {m_sidesNumber = sidesNumber; }

    inline QPen sidesPen() const { return m_sidesPen; }// 设置维度网格画笔
    inline void setSidesPen(const QPen &sidesPen) { m_sidesPen = sidesPen; }

    inline QVector<DimensionInfo> dimensionInfos() const { return m_dimensionInfos; }
    inline void setDimensionInfos(const QVector<DimensionInfo> &dimensionInfos) { m_dimensionInfos = dimensionInfos; }

    inline QPen textPen() const { return m_textPen; }//设置字体画笔
    inline void setTextPen(const QPen &textPen) { m_textPen = textPen; }

    inline QPen dimensionPen() const { return m_dimensionPen; }// 设置维度画笔
    inline void setDimensionPen(const QPen &dimensionPen) { m_dimensionPen = dimensionPen; }

    inline QFont textFont() const { return m_textFont; }//设置字体
    inline void setTextFont(const QFont &textFont) { m_textFont = textFont; }

    inline int filletRadius() const { return m_filletRadius; }
    inline void setFilletRadius(int filletRadius) { m_filletRadius = filletRadius; }

protected:
    void paintEvent(QPaintEvent*);
    void drawText(QPainter&, QPointF, QString text);
    void convertPoint(QPointF&);

private:
    void init();

private:
    QPen m_textPen;
    QPen m_sidesPen;
    QPen m_dimensionPen;
    QFont m_textFont;
    QColor m_backgroundColor;

    int m_filletRadius;
    float m_radius;
    int m_sidesNumber;
    QVector<DimensionInfo> m_dimensionInfos;
};


class DimensionInfo {
public:
    DimensionInfo() = default;
    DimensionInfo(QString text, float percentage) { m_text = text; m_percentage = percentage; }

    inline QString text() const { return m_text; }
    inline void setText(const QString &text) { m_text = text; }

    inline float percentage() const { return m_percentage; }
    inline void setPercentage(float percentage) { m_percentage = percentage; }

private:
    QString m_text;
    float m_percentage;
};
#endif // DIMENSIONCHARTWIDGET_H
2、实现代码
#include "DimensionChartWidget.h"
#include <QPainter>
#include <QtMath>
#include <QPainterPath>

DimensionChartWidget::DimensionChartWidget(QWidget *parent) : QWidget(parent)
{
    m_radius = 0;
    m_sidesNumber = 1;

    init();
}

void DimensionChartWidget::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing);

    painter.setPen(QColor(m_backgroundColor));
    painter.setBrush(QBrush(m_backgroundColor));
    painter.drawRoundedRect(rect(), m_filletRadius, m_filletRadius);

    // 绘图设备的坐标原点(0,0)在左上角,水平向右增长,垂直向下增长。
    // 将坐标系原点移动到界面中间
    painter.translate(width() / 2.0, height() / 2.0);

    // 设中心点到边的垂线与半径的夹角为degree=(360/count)/2
    float degree = 360.0 / m_dimensionInfos.size();

    // 开始绘制多边形,并为每个区域上色
    painter.setPen(m_sidesPen);
    QPointF lastPoint(0, -m_radius);
    QVector<QPointF> points;
    for(int i = 0; i < m_dimensionInfos.size(); i++)
    {
        float radian = qDegreesToRadians(degree * (i + 1));
        float x = m_radius * qSin(radian);
        float y = m_radius * qCos(radian);

        // 绘制该三角区块
        QPainterPath path;
        QPointF point(x, -y);
        path.lineTo(point);
        path.lineTo(lastPoint);
        path.lineTo(0, 0);
        painter.drawPath(path);

        //  绘制内线
        for(int j = m_sidesNumber - 1; j > 0; j--)
        {
            float multiple = (float)j / m_sidesNumber;
            painter.drawLine(point * multiple, lastPoint * multiple);
        }

        // 绘制文本
        painter.save();
        painter.setPen(m_textPen);
        painter.setFont(m_textFont);
        drawText(painter, point, m_dimensionInfos.at(i).text());
        painter.restore();

        lastPoint = point;
        points << point * m_dimensionInfos.at(i).percentage();
    }

    // 绘制维度信息
    painter.setPen(m_dimensionPen);
    QColor color = m_dimensionPen.color();
    color.setAlpha(150);
    painter.setBrush(QBrush(color));
    QPolygonF polygon(points);
    QPainterPath painterPath;
    painterPath.addPolygon(polygon);
    painter.drawPolygon(polygon);
}

//绘制文本
void DimensionChartWidget::drawText(QPainter& painter, QPointF point, QString text)
{
    convertPoint(point);
    QRectF textRect;
    textRect.setSize(QSize(50, 30));
    int flag = Qt::AlignCenter;
    if(point.x() > 0)
    {
        if(point.y() < 0)
        {
            //x > 0 y < 0
            textRect.setBottomLeft(point);
            textRect.setTopRight(QPoint(point.x() + 50, point.y() - 30));
            flag = Qt::AlignBottom | Qt::AlignLeft;
        }
        else if(point.y() > 0)
        {
            //x>0 y>0
            textRect.setTopLeft(point);
            textRect.setBottomRight(QPoint(point.x() + 50, point.y() + 30));
            flag = Qt::AlignTop | Qt::AlignLeft;
        }
        else
        {
            //x>0 y=0
            point.setY(point.y() - 15);
            textRect.setTopLeft(point);
            textRect.setBottomRight(QPoint(point.x() + 50, point.y() + 30));
            flag = Qt::AlignVCenter | Qt::AlignLeft;
        }
    }
    else if(point.x() < 0)
    {
        if(point.y() < 0)
        {
            //x<0 y<0
            textRect.setBottomRight(point);
            textRect.setTopLeft(QPoint(point.x() - 50, point.y() - 30));
            flag = Qt::AlignBottom | Qt::AlignRight;
        }
        else if(point.y() > 0)
        {
            //x<0 y>0
            textRect.setTopRight(point);
            textRect.setBottomLeft(QPoint(point.x() - 50, point.y() + 30));
            flag = Qt::AlignTop | Qt::AlignRight;
        }
        else
        {
            //x<0 y=0
            point.setY(point.y() - 15);
            textRect.setTopRight(point);
            textRect.setBottomLeft(QPoint(point.x() - 50, point.y() + 30));
            flag = Qt::AlignVCenter | Qt::AlignRight;
        }
    }
    else
    {
        if(point.y() < 0)
        {
            //x=0 y<0
            point.setX(point.x() - 25);
            textRect.setBottomRight(point);
            textRect.setTopLeft(QPoint(point.x() + 50, point.y() - 30));
            flag = Qt::AlignHCenter | Qt::AlignBottom;
        }
        else if(point.y() > 0)
        {
            //x=0 y>0
            point.setX(point.x() - 25);
            textRect.setTopLeft(point);
            textRect.setBottomRight(QPoint(point.x() + 50, point.y() + 30));
            flag = Qt::AlignHCenter | Qt::AlignTop;
        }
    }
    painter.drawText(textRect, flag, text);
}

void DimensionChartWidget::convertPoint(QPointF& point)
{
    if(qAbs(point.x()) < 0.001)
    {
        point.setX(0);
    }
    else if(qAbs(point.y()) < 0.001)
    {
        point.setY(0);
    }
}

void DimensionChartWidget::init()
{
    setBackgroundColor(QColor(255,255,255));

    // 设置维度网格数量
    setSidesNumber(5);

    // 设置维度半径
    setRadius(120);

    // 设置维度网格画笔
    QPen sidesPen;
    sidesPen.setColor(QColor("#0095C5"));
    sidesPen.setWidth(2);
    setSidesPen(sidesPen);

    // 设置维度画笔
    QPen dimensionPen;
    dimensionPen.setColor(Qt::GlobalColor::red);
    dimensionPen.setWidth(3);
    setDimensionPen(dimensionPen);

    // 设置字体信息
    QPen textPen;
    setTextPen(textPen);
    QFont textFont;
    textFont.setFamily("微软雅黑");
    textFont.setPointSize(10);
    setTextFont(textFont);
}

        以上是多边形统计维度图实现代码,在实际使用中,可以根据需要进行扩展,添加一些其他的功能。例如:

  1. 支持多边形的操作。使其支持多边形的旋转、平移、缩放等操作,以方便更灵活地对多边形进行分析和展示。

  2. 导入和导出多边形数据。我们可以添加文件读写功能,允许用户从外部文件导入多边形数据,并将统计维度的结果保存到文件中。

        这些功能的添加可以进一步提升多边形统计维度图的实用性和灵活性。根据具体的需求,我们可以选择扩展代码的功能,以适应不同的应用场景。      

五、使用示例

以下是一个简单的示例代码,演示了如何在Qt中使用此控件:

#include "mainwindow.h"
#include "DimensionChartWidget.h"

MainWindow::MainWindow(QWidget *parent)
    : QMainWindow(parent)
{
    setWindowTitle("维度图");
    resize(400, 400);

    DimensionChartWidget* pDimensionChartWidget = new DimensionChartWidget(this);
    pDimensionChartWidget->resize(this->width() - 60, this->height() -60);
    pDimensionChartWidget->move(30, 30);

    QVector<DimensionInfo> dimensionInfos;
    DimensionInfo dimensionInfo("攻击力", 0.55);
    dimensionInfos.append(dimensionInfo);
    dimensionInfo.setText("防御力");
    dimensionInfo.setPercentage(0.85);
    dimensionInfos.append(dimensionInfo);
    dimensionInfo.setText("速度");
    dimensionInfo.setPercentage(0.95);
    dimensionInfos.append(dimensionInfo);
    dimensionInfo.setText("智力");
    dimensionInfo.setPercentage(0.45);
    dimensionInfos.append(dimensionInfo);
    dimensionInfo.setText("生命值");
    dimensionInfo.setPercentage(0.65);
    dimensionInfos.append(dimensionInfo);
    dimensionInfo.setText("特殊能力");
    dimensionInfo.setPercentage(0.65);
    dimensionInfos.append(dimensionInfo);
    pDimensionChartWidget->setDimensionInfos(dimensionInfos);
}

MainWindow::~MainWindow()
{

}

        以上示例是一个六边形战力统计维度图,可以用于综合评估某个实体在不同维度上的战力表现。通过提供各个维度的评分和连接评分最高点的边界来展示实体在不同维度上的能力表现。这样的图表可以帮助决策者更全面地了解和比较实体的战力,从而做出更准确的决策。

        谢谢您的关注和阅读!如有任何问题或需要帮助,请随时与我联系。希望您能继续支持并享受更多精彩的内容。祝您生活愉快!

六、源代码下载

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1930958.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

leetcode-383.赎金信

题源 383.赎金信 题目描述 给你两个字符串&#xff1a;ransomNote 和 magazine &#xff0c;判断 ransomNote 能不能由 magazine 里面的字符构成。如果可以&#xff0c;返回 true &#xff1b;否则返回 false 。magazine 中的每个字符只能在 ransomNote 中使用一次。示例 1&…

MySQL(3)表的操作

目录 1. 表的操作; 2. 数据类型; 1. 表的操作: 1.1 创建表: 语法: create table 表名( 属性 类型 [comment ], 属性 类型 [comment ], 属性 类型 ) character set 字符集 collate 校验集 engine 存储引擎; 前面博客提到: MyISAM和InoDB这两个比较重要. 1.2 查看表…

Spring与设计模式实战之策略模式

Spring与设计模式实战之策略模式 引言 在现代软件开发中&#xff0c;设计模式是解决常见设计问题的有效工具。它们提供了经过验证的解决方案&#xff0c;帮助开发人员构建灵活、可扩展和可维护的系统。本文将探讨策略模式在Spring框架中的应用&#xff0c;并通过实际例子展示…

three.js领衔,10大基于webGL的JavaScript库。

Three.js的赫赫威名补多少&#xff0c;不了解的自行搜索或者翻看大宇之前的文章&#xff0c;除了three.js外&#xff0c;我想实现web3D效果还有其他库吗&#xff1f;答案是有的&#xff0c;而且还不少。 除了 Three.js&#xff0c;还有一些基于 WebGL 的库和框架&#xff0c;它…

动态环境下的激光slam论文列表

文章目录 Scan Context: Egocentric Spatial Descriptor for Place Recognition within 3D Point Cloud Map&#xff08;2018&#xff09;LIO-CSI: LiDAR inertial odometry with loop closure combined with semantic information&#xff08;2021&#xff09;Semantic Lidar-…

防火墙--双机热备

目录 双击热备作用 防火墙和路由器备份不同之处 如何连线 双机 热备 冷备 VRRP VGMP&#xff08;华为私有协议&#xff09; 场景解释 VGMP作用过程 主备的形成场景 接口故障的切换场景 整机故障 原主设备故障恢复的场景 如果没有开启抢占 如果开启了抢占 负载分…

网络原理(上)

前言&#x1f440;~ 上一章我们介绍了网络的一些基础知识&#xff0c;今天来讲解一下网络原理相关的知识点&#xff0c;分三篇进行阐述内容有点多​​​​​​​ 再谈协议分层 应用层 传输层&#xff08;重点&#xff09; UDP协议 TCP协议 TCP如何完成可靠传输&#xff…

在 PostgreSQL 里如何处理数据的归档和清理过程中的数据完整性验证?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 在 PostgreSQL 里如何处理数据的归档和清理过程中的数据完整性验证 在 PostgreSQL 里如何处理数据的归…

3D数字孪生项目运行卡顿,来看看它要求的电脑配置。

有些小伙伴和我说&#xff0c;数字孪生项目运行卡顿&#xff0c;不知道啥原因&#xff0c;根源还是这类项目是浏览器渲染&#xff0c;对电脑配置要求很高。 运行3D数字孪生项目需要一台性能强大的电脑&#xff0c; 以下是一个推荐的配置清单&#xff1a; 1. 处理器&#xff1…

css实现每个小盒子占32%,超出就换行

代码 <div class"visitors"><visitor class"item" v-for"(user,index) in userArr" :key"user.id" :user"user" :index"index"></visitor></div><style lang"scss" scoped&…

Porfinet转DeviceNet主总线协议转换网关

产品功能 1. 远创智控YC-DNTM-PN型网关是Porfinet从转Devicenet主工业级Porfinet网关。‌这种网关设备允许将Porfinet网络中的设备连接到Devicenet网络中&#xff0c;‌从而实现不同工业通信协议之间的互操作性。‌这些网关设备通常具有两个以太网接口&#xff0c;‌分别用于连…

shell脚本-linux如何在脚本中远程到一台linux机器并执行命令

需求&#xff1a;我们需要从11.0.1.17远程到11.0.1.16上执行命令 实现&#xff1a; 1.让11.0.1.17 可以免密登录到11.0.1.16 [rootlocalhost ~]# ssh-keygen Generating public/private rsa key pair. Enter file in which to save the key (/root/.ssh/id_rsa): Created d…

Ubuntu 22.04.4 LTS (linux) 安装iftop 监控网卡流量 软件

1 安装iftop sudo apt update sudo apt-get install iftop 2 监控网卡 sudo iftop -i eth0 -n -p 界面最上面&#xff0c;显示的是类似刻度尺的刻度范围&#xff0c;显示流量图形的长条作标尺用的。 中间的< >这两个左右箭头&#xff0c;表示的是流量的进出方向.TX&…

使用JS和CSS制作的小案例(day二)

一、写在开头 本项目是从github上摘取&#xff0c;自己练习使用后分享&#xff0c;方便登录github的小伙伴可以看本篇文章 50项目50天​编辑https://github.com/bradtraversy/50projects50dayshttps://github.com/bradtraversy/50projects50days有兴趣的小伙伴可以自己去gith…

美式键盘 QWERTY 布局的起源

注&#xff1a;机翻&#xff0c;未校对。 The QWERTY Keyboard Is Tech’s Biggest Unsolved Mystery QWERTY 键盘是科技界最大的未解之谜 It’s on your computer keyboard and your smartphone screen: QWERTY, the first six letters of the top row of the standard keybo…

Redis的热key解决

1、Redis热Key会带来哪些问题 1、流量集中&#xff0c;达到物理网卡上限。 当某一热点 Key 的请求在某一主机上超过该主机网卡上限时&#xff0c;由于流量的过度集中&#xff0c;会导致服务器中其它服务无法进行。 2、请求过多&#xff0c;缓存分片服务被打垮。 如果热点过于…

Linux入门笔记(指令)

操作系统是什么&#xff1f; 操作系统是一款做软硬件管理的软件。计算机系统自下而上可以大致分为4部分&#xff1a;硬件、操作系统、应用程序和用户。操作系统管理各种计算机硬件&#xff0c;为应用程序提供基础&#xff0c;并且充当计算机硬件与用户之间的中介。重点&#x…

泛微e-cology WorkflowServiceXml SQL注入漏洞(POC)

漏洞描述&#xff1a; 泛微 e-cology 是泛微公司开发的协同管理应用平台。泛微 e-cology v10.64.1的/services/接口默认对内网暴露&#xff0c;用于服务调用&#xff0c;未经身份认证的攻击者可向 /services/WorkflowServiceXml 接口发送恶意的SOAP请求进行SQL注入&#xff0c;…

高并发项目(一):内存池的概念/定长内存池的实现

目录 一、池化技术及其优势 1.1什么是池化技术 1.2内存池的作用 1.3malloc的原理 二、定长内存池的实现 一、池化技术及其优势 1.1什么是池化技术 所谓 “ 池化技术 ” &#xff0c;就是程序先向系统申请过量的资源&#xff0c;然后自己管理&#xff0c;以备不时之需。之所…

微调 Florence-2 - 微软的尖端视觉语言模型

Florence-2 是微软于 2024 年 6 月发布的一个基础视觉语言模型。该模型极具吸引力&#xff0c;因为它尺寸很小 (0.2B 及 0.7B) 且在各种计算机视觉和视觉语言任务上表现出色。 Florence 开箱即用支持多种类型的任务&#xff0c;包括: 看图说话、目标检测、OCR 等等。虽然覆盖面…