qt绘制饼状图并实现点击即放大点击部分

做得比较low

cpp 复制代码
#ifndef TEST_POWER_H
#define TEST_POWER_H

#include <QWidget>
#include <QtMath>
#include <QPainter>
#include <QPushButton>
#include <QVector>
#include <cmath>

namespace Ui {
class test_power;
}
struct PieData
{
    QString name;   //名称
    int num;        //数量
    QColor color;   //颜色

    PieData(QString name,int num,QColor color)
    {
        this->name = name;
        this->num = num;
        this->color = color;
    }
};

struct shanxing{
    double zhanbi;
    double start_x;
    double start_y;
    double end_x;
    double end_y;
};



class test_power : public QWidget
{
    Q_OBJECT

public:
    explicit test_power(int dongli=0,int zhaoming=0,int kongtiao=0,int teshu=0,int clicked=1000,QWidget *parent = nullptr);
    ~test_power();
    QVector<QPointF> a;
    int m_proportion[4],clicked_code;
    void initWidget();
    //圆环图各参数函数接口
    void setRadiusLong(int radius_long);
    void setRadius(int radius);
    void setInnerWidth(int width);
    void setCenter(QPoint center);
    void setStartAngle(qreal startAngle);
    void setTextDistance(int textDistance);
    void setPieData(QVector<PieData> vData);
    void refreshChart();
    shanxing my_shanxing[4];
    int m_radius,m_radius_long;         //外圆半径
    int m_innerWidth;     //圆环内径
    QPoint m_center;      //圆心坐标
    qreal m_startAngle;   //圆环绘制起点
    int m_textDistance;   //文本与圆心的距离
    qreal m_totality;     //总数
    QVector<PieData> m_vData;   //数据容器
    QPointF getArcPoint(const QPoint &center, int radius, double angle);
    void calculateArcPoints(QPainter &painter, const QPoint &center, int radius, double startAngle, double sweepLength,int i);
protected:
    void paintEvent(QPaintEvent *);
    void mousePressEvent(QMouseEvent *event);
private slots:
//    void on_pb_test_clicked();
//    void pb_clicked();
private:
    Ui::test_power *ui;
signals:
//传出你想要传出的参数
   void notice_clicked(int);

};

#endif // TEST_POWER_H
cpp 复制代码
#include "test_power.h"
#include "ui_test_power.h"
#include <QDebug>
#include <QMouseEvent>

test_power::test_power(int dongli,int zhaoming,int kongtiao,int teshu,int clicked,QWidget *parent) :
    QWidget(parent),
    ui(new Ui::test_power)
{
    ui->setupUi(this);
    setAttribute(Qt::WA_DeleteOnClose);
    setWindowFlags(Qt::FramelessWindowHint);
    this->initWidget();
    m_proportion[0] = dongli;
    m_proportion[1] = zhaoming;
    m_proportion[2] = kongtiao;
    m_proportion[3] = teshu;
    clicked_code = clicked;
    //设置圆环各参数
    this->setRadius(160);
    this->setRadiusLong(190);
    this->setInnerWidth(0);   //设为0即为饼图
    this->setCenter(QPoint(275,275));
    this->setStartAngle(0);   //区域绘制方向为逆时针
    this->setTextDistance(210);
    this->refreshChart();
}

test_power::~test_power()
{
    delete ui;
}


//初始化界面
void test_power::initWidget()
{
    //初始化变量
    m_radius = 0;
    m_radius_long=0;
    m_innerWidth = 0;
    m_center = QPoint(0,0);
    m_startAngle = 0;
    m_textDistance = 0;
    m_totality = 0;
    m_vData.clear();
}
//设置外圆长半径
void test_power::setRadiusLong(int radius_long)
{
    m_radius_long=radius_long;
}
//设置外圆半径
void test_power::setRadius(int radius)
{
    m_radius = radius;
}

//设置圆环内径
void test_power::setInnerWidth(int width)
{
    m_innerWidth = width;
}

//设置圆心
void test_power::setCenter(QPoint center)
{
    m_center = center;
}

//设置圆环绘制起点
void test_power::setStartAngle(qreal startAngle)
{
    m_startAngle = startAngle;
}

//设置文本与圆心的距离
void test_power::setTextDistance(int textDistance)
{
    m_textDistance = textDistance;
}

//设置饼图数据
void test_power::setPieData(QVector<PieData> vData)
{
    //获取数据
    m_vData = vData;

    //获取总数
    m_totality = 0;
    for(int i=0;i<m_vData.size();i++)
    {
        m_totality += m_vData[i].num;
    }
    this->update();
}

//更新饼图
void test_power::refreshChart()
{
    //设置圆环图各区域数据名称及颜色
    QVector<PieData> vData;
    QColor m_colors[4] = {QColor(237,117,57),QColor(111,173,30),QColor(198,49,16),QColor(248,206,34)};
    QString m_names[4] = {"quyu1","quyu2","quyu3","quyu4"};
    for(int i=0;i<4;i++)
    {
        if(!m_names[i].isEmpty() && m_proportion[i]>0)
        {
            PieData data = PieData(QString("%1\n%2").arg(m_names[i]).arg(QString::number(m_proportion[i])+"%"),m_proportion[i],m_colors[i]);
            vData.push_back(data);
        }
    }
    this->setPieData(vData);
}

//重写绘图事件
void test_power::paintEvent(QPaintEvent *)
{
    QPainter painter(this);
    painter.setRenderHint(QPainter::Antialiasing,true);   //抗锯齿

    //绘制圆角背景
    painter.setBrush(Qt::transparent);
    painter.setPen(Qt::NoPen);   //去除背景边框
    painter.drawRoundedRect(10,10,360,360,8,8);

    //绘制饼图
    qreal startAngle = m_startAngle;   //绘制起点
    qreal spanAngle = 0;   //各区域占比,覆盖角度
    for(int i=0;i<m_vData.size();i++)
    {
        painter.setPen(m_vData[i].color);
        painter.setBrush(m_vData[i].color);
        if(m_totality)   //防止总数为0
        {
            spanAngle = m_vData[i].num * 360 / m_totality;
            my_shanxing[i].zhanbi = m_vData[i].num / m_totality;
        }
        if(i==clicked_code){
            painter.drawPie(m_center.x() - m_radius_long,m_center.y() - m_radius_long,m_radius_long * 2,m_radius_long * 2,startAngle * 16,spanAngle * 16);
        }else{
            painter.drawPie(m_center.x() - m_radius,m_center.y() - m_radius,m_radius * 2,m_radius * 2,startAngle * 16,spanAngle * 16);
        }
        calculateArcPoints(painter, m_center,m_radius,startAngle,spanAngle,i);
        startAngle += spanAngle;
    }

    //绘制区域名称和占比
    startAngle = m_startAngle;
    spanAngle = 0;
    for(int i=0;i<m_vData.size();i++)
    {
        painter.setPen(QColor("#ffffff"));
        painter.setFont(QFont("STSongti-SC-Bold, STSongti-SC",16));
        if(m_totality)
        {
            spanAngle = m_vData[i].num * 360 / m_totality;
        }
        int textAngle = startAngle + spanAngle / 2;
        QString text = QString("%1").arg(m_vData[i].name);
        int textWidth = painter.fontMetrics().horizontalAdvance(text);
        int textHeight = painter.fontMetrics().height();
        int textX = m_center.x() + m_textDistance * qCos(textAngle * M_PI / 180) - textWidth / 2;
        int textY = m_center.y() - m_textDistance * qSin(textAngle * M_PI / 180) + textHeight / 2;
        startAngle += spanAngle;
        //绘制文本
        QRect rect(textX,textY - textHeight,textWidth + 10,textHeight * 2);
        painter.drawText(rect,Qt::AlignCenter,text);

        //绘制连接线,文本要靠近对应区域,需要修改连接线终点位置
        painter.setPen(m_vData[i].color);
        int lineStartX = m_center.x() + (m_radius - 10) * qCos(textAngle * M_PI / 180);
        int lineStartY = m_center.y() - (m_radius - 10) * qSin(textAngle * M_PI / 180);
        int lineEndX = 0;
        int lineEndY = 0;
        if(textX < lineStartX)   //文本在左边
        {
            //可自行根据实际进行位置偏移的修改
            lineEndX = textX + textWidth/2;
            if(textY < lineStartY)   //文本在上边
            {
                lineEndY = textY + textHeight + 5;
            }
            else
            {
                lineEndY = textY - textHeight - 5;
            }
        }
        else
        {
            lineEndX = textX + textWidth/2;
            if(textY < lineStartY)
            {
                lineEndY = textY;
            }
            else
            {
                lineEndY = textY - textHeight - 5;
            }
        }
        painter.drawLine(lineStartX,lineStartY,lineEndX,lineEndY);

        //绘制终点
        painter.setPen(QPen(m_vData[i].color,5));
        painter.drawPoint(lineEndX,lineEndY);

        //将终点设为空心圆
        //painter.drawEllipse(QPoint(lineEndX,lineEndY),5,5);
        //painter.setPen(QPen(QColor("#FFFFFF"),1));
        //painter.setBrush(QColor("#FFFFFF"));
        //painter.drawEllipse(QPoint(lineEndX,lineEndY),3,3);
    }

    //绘制内圆,将饼图变为圆环
    painter.setPen(QPen(QColor("#FFFFFF"),10));
    painter.setBrush(QColor("#FFFFFF"));
    painter.drawEllipse(m_center,m_innerWidth,m_innerWidth);
}

void test_power::mousePressEvent(QMouseEvent *event)
{
    QPoint mousePos = event->pos();
    double x = mousePos.x()- m_center.x();
    if(x==0){
        return;
    }
    double y =  m_center.y() - mousePos.y();
    if(y==0){
       return;
    }
    if(  x*x + y*y < m_radius*m_radius){
        // 输出或使用坐标点
//        qDebug() << "Mouse pressed at position:" << x << "," << y;
        if(x>0 && y>0 ){
            double zhanbi=0;
            for (int i=0;i<4;i++){
                zhanbi += my_shanxing[i].zhanbi;
                if(zhanbi<0.25 && y/x>my_shanxing[i].start_y/my_shanxing[i].start_x && y/x<my_shanxing[i].end_y/my_shanxing[i].end_x){
                    qDebug()<<"clicked ============"<<i;
                    emit notice_clicked(i);
                    return;
                }
                if(zhanbi>=0.25) {
                    qDebug()<<"clicked ============"<<i;
                    emit notice_clicked(i);
                    return;
                }
            }
        }else if(x<0 && y>0 ){
            double zhanbi=0;
            for (int i=0;i<4;i++){
                zhanbi += my_shanxing[i].zhanbi;
                    if(zhanbi>0.25 && zhanbi<=0.5){
                        if(my_shanxing[i].start_x>=0){
                            //起始位置在第一象限
                            if(y/x<my_shanxing[i].end_y/my_shanxing[i].end_x){
                                qDebug()<<"clicked ============"<<i;
                                emit notice_clicked(i);
                                return;
                            }
                        }else{
                            //起始位置在第二象限
                            if(y/x>my_shanxing[i].start_y/my_shanxing[i].start_x && y/x<my_shanxing[i].end_y/my_shanxing[i].end_x){
                                qDebug()<<"clicked ============"<<i;
                                emit notice_clicked(i);
                                return;
                            }
                        }
                    }
                    if(zhanbi>0.5) {
                        qDebug()<<"clicked ============"<<i;
                        emit notice_clicked(i);
                        return;
                    }

            }
        }else if(x<0 && y<0 ){
            double zhanbi=0;
            for (int i=0;i<4;i++){
                zhanbi += my_shanxing[i].zhanbi;
                if(zhanbi>0.5 && zhanbi<=0.75){
                    if(my_shanxing[i].start_y>=0){
                        //起始位置在第一或者第二象限
                        if(y/x<my_shanxing[i].end_y/my_shanxing[i].end_x){
                            qDebug()<<"clicked ============"<<i;
                            return;
                        }
                    }else{
                        //起始位置在第三象限
                        if(y/x>my_shanxing[i].start_y/my_shanxing[i].start_x && y/x<my_shanxing[i].end_y/my_shanxing[i].end_x){
                            qDebug()<<"clicked ============"<<i;
                            return;
                        }
                    }
                }
                if(zhanbi>0.75) {
                    qDebug()<<"clicked ============"<<i;
                    emit notice_clicked(i);
                    return;
                }
            }
        }else if(x>0 && y<0 ){
            double zhanbi=0;
            for (int i=0;i<4;i++){
                zhanbi += my_shanxing[i].zhanbi;
                if(zhanbi>0.75){
                    if(my_shanxing[i].start_y>=0 || (my_shanxing[i].start_y<=0 && my_shanxing[i].start_x<=0)){
                        //起始位置位于一或者二或者三象限
                        if(y/x<my_shanxing[i].end_y/my_shanxing[i].end_x){
                            qDebug()<<"clicked ============"<<i;
                            emit notice_clicked(i);
                            return;
                        }
                    }else{
//                        qDebug()<<"iiiiiiiiiiiiiiiiii="<<i<<"=="<<y/x<<"==="<<my_shanxing[i].start_y/my_shanxing[i].start_x<<"------"<<my_shanxing[i].end_y/my_shanxing[i].end_x;
//                        qDebug()<<i<<"==y="<<y<<"   x="<<x<<"===y="<<my_shanxing[i].start_y<<"===x"<<my_shanxing[i].start_x<<"------y="<<my_shanxing[i].end_y<<"   x="<<my_shanxing[i].end_x;
                        if(y/x>my_shanxing[i].start_y/my_shanxing[i].start_x && y/x<my_shanxing[i].end_y/my_shanxing[i].end_x){
                            qDebug()<<"clicked ============"<<i;
                            
                            emit notice_clicked(i);
                            return;
                        }
                    }
                }
            }
        }
    }
}

void test_power::calculateArcPoints(QPainter &painter, const QPoint &center, int radius, double startAngle, double sweepLength,int inum) {
     QPointF startPoint = getArcPoint(center, radius, startAngle);
     QPointF endPoint = getArcPoint(center, radius, startAngle + sweepLength);

     my_shanxing[inum].start_x=startPoint.x()-m_center.x();
     my_shanxing[inum].start_y= m_center.y()-startPoint.y();
     my_shanxing[inum].end_x= endPoint.x()-m_center.x();
     my_shanxing[inum].end_y= m_center.y()-endPoint.y();
     // 这里可以处理startPoint和endPoint,例如绘制线条或进行其他操作
//     painter.drawLine(startPoint, endPoint); // 示例:绘制扇形边界线
//     qDebug()<<"startPoint x="<<my_shanxing[inum].start_x<<"    y="<<my_shanxing[inum].start_y;
//     qDebug()<<"endPoint x="<<my_shanxing[inum].end_x<<"    y="<<my_shanxing[inum].end_y;
 }

 QPointF test_power::getArcPoint(const QPoint &center, int radius, double angle) {
     double radian = angle * M_PI / 180; // 将角度转换为弧度
     return QPointF(center.x() + radius * cos(radian), center.y() - radius * sin(radian));
 }

ui里面就很简单只要设置尺寸就行了

调用时候的代码

//加载饼状图

dongli=20;zhaoming=30;kongtiao=40;teshu=20;

my_test_power = new test_power(dongli,zhaoming,kongtiao,teshu);

connect(my_test_power,SIGNAL(notice_clicked(int)),this,SLOT(refresh_power(int)));

test_power_list.append(my_test_power);

glayout_power = new QVBoxLayout();

glayout_power->addWidget(my_test_power);

ui->frame->setLayout(glayout_power);

相关推荐
默凉18 分钟前
C++ 虚函数(多态,多重继承,菱形继承)
开发语言·c++
我爱Jack19 分钟前
Java List 使用详解:从入门到精通
java·开发语言·数据结构
-凌凌漆-25 分钟前
【Qt】Qt QML json处理
开发语言·qt·json
手握风云-26 分钟前
JavaEE初阶第八期:解锁多线程,从 “单车道” 到 “高速公路” 的编程升级(六)
java·开发语言
楼田莉子1 小时前
数据学习之队列
c语言·开发语言·数据结构·学习·算法
写不出来就跑路1 小时前
SpringBoot静态资源与缓存配置全解析
java·开发语言·spring boot·spring·springboot
我命由我123451 小时前
Vue 开发问题:Missing required prop: “value“
开发语言·前端·javascript·vue.js·前端框架·ecmascript·js
企鹅侠客1 小时前
Bash与Zsh与Fish:在Linux中你应该使用哪个Shell
linux·开发语言·bash·zsh·fish
RAY_01042 小时前
Python—数据容器
开发语言·python
June bug2 小时前
【python基础】python和pycharm的下载与安装
开发语言·python·pycharm