#include "VPieChart.h" #include #include "GvlManager.h" // 默认的分块数 #define DEFAULT_PEICHART_SLICE_COUNT 3 VPieChart::VPieChart( QWidget *parent, const QPoint& pos, const QSize& size, CONTROL_PROPERTY* pProperty, CONTROL_PROPERTY_EX* pPropertyEx ) : QWidget(parent) , VControlObject(pProperty) { // 设置控件指针 m_pWidget = this; // 设置控件类型 m_Type = VALUE_TYPE::Control_PieChart; // 初始化UI风格 this->initStyle(); // 设置尺寸 if (size == DEFAULT_CONTROL_SIZE) { this->resize(DEFAULT_PIE_SIZE); } else { this->resize(size); } // 设置中心点坐标 QPoint tempPos; tempPos.setX(pos.x() - width() / 2); tempPos.setY(pos.y() - height() / 2); // 设置位置 this->move(tempPos); // 初始化扩展属性 if (pPropertyEx == nullptr) { // 初始化饼图的分块数 this->m_Property.m_nSliceCount = DEFAULT_PEICHART_SLICE_COUNT; // 初始化扩展属性 this->initPropertyEx(); // 重新绘制预览饼图 this->update(); } else { this->m_PropertyEx = *pPropertyEx; // 根据分块数量先设置一个默认显示的Value for (int i = 0; i < m_Property.m_nSliceCount; i++) { // 初始化一下Value的预览数值(全为1) m_Values << 1.0; } // 重新绘制预览饼图 this->update(); } } VPieChart::~VPieChart() { } void VPieChart::paintEvent(QPaintEvent *) { int width = this->width(); int height = this->height(); int side = qMin(width, height); //绘制准备工作,启用反锯齿,平移坐标轴中心,等比例缩放 QPainter painter(this); painter.setRenderHints(QPainter::Antialiasing | QPainter::TextAntialiasing); painter.translate(width / 2, height / 2); painter.scale(side / 200.0, side / 200.0); //绘制饼图 drawPie(&painter); } /// /// 初始化UI风格 /// void VPieChart::initStyle() { explodedIndex = -1; explodedAll = false; showPercent = false; holeSize = 0.0; // 设置基础属性 this->setTextColor(m_Property.m_clrText); this->setBorderColor(m_Property.m_clrBorder); // 备用颜色集合(按分块顺序赋值) m_defaultColors << QColor(0, 176, 180) << QColor(255, 192, 0) << QColor(0, 113, 193) << QColor(72, 103, 149) << QColor(185, 87, 86) << QColor(0, 177, 125) << QColor(214, 77, 84) << QColor(71, 164, 233) << QColor(34, 163, 169) << QColor(40, 45, 48) << QColor(162, 121, 197) << QColor(72, 202, 245) << QColor(0, 150, 121) << QColor(111, 9, 176) << QColor(250, 170, 20); } /// /// 核心函数:绘制饼图 /// /// void VPieChart::drawPie(QPainter *painter) { painter->save(); int radius = 93; QRect rect(-radius, -radius, radius * 2, radius * 2); double startAngle = 0; double sum = getSumValue(); //逐个取出值并绘制饼图区域和对应的文字 int count = this->m_Property.m_nSliceCount; for (int i = 0; i < count; ++i) { // 取出当前属性值用于绘制 const PROPERTY_EX_SUBGROUP& subGroup = m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[i]; //取出值并计算当前值占比面积 double value = m_Values.at(i); double arcLength = value / sum * 360; double percent = value / sum * 100; QRect pieRect = rect; //如果当前区域展开则需要设置边框 painter->setPen(Qt::NoPen); if (explodedIndex == i || explodedAll) { painter->setPen(m_BorderColor); QPoint center = pieRect.center(); int mid = startAngle + arcLength / 2; center += getOffsetPoint(mid); pieRect.moveCenter(center); } // 从颜色集合中取出颜色(按照索引顺序依次获取) painter->setBrush(subGroup.color); painter->drawPie(pieRect, startAngle * 16, arcLength * 16); QString strValue = subGroup.strValue; if (showPercent && percent > 7) { strValue = QString("%1%2%3%").arg(strValue).arg(strValue.isEmpty() ? "" : "\n").arg(QString::number(percent, 'f', 0)); } int mid = startAngle + arcLength / 2; int offset = 60; if (percent >= 50) { offset = 45; } else if (percent >= 30) { offset = 55; } else if (percent >= 15) { offset = 60; } QPoint p = getOffsetPoint(mid, offset); QRect textRect; textRect.setX(p.x() - 40); textRect.setY(p.y() - 30); textRect.setWidth(80); textRect.setHeight(60); painter->setPen(Qt::black); //painter->drawRect(textRect); // 设置字体 QFont font; font.setPixelSize(strValue.isEmpty() ? 20 : 17); painter->setFont(font); // 绘制文字 painter->setPen(m_TextColor); painter->drawText(textRect, Qt::AlignCenter, strValue); // 递增角度 startAngle += arcLength; } painter->restore(); } /// /// 初始化扩展属性 /// void VPieChart::initPropertyEx() { // 扩展属性的总名称 m_PropertyEx.m_strTitle = PIE_PROPERTY_EX_NAME; // 用于触发刷新的数据链接 m_PropertyEx.m_refreshLink.title = PIE_PROPERTY_EX_REFRESHLINK_NAME; // 强制设置 m_PropertyEx.m_refreshLink.bForce = true; //// 默认值(暂不使用) //m_PropertyEx.m_refreshLink.defaultValue = new VARIABLE("", // false, m_PropertyEx.m_refreshLink.title, "int", DEFAULT_DATALINK_VALUE_0, ""); // 初始化每一个扩展属性组(Pie控件只有一个属性组) PROPERTY_EX_GROUP group; // 列信息属性 group.strTitle = PIE_PROPERTY_EX_GROUP_NAME; group.strRelationKey = PIE_SLICE_COUNT_NAME; //初始化列信息每一个子分组 for (int i = 0; i < m_Property.m_nSliceCount; i++) { PROPERTY_EX_SUBGROUP subGroup; this->initSubGroup(subGroup, i, GROUP_INDEX_PIE); // 保存子分组 group.subGroups.push_back(subGroup); // 顺便初始化一下Value的预览数值(全为1) m_Values << 1.0; } // 保存本子分组定义信息 m_PropertyEx.m_groups.push_back(group); //// 2022-9-11 为刷新链接绑定默认值(系统的执行次数) //this->bindDefaultRefreshDataLink(); } /// /// 初始化一个扩展属性组 /// /// void VPieChart::initSubGroup(PROPERTY_EX_SUBGROUP& subGroup, int nIndex, int nSubGroupID) { if (nSubGroupID == GROUP_INDEX_PIE) { QString idx = QString::number(nIndex + 1); // 子分组名称 subGroup.strTitle = PIE_PROPERTY_EX_SUBGROUP_NAME + idx; // 列名称 + 数值 subGroup.strValueName = PIE_PROPERTY_EX_VALUE_NAME + idx; subGroup.strValue = PIE_PROPERTY_EX_VALUE_NAME + idx; // 分块颜色名称 + 数值 subGroup.strColorName = PIE_PROPERTY_EX_COLOR_NAME + idx; // 按顺序给一个默认值(可能会越界) if (nIndex < m_defaultColors.size()) { subGroup.color = m_defaultColors.at(nIndex); } else { subGroup.color = m_defaultColors.at(nIndex - m_defaultColors.size()); } // 数据链接名称 DataLink dataLink; dataLink.title = PIE_PROPERTY_EX_LINK_NAME + idx; dataLink.defaultValue = g_pGvlManager->getDefaultValueByName(DEFAULT_VALUE_NULLSTRING); subGroup.dataLinks.push_back(dataLink); } } /// /// 获取文字颜色 /// /// QColor VPieChart::getTextColor() const { return this->m_TextColor; } /// /// 获取边界颜色 /// /// QColor VPieChart::getBorderColor() const { return this->m_BorderColor; } /// /// 设置块数 /// /// void VPieChart::setSliceCount(const int& count) { if (this->m_Property.m_nSliceCount != count) { this->m_Property.m_nSliceCount = count; this->update(); } } /// /// 获取当前块数 /// int VPieChart::getSliceCount() { return this->m_Property.m_nSliceCount; } /// /// 设置分块文字 /// /// /// void VPieChart::updateSliceTitle(const QString& newValue, const int nIndex) { // 更新分块文字 QString& strValue = m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].strValue; if (strValue != newValue) { strValue = newValue; this->update(); } qDebug() << "VPieChart::updateColTitle - update [" << m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].strValueName << "] to [" << newValue << "]"; } /// /// 设置分块颜色 /// /// /// void VPieChart::updateSliceColor(QColor newValue, const int nIndex) { QColor& color = m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].color; if (color != newValue) { color = newValue; this->update(); } qDebug() << "VPieChart::updateSliceColor - update [" << m_PropertyEx.m_groups[GROUP_INDEX_PIE].subGroups[nIndex].strColorName << "] to [" << newValue << "]"; } /// /// 设置分块数值 /// /// /// void VPieChart::updateSliceValue(const int& newValue, const int nIndex) { if (newValue != m_Values[nIndex]) { m_Values[nIndex] = newValue; this->update(); } qDebug() << "VPieChart::updateSliceValue - update m_Values[" << nIndex << "] to [" << newValue << "]"; } /// /// 调整块属性数量(虚函数) /// /// void VPieChart::updateExPropertyCount(const int fixCount, const QString& strPropName) { // 获取属性的当前数量 int curCount = this->getExPropertyCountByName(strPropName); // 子分组编号 int curGroup = this->getGroupIDByName(strPropName); // 检查有效性 if (curGroup < 0) { qDebug() << "[Error] VTableControl::updateExPropertyCount - nGroupID in invalid: " << curGroup; return; } // 如果需要增加 if (fixCount > 0) { int nEnd = curCount + fixCount; for (int i = curCount; i < nEnd; i++) { PROPERTY_EX_SUBGROUP subItem; this->initSubGroup(subItem, i, curGroup); m_PropertyEx.m_groups[curGroup].subGroups.push_back(subItem); // 2022-1-5 顺便初始化一下Value的预览数值(全为1) m_Values << 1.0; } } // 如果需要减少 else if (fixCount < 0) { for (int i = 0; i < qAbs(fixCount); i++) { // 去掉对应的数据结构信息 m_PropertyEx.m_groups[curGroup].subGroups.pop_back(); // 同时去掉预览数值 m_Values.pop_back(); } } } /// /// 修改扩展属性(虚函数) /// /// /// void VPieChart::changeExProperties(QString strValueTitle, const QVariant& newValue) { // 如果改变了分块标题 if (strValueTitle.contains(PIE_PROPERTY_EX_VALUE_NAME)) { // 获取修改属性的索引 int nIndex = strValueTitle.remove(PIE_PROPERTY_EX_VALUE_NAME).toInt() - 1; // 如果索引值有效,则对应更新 if (nIndex >= 0) { // 修改对应Pie的数据结构内容 this->updateSliceTitle(newValue.toString(), nIndex); } } // 如果改变了分块颜色 else if (strValueTitle.contains(PIE_PROPERTY_EX_COLOR_NAME)) { // 获取修改属性的索引 int nIndex = strValueTitle.remove(PIE_PROPERTY_EX_COLOR_NAME).toInt() - 1; // 如果索引值有效,则对应更新 if (nIndex >= 0) { // 修改对应Pie的数据结构内容 this->updateSliceColor(newValue.value(), nIndex); } } // 如果改变了数据链接(此处应该不需要手工更新) else if (strValueTitle.contains(PIE_PROPERTY_EX_REFRESHLINK_NAME) || strValueTitle.contains(PIE_PROPERTY_EX_LINK_NAME) ) { } // Error:不应该执行到这里 else { qWarning() << "[Error]: VPieChart::changeExProperties - invalid strValueTitle: " << strValueTitle; } } void VPieChart::setExplodedAll(bool explodedAll) { if (this->explodedAll != explodedAll) { this->explodedAll = explodedAll; this->update(); } } void VPieChart::setExplodedIndex(int explodedIndex) { if (this->explodedIndex != explodedIndex) { this->explodedIndex = explodedIndex; this->update(); } } void VPieChart::setDefaultColor(bool defaultColor) { Q_UNUSED(defaultColor); } void VPieChart::setTextColor(const QColor &textColor) { if (this->m_TextColor != textColor) { this->m_TextColor = textColor; this->update(); } } void VPieChart::setBorderColor(const QColor &borderColor) { if (this->m_BorderColor != borderColor) { this->m_BorderColor = borderColor; this->update(); } } void VPieChart::setColors(const QVector &colors) { this->m_defaultColors = colors; this->update(); } void VPieChart::initPie() { } /// /// 设置显示百分比 /// void VPieChart::loadPercent() { showPercent = true; this->update(); } ///// ///// 清空饼图 ///// //void VPieChart::clearPie() //{ // m_Titles.clear(); // m_Values.clear(); //} void VPieChart::setHoleSize(double holeSize) { if (this->holeSize != holeSize) { this->holeSize = holeSize; this->update(); } } //========================================================= // // 辅助函数 // //========================================================= double VPieChart::getSumValue() { double sum = 0; for (int i = 0; i < m_Values.count(); i++) { sum += m_Values.at(i); } if (sum == 0.0) { sum = 1; } return sum; } QPoint VPieChart::getOffsetPoint(double angel, int offset) { int wl = 0; int hl = 0; double ang = (angel)*M_PI / 180; wl = offset * qCos(ang); hl = offset * qSin(ang); if (angel > 90 && angel < 270) { wl = qAbs(wl) * -1; } else { wl = qAbs(wl); } if (angel < 180) { hl = qAbs(hl) * -1; } else { hl = qAbs(hl); } return QPoint(wl, hl); }