上次在寫可視化數據大屏電子看板項目的時候,爲了逐步移除對QChart的依賴(主要是由於QChart真的太垃圾了,是全部Qt的模塊中源碼最爛的一個,看過源碼的人沒有一個不吐槽,不只不支持10W級別的數據量曲線展現,竟然一個餅圖控件,文字部分的展現還用QLabel來顯示的,這麼低效率的方式都有),起初曲線圖和柱狀圖等都用QCustomPlot替代了,就剩一個餅圖須要本身用無敵的QPainter來繪製了,繪製對應的背景區域難度不大,稍微會用QPainter的人均可以實現,用的就是drawPie繪製便可,關鍵是如何在本身所在的區域繪製對應的文字和百分比,這個須要找到對應區域,而後找到合理的位置擺放文字,這個可能就須要用到一點數學知識了,從圓中心開始,給定對應的角度,對應的偏離值,計算偏離值對應的中心點座標,此座標做爲繪製文字區域的中心,而後四周擴散必定的距離便可。程序員
#ifndef CUSTOMPIE_H #define CUSTOMPIE_H /** * 自定義餅圖控件 整理:feiyangqingyun(QQ:517216493) 2019-5-21 * 1:可設置文字顏色 * 2:可設置邊框顏色 * 3:可設置顏色集合 * 4:可設置某個區域是否彈出 * 5:可設置是否顯示百分比 */ #include <QWidget> #ifdef quc #if (QT_VERSION < QT_VERSION_CHECK(5,7,0)) #include <QtDesigner/QDesignerExportWidget> #else #include <QtUiPlugin/QDesignerExportWidget> #endif class QDESIGNER_WIDGET_EXPORT CustomPie : public QWidget #else class CustomPie : public QWidget #endif { Q_OBJECT Q_PROPERTY(QColor textColor READ getTextColor WRITE setTextColor) Q_PROPERTY(QColor borderColor READ getBorderColor WRITE setBorderColor) public: CustomPie(QWidget *parent = 0); ~CustomPie(); protected: void paintEvent(QPaintEvent *); void drawPie(QPainter *painter); private: bool explodedAll; //是否所有展開 int explodedIndex; //展開的索引 bool showPercent; //是否顯示百分比 double holeSize; //空心佔比 QColor textColor; //文字顏色 QColor borderColor; //邊框顏色 QList<QColor> colors; //顏色集合 QList<QString> labels; //標籤集合 QList<double> values; //值集合 private: //獲取總值 double getSumValue(); //根據偏移值獲取偏移點座標 QPoint getOffsetPoint(double angel, int offset = 6); public: QColor getTextColor() const; QColor getBorderColor() const; public Q_SLOTS: //設置是否所有展開+展開的索引 void setExplodedAll(bool explodedAll); void setExplodedIndex(int index); //設置是否啓用默認顏色 void setDefaultColor(bool defaultColor); //設置文字顏色+邊框顏色 void setTextColor(const QColor &textColor); void setBorderColor(const QColor &borderColor); //設置顏色集合 void setColors(const QList<QColor> &colors); //初始化餅圖 void initPie(); //添加餅圖數據 void appendPie(const QString &label, double value, const QString &tip = ""); //設置數據 void setDataPie(); //從新設置百分比 void loadPercent(); //清除餅圖 void clearPie(); //設置空心佔比 void setHoleSize(double holeSize); }; #endif // CUSTOMPIE_H
void CustomPie::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); } void CustomPie::drawPie(QPainter *painter) { painter->save(); int radius = 93; QRect rect(-radius, -radius, radius * 2, radius * 2); double startAngle = 0; double sum = getSumValue(); //逐個取出值並繪製餅圖區域和對應的文字 int count = labels.count(); for (int i = 0; i < count; ++i) { //取出值並計算當前值佔比面積 double value = 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(borderColor); QPoint center = pieRect.center(); int mid = startAngle + arcLength / 2; center += getOffsetPoint(mid); pieRect.moveCenter(center); } //從顏色集合中取出顏色 painter->setBrush(colors.at(i)); painter->drawPie(pieRect, startAngle * 16, arcLength * 16); QString strValue = labels.at(i); 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(textColor); painter->drawText(textRect, Qt::AlignCenter, strValue); startAngle += arcLength; } painter->restore(); }