MFC画饼图

⌚Time: 2025-01-14 16:40:00

👨‍💻Author: Jack Ge

最近翻找出以前的一些代码,有个饼图的博客没有写完,现在放在这里。MFC画饼图很复杂。只有一个CDC的Pie函数可用。

CDC::Pie

BOOL Pie(int x1 ,int y1 ,int x2,int y2,int x3 ,int y3 ,int x4 ,int y4);
BOOL Pie(LPRECT lpRect,POINT ptStart ,POINT ptEnd);

返回值:如果成功,则返回非零值,否则为0

参数:
x1  指定外接矩形左上角的X逻辑坐标。
y1  指定外接矩形左上角的Y逻辑坐标。
x2  指定外接矩形右下角的X逻辑坐标。
y2  指定外接矩形右下角的Y逻辑坐标。
x3  指定弧起点的X逻辑坐标。
y3  指定弧起点的Y逻辑坐标。
x4  指定弧终点的X逻辑坐标。
y4  指定弧终点的Y逻辑坐标。
lpRect  指定外接矩形,可以为该参数传递RECT结构或CRect对象。
ptStart 指定弧起点,可以为该参数传递POINT结构或CPoint对象 。
ptEnd   指定弧终点,可以为该参数传递POINT结构或CPoint对象。

说明:
画一个饼图。由一段椭圆弧形成,其中心点和两个端点已经用线段连接。弧中心是由x1,y1,x2,y2(或lpRect)指定的外接矩形的中心。弧的起点和终点由x3,y3,x4,y4(或ptStart和 ptEnd)指定。弧由选定笔沿逆时针绘制,另外还有两条从端点到中心点的连线。饼图区域由当前画刷填充。如果x3等于x4且y3等于y4,则结果为一个椭圆。它只有一条中心点与点(x3,y3)或点(x4,y4)的连线。该函数绘制的图形延伸到但不包括右边和底部坐标,即图形高度为y2-y1,宽度为x2-x1,外接矩形的宽度和高度都必须大于2单位且小于32,767单位。

下面的一个类是我当时自己写的一个饼图类。能够实现画饼图

DrawPieOnWindow.h

#pragma once
#include <map>

#define PI 3.1415926
//顺时针方向画饼图
//pie函数是 饼图的曲线由适合指定边界矩形的椭圆定义。曲线从椭圆与第一个径向相交的点开始,并逆时针扩展到椭圆与第二个径向相交的点。
class DrawPieOnWindow
{
public:
    DrawPieOnWindow();
    ~DrawPieOnWindow();
    //添加颜色
    void add_color(COLORREF color,float radio){
        m_radioMap.insert(std::make_pair(color, radio));
    }
    //设置绘制窗口
    void set_target_window(CWnd *targetWindow){
        m_targetWindow = targetWindow;
    }
    //设置饼的信息
    //左上角坐标 半径 开始的角度 ,顺时针计算一共的角度
    void set_pie_info(POINT topleft, float radius, float startAngle,float totalAngle){
        m_topleft = topleft;
        m_radius = radius;
        m_startAngle = startAngle;
        m_totalAngle = totalAngle;
    }
    //获取起始点坐标
    POINT get_pie_startend_point(float angle){
        POINT p;
        //cos运算的是弧度制的角度,不是角度制的
        p.x = m_topleft.x + m_radius + m_radius*cos(angle*PI / 180);
        //如果计算的位置是正数,对于y坐标是向上的负方向,所以是减号
        p.y = m_topleft.y + m_radius - m_radius*sin(angle*PI / 180);
        return p;
    }
    //顺时针方向画饼图,pie函数是逆时针画的所以起始点顺序相反
    void draw(){
        float endAngle;
        float startAngle;
        //获取dc
        CDC *hdc = m_targetWindow->GetDC();
        //最初的开始点
        startAngle = m_startAngle;
        for (auto it : m_radioMap){
            //每画一种颜色的饼更新起始点,终点接着上个饼的起点
            endAngle = startAngle;
            startAngle = endAngle - it.second * m_totalAngle;
            //根据起始点角度获取起始点坐标
            POINT endPoint = get_pie_startend_point(endAngle);
            POINT startPoint = get_pie_startend_point(startAngle);
            //如果起始点相等说明占比例为0就不画了,否则后面调用函数画起始点相同,直接全饼360度是这个颜色
            if (startPoint.x == endPoint.x && startPoint.y == endPoint.y){
                continue;
            }
            //计算圆外接矩形
            RECT rc;
            rc.left = m_topleft.x;
            rc.top = m_topleft.y;
            rc.right = rc.left + 2 * m_radius;
            rc.bottom = rc.top + 2 * m_radius;
            //设置颜色
            CBrush *brush = new CBrush(it.first);
            CBrush *oldBrush = hdc->SelectObject(brush);
            //画饼
            hdc->Pie(&rc, startPoint, endPoint);
            //释放资源
            hdc->SelectObject(oldBrush);
            brush->DeleteObject();//不能用DeleteObject(brush);来释放。大概是那是win32API的释放方法。
        }
        //释放dc
        m_targetWindow->ReleaseDC(hdc);
    }
private:
    //左上角坐标
    POINT m_topleft;
    //半径
    float m_radius;
    //起始角度
    float m_startAngle;
    //总共角度
    float m_totalAngle;
    //map的排列不按照颜色加入的顺序,并且不允许有颜色重复的情况
    std::map<COLORREF, float> m_radioMap;
    //目标绘制窗口
    CWnd *m_targetWindow;
};

DrawPieOnWindow.cpp

#include "stdafx.h"
#include "DrawPieOnWindow.h"


DrawPieOnWindow::DrawPieOnWindow()
{
}


DrawPieOnWindow::~DrawPieOnWindow()
{
}

这个类的add_color函数就是加一块颜色的饼,并且设置所占的比率。每调用一次就加一块不同颜色的饼,几个饼加起来比率是1.0。

set_target_window是设置在哪个窗体上画饼图。传入一个CWnd *指针

set_pie_info是设置整块饼图的信息,左上角起始位置,半径,还有起始角度和总共的角度。起始角度的0度位置就是笛卡尔坐标系x轴的那个角度。总共的角度是顺时针增加的。也就是说可以设置0-360,饼图整体可以是一块饼或者一个完整的圆。

draw函数是使用类进行绘制,应该放在窗体绘图函数里,比如OnPaint函数之类的。

get_pie_startend_point是类的内部函数外部没有用

使用方法就是在MFC窗体类中定义一个m_drawPie变量,在OnInitDialog函数里初始化,设置好饼图信息

    // TODO: 在此添加额外的初始化代码
    //设置绘制的目标窗体
    m_drawPie.set_target_window(this);
    //添加颜色
    COLORREF color;
    color = RGB(255, 0, 0);
    m_drawPie.add_color(color,0.3);
    color = RGB(155, 220, 0);
    m_drawPie.add_color(color,0.3);
    color = RGB(0, 70, 220);
    m_drawPie.add_color(color,0.2);
    color = RGB(110, 170, 120);
    m_drawPie.add_color(color,0.2);
    //设置绘制开始位置
    POINT p;
    p.x = 130;
    p.y = 130;
    //饼图信息,起始位置 半径 起始角度 总共角度
    m_drawPie.set_pie_info(p,150.f,0.f,270.f);

在窗体的OnPaint函数里调用类进行绘制

    m_drawPie.draw();

效果展示

总结:MFC非常麻烦,什么功能都没有直接的提供。都得根据理解去修改,篡改。如果是其它编程语言,一句原生代码的事情就解决了。还用这么麻烦?