用duilib做界面开发,但是窗口需要生成一个饼状图的图表,因为是动态生成,所以决定采用绘图的方式来做。
一开始选择了使用easyx来做,但是一则easyx窗口句柄一方面很麻烦,二来绘制出来的图效果很差,需要进行抗锯齿修复,但是本人学艺不精,抗锯齿对我来说难度较高,所以最后还是选择了简单的gdi+重绘控件来做。
我们的绘图效果要求呢,是这样的一个效果。
分步解析一下。
首先因为是三个模块,我们需要绘制三个扇形。
然后再在中间绘制一个圆形,然后绘制三条折线,在折线上补足文字就OK了。
废话不多说,直接上代码。
这是一个基于CControlUI的CPieChartUI控件,因为只是展示图表,我们不需要对他进行任何功能方面的开发,只需要重写他的DoPaint函数就可以了。
这是头文件PieChartUI.h
1 #pragma once 2 #include "duilib.h" 3 #include <afxdtctl.h> 4 #include <gdiplus.h> 5 #pragma comment(lib, "gdiplus.lib") 6 using namespace Gdiplus; 7 8 #define PI 3.14159265 9 10 class CPieChartUI : 11 public CControlUI 12 { 13 public: 14 CPieChartUI(int InUse, int Free, int Error); 15 ~CPieChartUI(); 16 17 bool DoPaint(HDC hDC, const RECT& rcPaint, CControlUI* pStopControl); 18 19 // 绘制折线 20 //void PaintPolyline(); 21 public: 22 int InUseNum; 23 int FreeNum; 24 int ErrorNum; 25 };
PieChartUI.cpp
1 #include "stdafx.h" 2 #include <math.h> 3 #include "PieChartUI.h" 4 5 CPieChartUI::CPieChartUI(int InUse, int Free, int Error) 6 :InUseNum(InUse), 7 FreeNum(Free), 8 ErrorNum(Error) 9 { 10 } 11 12 13 CPieChartUI::~CPieChartUI() 14 { 15 } 16 17 bool CPieChartUI::DoPaint(HDC hDC, const RECT & rcPaint, CControlUI * pStopControl) 18 { 19 // 初始化 GDI+ 20 GdiplusStartupInput gdiplusStartupInput; 21 ULONG_PTR gdiplusToken; 22 GdiplusStartup(&gdiplusToken, &gdiplusStartupInput, NULL); 23 Gdiplus::Graphics graphics(hDC); 24 25 // 获取当前控件的位置 26 RECT CtrlPos = this->GetPos(); 27 28 // 计算三块扇形数据的总数 29 int TotalNum = InUseNum + FreeNum + ErrorNum; 30 31 // 获取圆心点坐标 32 int CenterX = CtrlPos.left + 28 + 75; 33 int CenterY = CtrlPos.top + 27 + 75; 34 35 // 绘制第一部分的扇形 36 Gdiplus::SolidBrush inuseBrush(Color(255, 7, 143, 214)); 37 graphics.SetSmoothingMode(SmoothingMode::SmoothingModeAntiAlias); 38 REAL startAngle = 180.0f; 39 REAL sweepAngle = 360.0f * InUseNum / TotalNum; 40 Gdiplus::Rect ellipseRect(CtrlPos.left+28, CtrlPos.top+27, 150, 150); 41 graphics.FillPie(&inuseBrush, ellipseRect, startAngle, sweepAngle); 42 // 绘制百分比信息折线 43 if (InUseNum != 0) 44 { 45 // 计算折线起点坐标,取弧线中点的角度 46 REAL CenterAngle = (startAngle + sweepAngle / 2); 47 // 计算折线起点的弧度(C++中cos和sin的参数值是弧度,不是角度) 48 REAL CenterAngleRad = CenterAngle * PI / 180; 49 // 计算折线起点的坐标值 50 REAL PolylineStartX = CenterX + 75 * cos(CenterAngleRad); 51 REAL PolylineStartY = CenterY + 75 * sin(CenterAngleRad); 52 // 计算折线中点坐标 53 REAL PolylineCenterX = (PolylineStartX <= CenterX) ? (PolylineStartX - 15) : (PolylineStartX + 15); 54 REAL PolylineCenterY = (PolylineStartY <= CenterY) ? (PolylineStartY - 15) : (PolylineStartY + 15); 55 // 计算折线终点坐标 56 REAL PolylineEndX = (PolylineStartX <= CenterX) ? (PolylineCenterX - 30) : (PolylineCenterX + 30); 57 REAL PolylineEndY = PolylineCenterY; 58 // 绘制折线 59 Pen PolylinePen(Color(255, 255, 255, 255), 1); 60 PointF PolylineStart(PolylineStartX, PolylineStartY); 61 PointF PolylineCenter(PolylineCenterX, PolylineCenterY); 62 PointF PolylineEnd(PolylineEndX, PolylineEndY); 63 PointF Polyline[3] = { PolylineStart, PolylineCenter, PolylineEnd }; 64 graphics.DrawLines(&PolylinePen, Polyline, 3); 65 // 填写百分比数据 66 SolidBrush brush(Color(255, 255, 255, 255)); // 设置画刷 67 FontFamily fontfamily(L"微软雅黑"); // 设置字体类型 68 Gdiplus::Font font(&fontfamily, 14, FontStyleRegular, UnitPixel); // 设置字体 69 // PointF类对点进行了封装,这里是指定写字的开始点 70 PointF pointf((CenterAngle > 90 && CenterAngle <270) ? 71 PolylineEndX : PolylineEndX - 30 72 , PolylineEndY - 20); 73 CStringW PecentStrW; 74 PecentStrW.Format(L"%d%%", InUseNum * 100 / TotalNum ); 75 graphics.DrawString(PecentStrW, -1, &font, pointf, &brush); 76 } 77 78 // 绘制扇形 79 Gdiplus::SolidBrush freeBrush(Color(255, 204, 134, 0)); 80 startAngle += sweepAngle; 81 sweepAngle = 360.0f * FreeNum / TotalNum; 82 ellipseRect = { CtrlPos.left + 38, CtrlPos.top + 37, 130, 130 }; 83 graphics.FillPie(&freeBrush, ellipseRect, startAngle, sweepAngle); 84 if (FreeNum != 0) 85 { 86 REAL CenterAngle = (startAngle + sweepAngle / 2); 87 REAL CenterAngleRad = CenterAngle * PI / 180; 88 REAL PolylineStartX = CenterX + 65 * cos(CenterAngleRad); 89 REAL PolylineStartY = CenterY + 65 * sin(CenterAngleRad); 90 // 计算折线中点坐标 91 REAL PolylineCenterX = (PolylineStartX <= CenterX) ? (PolylineStartX - 15) : (PolylineStartX + 15); 92 REAL PolylineCenterY = (PolylineStartY <= CenterY) ? (PolylineStartY - 15) : (PolylineStartY + 15); 93 // 计算折线终点坐标 94 REAL PolylineEndX = (PolylineStartX <= CenterX) ? (PolylineCenterX - 30) : (PolylineCenterX + 30); 95 REAL PolylineEndY = PolylineCenterY; 96 // 绘制折线 97 Pen PolylinePen(Color(255, 255, 255, 255), 1); 98 PointF PolylineStart(PolylineStartX, PolylineStartY); 99 PointF PolylineCenter(PolylineCenterX, PolylineCenterY); 100 PointF PolylineEnd(PolylineEndX, PolylineEndY); 101 PointF Polyline[3] = { PolylineStart, PolylineCenter, PolylineEnd }; 102 graphics.DrawLines(&PolylinePen, Polyline, 3); 103 // 填写百分比数据 104 SolidBrush brush(Color(255, 255, 255, 255)); // 设置画刷 105 FontFamily fontfamily(L"微软雅黑"); // 设置字体类型 106 Gdiplus::Font font(&fontfamily, 14, FontStyleRegular, UnitPixel); // 设置字体 107 // PointF类对点进行了封装,这里是指定写字的开始点 108 PointF pointf((CenterAngle > 90 && CenterAngle < 270) ? 109 PolylineEndX : PolylineEndX - 30 110 , PolylineEndY - 20); 111 CStringW PecentStrW; 112 PecentStrW.Format(L"%d%%", FreeNum * 100 / TotalNum); 113 graphics.DrawString(PecentStrW, -1, &font, pointf, &brush); 114 } 115 116 // 绘制扇形 117 Gdiplus::SolidBrush errorBrush(Color(255, 215, 74, 67)); 118 startAngle += sweepAngle; 119 sweepAngle = 360.0f * ErrorNum / TotalNum; 120 ellipseRect = { CtrlPos.left + 48, CtrlPos.top + 47, 110, 110 }; 121 graphics.FillPie(&errorBrush, ellipseRect, startAngle, sweepAngle); 122 if (ErrorNum != 0) 123 { 124 REAL CenterAngle = (startAngle + sweepAngle / 2); 125 REAL CenterAngleRad = CenterAngle * PI / 180; 126 REAL PolylineStartX = CenterX + 55 * cos(CenterAngleRad); 127 REAL PolylineStartY = CenterY + 55 * sin(CenterAngleRad); 128 // 计算折线中点坐标 129 REAL PolylineCenterX = (PolylineStartX <= CenterX) ? (PolylineStartX - 15) : (PolylineStartX + 15); 130 REAL PolylineCenterY = (PolylineStartY <= CenterY) ? (PolylineStartY - 15) : (PolylineStartY + 15); 131 // 计算折线终点坐标 132 REAL PolylineEndX = (PolylineStartX <= CenterX) ? (PolylineCenterX - 30) : (PolylineCenterX + 30); 133 REAL PolylineEndY = PolylineCenterY; 134 // 绘制折线 135 Pen PolylinePen(Color(255, 255, 255, 255), 1); 136 PointF PolylineStart(PolylineStartX, PolylineStartY); 137 PointF PolylineCenter(PolylineCenterX, PolylineCenterY); 138 PointF PolylineEnd(PolylineEndX, PolylineEndY); 139 PointF Polyline[3] = { PolylineStart, PolylineCenter, PolylineEnd }; 140 graphics.DrawLines(&PolylinePen, Polyline, 3); 141 // 填写百分比数据 142 SolidBrush brush(Color(255, 255, 255, 255)); // 设置画刷 143 FontFamily fontfamily(L"微软雅黑"); // 设置字体类型 144 Gdiplus::Font font(&fontfamily, 14, FontStyleRegular, UnitPixel); // 设置字体 145 // PointF类对点进行了封装,这里是指定写字的开始点 146 PointF pointf((CenterAngle > 90 && CenterAngle <270) ? 147 PolylineEndX : PolylineEndX - 30 148 , PolylineEndY - 20); 149 CStringW PecentStrW; 150 PecentStrW.Format(L"%d%%", ErrorNum * 100 / TotalNum); 151 graphics.DrawString(PecentStrW, -1, &font, pointf, &brush); 152 } 153 154 // 绘制一个圆形 155 Gdiplus::SolidBrush fullBrush(Color(255, 1, 21, 76)); 156 ellipseRect = { CtrlPos.left + 68, CtrlPos.top + 67, 70, 70 }; 157 graphics.FillEllipse(&fullBrush, ellipseRect); 158 159 return true; 160 }
调用方式
1 CPieChartUI* NewPieChart = new CPieChartUI(inUseNum, FreeNum, ErrorNum); 2 RoomInfoHor->Add(NewPieChart); 3 NewPieChart->SetFloat(true); 4 NewPieChart->SetPos({ 25,65,0,0 });