Qt之自繪製餅圖

一、說明

    最近在搞繪圖方面的工做,說實話C++的第三方繪圖庫並不算多,總之我瞭解的有:qtcharts、ChartDirector、qwt、kdchart和QCustomPlot。這幾個庫各有利弊。緩存

  • qtcharts:qt5.7以後纔開源的模塊,支持繪製各類圖標,而且功能至關豐富,可是可擴展性差,若是本身想高度定製,比較困難,主要是和qt的源碼風格有決定性的關係。
  • ChartDirector:開源的第三方繪圖庫,使用方便,推薦使用
  • qwt:主要繪製儀表盤相似的東西(這個庫能夠編譯後加入qt幫助文檔)
  • kdchart:不只能夠繪製圖表,並且能夠繪製甘特圖,功能也都挺好使,我我的以前在qt4.7的時候使用過
  • QCustomPlot:簡答的繪圖庫,由於只有兩個文件,若是想高度定製我我的推薦這個靠譜,畢竟理解起來容易些

二、效果展現

    下邊是繪製的餅圖展現效果,固然了不能知足大多數人的須要,我主要是在這裏提供一種思路,若是須要在繪製上有所調整的小夥伴能夠下載demo自行修改。佈局

圖1 展現圖1post

圖2 展現2測試

圖3 展現圖3spa

三、思路分析

    上邊三張展現圖,若是要說從理解難以成都來講,展現圖3是比較容易理解。下邊我就幾個須要注意的細節描述下:.net

  • 圖表矩形距離邊框距離,影響圖表繪製矩形的因素
  • 圖表繪製方向,默認是逆時針
  • 圖表文本描述位置
  • legend描述位置,demo中已經提供了接口,能夠支持不一樣legend的展示形式
  • 箭頭長短
  • 空心餅圖(圓環圖)

    餅圖繪製關鍵步驟:3d

  • 添加數據項->構造數據緩存->繪製圖表
  • 窗口大小變化->構造數據項矩形->構造數據緩存->繪製圖表

四、源碼解說

    首先來看兩個結構體,主要是用來緩存數據,PieItemPrivate存儲的是每個item項的內容,包括item的legend,文本、顏色、值和一些輔助的結構體;PieChartPrivate結構是餅圖類的私有數據存儲結構,具體含義看註釋code

 1 struct PieItemPrivate
 2 {
 3     PieItem item;//用戶插入數據時的結構,包括註釋、值和顏色
 4     QPainterPath path;//項繪製時區域
 5     QPoint labelPos;//文本位置
 6     QRect m_LegendRect;//legend的矩形
 7 };
 8 
 9 struct PieChartPrivate
10 {
11     bool m_bLegendVisible = false;//是否顯示圖例
12     int m_Minx = 25;//左右最小邊距
13     int m_Miny = 25;//上下最小邊距
14     int m_MinDiameter = 130;//餅圖最小直徑
15     int m_RingWidth = 0;//若是是環,環的寬度
16     int m_StartRotationAngle = 0;//繪製item項的時候,其實角度
17     int m_LegendWidth = 100;//圖表寬度  能夠在插入新數據項的時候更新,計算展現legend所須要的最小尺寸
18     int m_LegendHeight = 30;//圖例高度  能夠在插入新數據項的時候更新,計算展現legend所須要的最小尺寸
19     double m_SumValue = 0;//全部item的value和
20     QRect m_PieRect;//餅圖繪製矩形
21     QColor m_LabelColor = QColor(0, 0, 0);//百分比文字顏色
22     QString m_RingLabel = QStringLiteral("餅圖");//圖表中心文字描述
23     QVector<PieItemPrivate> m_Items;//圖表項
24 };

一、當有新數據或者窗口大小發生變化時,計算數據緩存blog

 1 void PieChart::ConstructData()
 2 {
 3     int pos = d_ptr->m_StartRotationAngle;
 4     int angle;
 5     QPainterPath subPath;
 6     subPath.addEllipse(d_ptr->m_PieRect.adjusted(d_ptr->m_RingWidth, d_ptr->m_RingWidth, -d_ptr->m_RingWidth, -d_ptr->m_RingWidth));
 7     
 8     for (auto iter = d_ptr->m_Items.begin(); iter != d_ptr->m_Items.end(); ++iter)
 9     {
10         angle = 16 * iter->item.value / d_ptr->m_SumValue * 360;
11     
12         QPainterPath path;
13         path.moveTo(d_ptr->m_PieRect.center());
14         path.arcTo(d_ptr->m_PieRect.x(), d_ptr->m_PieRect.y(), d_ptr->m_PieRect.width(), d_ptr->m_PieRect.height(), pos / 16.0, angle / 16.0);
15         path.closeSubpath();
16         
17         if (d_ptr->m_RingWidth > 0 && d_ptr->m_RingWidth <= d_ptr->m_PieRect.width() / 2)
18         {
19             path -= subPath;
20         }
21         
22         iter->path = path;
23 
24         double labelAngle = (pos + angle / 2) / 16;
25         double tx = (d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / 2 * qCos(labelAngle / 360 * 2 * 3.1415926);
26         double ty = -(d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / 2 * qSin(labelAngle / 360 * 2 * 3.1415926);
27 
28         iter->labelPos = QPoint(tx, ty) + d_ptr->m_PieRect.center();
29 
30         pos += angle;
31     }
32 }

二、當窗口大小發生變化時,從新計算各項所在矩形,ConstructRect方式是用來計算各子項矩形區域的,內部調用ConstructCornerLayout方法是生產製定的佈局,有興趣的小夥伴能夠寫本身的矩形區域計算方式,開達到不一樣的繪製效果。接口

 1 void PieChart::ConstructRect(const QSize & size)
 2 {
 3     switch (d_ptr->m_Items.size())
 4     {
 5     case 4:
 6         ConstructCornerLayout(size);
 7     default:
 8         break;
 9     }
10 }
11 //該方法是針對4個legend,而且在四角的位置所計算的佈局方式,小夥伴也能夠實現本身的佈局計算,而後在ConstructRect接口中調用
12 void PieChart::ConstructCornerLayout(const QSize & size)
13 {
14     int currentR = d_ptr->m_MinDiameter;
15     int diameter;
16     int horiWidth = size.width();
17     if (d_ptr->m_bLegendVisible)
18     {
19         horiWidth -= d_ptr->m_LegendWidth * 2;
20     }
21 
22     if (horiWidth > size.height())
23     {
24         diameter = size.height();
25     }
26     else
27     {
28         diameter = horiWidth;
29     }
30 
31     int x, y;
32     int r = diameter - d_ptr->m_Minx * 2;
33     currentR = r > currentR ? r : currentR;
34     if (d_ptr->m_bLegendVisible)
35     {
36         x = d_ptr->m_Minx + d_ptr->m_LegendWidth;
37         y = (size.height() - currentR) / 2;
38       //計算4個legend位置
39         d_ptr->m_Items[1].m_LegendRect = QRect(d_ptr->m_Minx, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
40         d_ptr->m_Items[0].m_LegendRect = QRect(x + r, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
41         d_ptr->m_Items[3].m_LegendRect = QRect(x + r, size.height() - d_ptr->m_Miny - 30, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
42         d_ptr->m_Items[2].m_LegendRect = QRect(d_ptr->m_Minx, size.height() - d_ptr->m_Miny - 30, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
43     }
44     else
45     {
46         x = d_ptr->m_Minx;
47         y = d_ptr->m_Miny;
48     }
49 
50     d_ptr->m_PieRect = QRect(x, y, currentR, currentR);//計算餅圖位置
51 }

五、測試代碼

 1 int main(int argc, char *argv[])
 2 {
 3     QApplication a(argc, argv);
 4 
 5     PieChart w;
 6     w.AddData(100, Qt::red, "red");
 7     w.AddData(100, Qt::green, "green");
 8     w.AddData(100, Qt::blue, "blue");
 9     w.AddData(100, Qt::gray, "gray");
10     w.show();
11 
12     return a.exec();
13 }

六、示例下載

    Qt之自繪製餅圖

 

若是您以爲文章不錯,不妨給個 打賞,寫做不易,感謝各位的支持。您的支持是我最大的動力,謝謝!!! 

 

  


很重要--轉載聲明

  1. 本站文章無特別說明,皆爲原創,版權全部,轉載時請用連接的方式,給出原文出處。同時寫上原做者:朝十晚八 or Twowords
  2. 如要轉載,請原文轉載,如在轉載時修改本文,請事先告知,謝絕在轉載時經過修改本文達到有利於轉載者的目的。 

相關文章
相關標籤/搜索