VPieChart.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653
  1. 
  2. #include "VPieChart.h"
  3. #include <qmath.h>
  4. #include "GvlManager.h"
  5. // 默认的分块数
  6. #define DEFAULT_PEICHART_SLICE_COUNT 3
  7. VPieChart::VPieChart(
  8. QWidget *parent,
  9. const QPoint& pos,
  10. const QSize& size,
  11. CONTROL_PROPERTY* pProperty,
  12. CONTROL_PROPERTY_EX* pPropertyEx
  13. )
  14. : QWidget(parent)
  15. , VControlObject(pProperty)
  16. {
  17. // 设置控件指针
  18. m_pWidget = this;
  19. // 设置控件类型
  20. m_Type = VALUE_TYPE::Control_PieChart;
  21. // 初始化UI风格
  22. this->initStyle();
  23. // 设置尺寸
  24. if (size == DEFAULT_CONTROL_SIZE)
  25. {
  26. this->resize(DEFAULT_PIE_SIZE);
  27. }
  28. else
  29. {
  30. this->resize(size);
  31. }
  32. // 设置中心点坐标
  33. QPoint tempPos;
  34. tempPos.setX(pos.x() - width() / 2);
  35. tempPos.setY(pos.y() - height() / 2);
  36. // 设置位置
  37. this->move(tempPos);
  38. // 初始化扩展属性
  39. if (pPropertyEx == nullptr)
  40. {
  41. // 初始化饼图的分块数
  42. this->m_Property.m_nSliceCount = DEFAULT_PEICHART_SLICE_COUNT;
  43. // 初始化扩展属性
  44. this->initPropertyEx();
  45. // 重新绘制预览饼图
  46. this->update();
  47. }
  48. else
  49. {
  50. this->m_PropertyEx = *pPropertyEx;
  51. // 根据分块数量先设置一个默认显示的Value
  52. for (int i = 0; i < m_Property.m_nSliceCount; i++)
  53. {
  54. // 初始化一下Value的预览数值(全为1)
  55. m_Values << 1.0;
  56. }
  57. // 重新绘制预览饼图
  58. this->update();
  59. }
  60. }
  61. VPieChart::~VPieChart()
  62. {
  63. }
  64. void VPieChart::paintEvent(QPaintEvent *)
  65. {
  66. int width = this->width();
  67. int height = this->height();
  68. int side = qMin(width, height);
  69. //绘制准备工作,启用反锯齿,平移坐标轴中心,等比例缩放
  70. QPainter painter(this);
  71. painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing);
  72. painter.translate(width / 2, height / 2);
  73. painter.scale(side / 200.0, side / 200.0);
  74. //绘制饼图
  75. drawPie(&painter);
  76. }
  77. /// <summary>
  78. /// 初始化UI风格
  79. /// </summary>
  80. void VPieChart::initStyle()
  81. {
  82. explodedIndex = -1;
  83. explodedAll = false;
  84. showPercent = false;
  85. holeSize = 0.0;
  86. // 设置基础属性
  87. this->setTextColor(m_Property.m_clrText);
  88. this->setBorderColor(m_Property.m_clrBorder);
  89. // 备用颜色集合(按分块顺序赋值)
  90. m_defaultColors << QColor(0, 176, 180)
  91. << QColor(255, 192, 0)
  92. << QColor(0, 113, 193)
  93. << QColor(72, 103, 149)
  94. << QColor(185, 87, 86)
  95. << QColor(0, 177, 125)
  96. << QColor(214, 77, 84)
  97. << QColor(71, 164, 233)
  98. << QColor(34, 163, 169)
  99. << QColor(40, 45, 48)
  100. << QColor(162, 121, 197)
  101. << QColor(72, 202, 245)
  102. << QColor(0, 150, 121)
  103. << QColor(111, 9, 176)
  104. << QColor(250, 170, 20);
  105. }
  106. /// <summary>
  107. /// 核心函数:绘制饼图
  108. /// </summary>
  109. /// <param name="painter"></param>
  110. void VPieChart::drawPie(QPainter *painter)
  111. {
  112. painter->save();
  113. int radius = 93;
  114. QRect rect(-radius, -radius, radius * 2, radius * 2);
  115. double startAngle = 0;
  116. double sum = getSumValue();
  117. //逐个取出值并绘制饼图区域和对应的文字
  118. int count = this->m_Property.m_nSliceCount;
  119. for (int i = 0; i < count; ++i)
  120. {
  121. // 取出当前属性值用于绘制
  122. const PROPERTY_EX_SUBGROUP& subGroup = m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[i];
  123. //取出值并计算当前值占比面积
  124. double value = m_Values.at(i);
  125. double arcLength = value / sum * 360;
  126. double percent = value / sum * 100;
  127. QRect pieRect = rect;
  128. //如果当前区域展开则需要设置边框
  129. painter->setPen(Qt::NoPen);
  130. if (explodedIndex == i || explodedAll)
  131. {
  132. painter->setPen(m_BorderColor);
  133. QPoint center = pieRect.center();
  134. int mid = startAngle + arcLength / 2;
  135. center += getOffsetPoint(mid);
  136. pieRect.moveCenter(center);
  137. }
  138. // 从颜色集合中取出颜色(按照索引顺序依次获取)
  139. painter->setBrush(subGroup.color);
  140. painter->drawPie(pieRect, startAngle * 16, arcLength * 16);
  141. QString strValue = subGroup.strValue;
  142. if (showPercent && percent > 7)
  143. {
  144. strValue = QString("%1%2%3%").arg(strValue).arg(strValue.isEmpty() ? "" : "\n").arg(QString::number(percent, 'f', 0));
  145. }
  146. int mid = startAngle + arcLength / 2;
  147. int offset = 60;
  148. if (percent >= 50)
  149. {
  150. offset = 45;
  151. }
  152. else if (percent >= 30)
  153. {
  154. offset = 55;
  155. }
  156. else if (percent >= 15)
  157. {
  158. offset = 60;
  159. }
  160. QPoint p = getOffsetPoint(mid, offset);
  161. QRect textRect;
  162. textRect.setX(p.x() - 40);
  163. textRect.setY(p.y() - 30);
  164. textRect.setWidth(80);
  165. textRect.setHeight(60);
  166. painter->setPen(Qt::black);
  167. //painter->drawRect(textRect);
  168. // 设置字体
  169. QFont font;
  170. font.setPixelSize(strValue.isEmpty() ? 20 : 17);
  171. painter->setFont(font);
  172. // 绘制文字
  173. painter->setPen(m_TextColor);
  174. painter->drawText(textRect, Qt::AlignCenter, strValue);
  175. // 递增角度
  176. startAngle += arcLength;
  177. }
  178. painter->restore();
  179. }
  180. /// <summary>
  181. /// 初始化扩展属性
  182. /// </summary>
  183. void VPieChart::initPropertyEx()
  184. {
  185. // 扩展属性的总名称
  186. m_PropertyEx.m_strTitle = PIE_PROPERTY_EX_NAME;
  187. // 用于触发刷新的数据链接
  188. m_PropertyEx.m_refreshLink.title = PIE_PROPERTY_EX_REFRESHLINK_NAME;
  189. // 强制设置
  190. m_PropertyEx.m_refreshLink.bForce = true;
  191. //// 默认值(暂不使用)
  192. //m_PropertyEx.m_refreshLink.defaultValue = new VARIABLE("",
  193. // false, m_PropertyEx.m_refreshLink.title, "int", DEFAULT_DATALINK_VALUE_0, "");
  194. // 初始化每一个扩展属性组(Pie控件只有一个属性组)
  195. PROPERTY_EX_GROUP group;
  196. // 列信息属性
  197. group.strTitle = PIE_PROPERTY_EX_GROUP_NAME;
  198. group.strRelationKey = PIE_SLICE_COUNT_NAME;
  199. //初始化列信息每一个子分组
  200. for (int i = 0; i < m_Property.m_nSliceCount; i++)
  201. {
  202. PROPERTY_EX_SUBGROUP subGroup;
  203. this->initSubGroup(subGroup, i, GROUP_INDEX_PIE);
  204. // 保存子分组
  205. group.subGroups.push_back(subGroup);
  206. // 顺便初始化一下Value的预览数值(全为1)
  207. m_Values << 1.0;
  208. }
  209. // 保存本子分组定义信息
  210. m_PropertyEx.m_groups.push_back(group);
  211. //// 2022-9-11 为刷新链接绑定默认值(系统的执行次数)
  212. //this->bindDefaultRefreshDataLink();
  213. }
  214. /// <summary>
  215. /// 初始化一个扩展属性组
  216. /// </summary>
  217. /// <param name="subItem"></param>
  218. void VPieChart::initSubGroup(PROPERTY_EX_SUBGROUP& subGroup, int nIndex, int nSubGroupID)
  219. {
  220. if (nSubGroupID == GROUP_INDEX_PIE)
  221. {
  222. QString idx = QString::number(nIndex + 1);
  223. // 子分组名称
  224. subGroup.strTitle = PIE_PROPERTY_EX_SUBGROUP_NAME + idx;
  225. // 列名称 + 数值
  226. subGroup.strValueName = PIE_PROPERTY_EX_VALUE_NAME + idx;
  227. subGroup.strValue = PIE_PROPERTY_EX_VALUE_NAME + idx;
  228. // 分块颜色名称 + 数值
  229. subGroup.strColorName = PIE_PROPERTY_EX_COLOR_NAME + idx;
  230. // 按顺序给一个默认值(可能会越界)
  231. if (nIndex < m_defaultColors.size())
  232. {
  233. subGroup.color = m_defaultColors.at(nIndex);
  234. }
  235. else
  236. {
  237. subGroup.color = m_defaultColors.at(nIndex - m_defaultColors.size());
  238. }
  239. // 数据链接名称
  240. DataLink dataLink;
  241. dataLink.title = PIE_PROPERTY_EX_LINK_NAME + idx;
  242. dataLink.defaultValue = g_pGvlManager->getDefaultValueByName(DEFAULT_VALUE_NULLSTRING);
  243. subGroup.dataLinks.push_back(dataLink);
  244. }
  245. }
  246. /// <summary>
  247. /// 获取文字颜色
  248. /// </summary>
  249. /// <returns></returns>
  250. QColor VPieChart::getTextColor() const
  251. {
  252. return this->m_TextColor;
  253. }
  254. /// <summary>
  255. /// 获取边界颜色
  256. /// </summary>
  257. /// <returns></returns>
  258. QColor VPieChart::getBorderColor() const
  259. {
  260. return this->m_BorderColor;
  261. }
  262. /// <summary>
  263. /// 设置块数
  264. /// </summary>
  265. /// <param name="count"></param>
  266. void VPieChart::setSliceCount(const int& count)
  267. {
  268. if (this->m_Property.m_nSliceCount != count)
  269. {
  270. this->m_Property.m_nSliceCount = count;
  271. this->update();
  272. }
  273. }
  274. /// <summary>
  275. /// 获取当前块数
  276. /// </summary>
  277. int VPieChart::getSliceCount()
  278. {
  279. return this->m_Property.m_nSliceCount;
  280. }
  281. /// <summary>
  282. /// 设置分块文字
  283. /// </summary>
  284. /// <param name="newValue"></param>
  285. /// <param name="nIndex"></param>
  286. void VPieChart::updateSliceTitle(const QString& newValue, const int nIndex)
  287. {
  288. // 更新分块文字
  289. QString& strValue = m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].strValue;
  290. if (strValue != newValue)
  291. {
  292. strValue = newValue;
  293. this->update();
  294. }
  295. qDebug() << "VPieChart::updateColTitle - update ["
  296. << m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].strValueName << "] to [" << newValue << "]";
  297. }
  298. /// <summary>
  299. /// 设置分块颜色
  300. /// </summary>
  301. /// <param name="newValue"></param>
  302. /// <param name="nIndex"></param>
  303. void VPieChart::updateSliceColor(QColor newValue, const int nIndex)
  304. {
  305. QColor& color = m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].color;
  306. if (color != newValue)
  307. {
  308. color = newValue;
  309. this->update();
  310. }
  311. qDebug() << "VPieChart::updateSliceColor - update ["
  312. << m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].strColorName
  313. << "] to [" << newValue << "]";
  314. }
  315. /// <summary>
  316. /// 设置分块数值
  317. /// </summary>
  318. /// <param name="newValue"></param>
  319. /// <param name="nIndex"></param>
  320. void VPieChart::updateSliceValue(const int& newValue, const int nIndex)
  321. {
  322. if (newValue != m_Values[nIndex])
  323. {
  324. m_Values[nIndex] = newValue;
  325. this->update();
  326. }
  327. qDebug() << "VPieChart::updateSliceValue - update m_Values["
  328. << nIndex << "] to [" << newValue << "]";
  329. }
  330. /// <summary>
  331. /// 调整块属性数量(虚函数)
  332. /// </summary>
  333. /// <param name="fixCount"></param>
  334. void VPieChart::updateExPropertyCount(const int fixCount, const QString& strPropName)
  335. {
  336. // 获取属性的当前数量
  337. int curCount = this->getExPropertyCountByName(strPropName);
  338. // 子分组编号
  339. int curGroup = this->getGroupIDByName(strPropName);
  340. // 检查有效性
  341. if (curGroup < 0)
  342. {
  343. qDebug() << "[Error] VTableControl::updateExPropertyCount - nGroupID in invalid: " << curGroup;
  344. return;
  345. }
  346. // 如果需要增加
  347. if (fixCount > 0)
  348. {
  349. int nEnd = curCount + fixCount;
  350. for (int i = curCount; i < nEnd; i++)
  351. {
  352. PROPERTY_EX_SUBGROUP subItem;
  353. this->initSubGroup(subItem, i, curGroup);
  354. m_PropertyEx.m_groups[curGroup].subGroups.push_back(subItem);
  355. // 2022-1-5 顺便初始化一下Value的预览数值(全为1)
  356. m_Values << 1.0;
  357. }
  358. }
  359. // 如果需要减少
  360. else if (fixCount < 0)
  361. {
  362. for (int i = 0; i < qAbs(fixCount); i++)
  363. {
  364. // 去掉对应的数据结构信息
  365. m_PropertyEx.m_groups[curGroup].subGroups.pop_back();
  366. // 同时去掉预览数值
  367. m_Values.pop_back();
  368. }
  369. }
  370. }
  371. /// <summary>
  372. /// 修改扩展属性(虚函数)
  373. /// </summary>
  374. /// <param name="strValueTile"></param>
  375. /// <param name="newValue"></param>
  376. void VPieChart::changeExProperties(QString strValueTitle, const QVariant& newValue)
  377. {
  378. // 如果改变了分块标题
  379. if (strValueTitle.contains(PIE_PROPERTY_EX_VALUE_NAME))
  380. {
  381. // 获取修改属性的索引
  382. int nIndex = strValueTitle.remove(PIE_PROPERTY_EX_VALUE_NAME).toInt() - 1;
  383. // 如果索引值有效,则对应更新
  384. if (nIndex >= 0)
  385. {
  386. // 修改对应Pie的数据结构内容
  387. this->updateSliceTitle(newValue.toString(), nIndex);
  388. }
  389. }
  390. // 如果改变了分块颜色
  391. else if (strValueTitle.contains(PIE_PROPERTY_EX_COLOR_NAME))
  392. {
  393. // 获取修改属性的索引
  394. int nIndex = strValueTitle.remove(PIE_PROPERTY_EX_COLOR_NAME).toInt() - 1;
  395. // 如果索引值有效,则对应更新
  396. if (nIndex >= 0)
  397. {
  398. // 修改对应Pie的数据结构内容
  399. this->updateSliceColor(newValue.value<QColor>(), nIndex);
  400. }
  401. }
  402. // 如果改变了数据链接(此处应该不需要手工更新)
  403. else if (strValueTitle.contains(PIE_PROPERTY_EX_REFRESHLINK_NAME)
  404. || strValueTitle.contains(PIE_PROPERTY_EX_LINK_NAME)
  405. )
  406. {
  407. }
  408. // Error:不应该执行到这里
  409. else
  410. {
  411. qWarning() << "[Error]: VPieChart::changeExProperties - invalid strValueTitle: " << strValueTitle;
  412. }
  413. }
  414. void VPieChart::setExplodedAll(bool explodedAll)
  415. {
  416. if (this->explodedAll != explodedAll)
  417. {
  418. this->explodedAll = explodedAll;
  419. this->update();
  420. }
  421. }
  422. void VPieChart::setExplodedIndex(int explodedIndex)
  423. {
  424. if (this->explodedIndex != explodedIndex)
  425. {
  426. this->explodedIndex = explodedIndex;
  427. this->update();
  428. }
  429. }
  430. void VPieChart::setDefaultColor(bool defaultColor)
  431. {
  432. Q_UNUSED(defaultColor);
  433. }
  434. void VPieChart::setTextColor(const QColor &textColor)
  435. {
  436. if (this->m_TextColor != textColor)
  437. {
  438. this->m_TextColor = textColor;
  439. this->update();
  440. }
  441. }
  442. void VPieChart::setBorderColor(const QColor &borderColor)
  443. {
  444. if (this->m_BorderColor != borderColor)
  445. {
  446. this->m_BorderColor = borderColor;
  447. this->update();
  448. }
  449. }
  450. void VPieChart::setColors(const QVector<QColor> &colors)
  451. {
  452. this->m_defaultColors = colors;
  453. this->update();
  454. }
  455. void VPieChart::initPie()
  456. {
  457. }
  458. /// <summary>
  459. /// 设置显示百分比
  460. /// </summary>
  461. void VPieChart::loadPercent()
  462. {
  463. showPercent = true;
  464. this->update();
  465. }
  466. ///// <summary>
  467. ///// 清空饼图
  468. ///// </summary>
  469. //void VPieChart::clearPie()
  470. //{
  471. // m_Titles.clear();
  472. // m_Values.clear();
  473. //}
  474. void VPieChart::setHoleSize(double holeSize)
  475. {
  476. if (this->holeSize != holeSize) {
  477. this->holeSize = holeSize;
  478. this->update();
  479. }
  480. }
  481. //=========================================================
  482. //
  483. // 辅助函数
  484. //
  485. //=========================================================
  486. double VPieChart::getSumValue()
  487. {
  488. double sum = 0;
  489. for (int i = 0; i < m_Values.count(); i++)
  490. {
  491. sum += m_Values.at(i);
  492. }
  493. if (sum == 0.0)
  494. {
  495. sum = 1;
  496. }
  497. return sum;
  498. }
  499. QPoint VPieChart::getOffsetPoint(double angel, int offset)
  500. {
  501. int wl = 0;
  502. int hl = 0;
  503. double ang = (angel)*M_PI / 180;
  504. wl = offset * qCos(ang);
  505. hl = offset * qSin(ang);
  506. if (angel > 90 && angel < 270)
  507. {
  508. wl = qAbs(wl) * -1;
  509. }
  510. else {
  511. wl = qAbs(wl);
  512. }
  513. if (angel < 180)
  514. {
  515. hl = qAbs(hl) * -1;
  516. }
  517. else {
  518. hl = qAbs(hl);
  519. }
  520. return QPoint(wl, hl);
  521. }