QPainter绘制3D 饼状图

先展示图片 

核心代码如下:

pie.h

#ifndef Q3DPIE_H
#define Q3DPIE_H#include <QtGui/QPen>
#include <QtGui/QBrush>class  Pie
{
public:double value;           QBrush brush;           QString description;    double percentValue;QString percentStr;double startAngle;double spanAngle;QPointF startPoint;QPointF endPoint;QPointF centerPoint;bool isExploded;QRectF boundRect;QRectF boundShadowRect;private:};class PieSide
{
public:Pie *pie;double angle;
};#endif // Q3DPIE_H

q3dpiechart.h

#ifndef Q3DPIECHART_H
#define Q3DPIECHART_H#include <QWidget>
#include <QPainter>
#include <QToolTip>
#include <QMouseEvent>#include "q3dpiechart_global.h"
#include "pie.h"
#include<QPair>class  q3dpiechart : public QWidget
{
public:q3dpiechart(QWidget* parent = NULL);~q3dpiechart();public:void addPie(double v, const QString &desc);void addPie(double v, const QString &desc, const QColor &fillColor);void removePie(int pieIndex);void clear();protected:void paintEvent(QPaintEvent *event);void mousePressEvent(QMouseEvent *event);void mouseReleaseEvent(QMouseEvent *event);void mouseMoveEvent(QMouseEvent *event);void resizeEvent(QResizeEvent *event);void timerEvent(QTimerEvent * event);private:void refreshChart();void drawBackground(QPainter &painter);void drawBackground3DShadow(QPainter &painter, const QRectF &rect);void draw3DPieSuface(QPainter &painter, const Pie &pie, const QRectF &rcBound, const QBrush &brush, bool isTop = true);void draw3DPieLabel(QPainter &painter, const Pie &pie);void draw3DPieLabelOut(QPainter &painter, const Pie &pie, QRectF labelSizeRect);void drawLegend(QPainter &painter);void drawLegendCell(QPainter &painter, const Pie &pie, const QRectF &rcBound);void draw3DPieRectSide(QPainter &painter, const Pie &pie, QBrush &brush);void draw3DPieRectSide(QPainter &painter, const PieSide &pieSide, QBrush &brush);void draw3DPieArcSide(QPainter &painter, const Pie &pie, bool front=true);void drawEmptyPieChart(QPainter &painter);void showGridToolTip(const QPoint &pt);void calculatePieRect();//计算饼矩形范围void calculatePieData();//计算饼位置、百分比等void calculatePieLegendGrid();//计算图例表格宽度高度等void sortPieRectSide();//计算侧面的先后顺序QPainterPath make3DPieArcSidePath(double startAngle, double endAngle, const Pie &pie);//生成弧形侧面路径Pie *hitTest(QPoint pt);//判断是否点中double getMaxLegendWidth();//取得描述文字最宽的宽度void initDefaultColors();QColor getDefaultColor();QString getPieToolTip(const Pie &pie);QString getPieLabel(const Pie &pie);private:QVector<Pie> m_pies;QVector<PieSide> m_pieSides;QString m_unit;int m_startAngle;double m_totalValue;bool m_isLegendVisible;bool m_isLabelVisible;bool m_isTransparentBg;bool m_isTurning;bool m_isClockWiseTurning;QPen m_textPen;QPen m_borderPen;QRectF m_chartRect;double m_3DoffsetY;double m_explodedRadius;double m_chartMargin; double m_pieMargin;int m_pieColorAlpha; int m_pieColorAlphaDark; QRectF m_pieRect;QRectF m_pieShadowRect;QColor m_chartBgDarkColor;QColor m_chartBgLightColor;QColor m_chartBorderColor;double m_roundRadius;double m_pieWidthHeightRatio;int m_gridRowCount;int m_gridColCount;double m_gridCellWidth;double m_gridCellHeight;QRectF m_gridRect;QPoint m_mouseDownPoint;QVector<QColor> m_defaultColors;
};#endif // Q3DPIECHART_H

 q3dpiechart.cpp

#include "q3dpiechart.h"#include <math.h>
//#include <algorithm>const double PI = 3.1415926535;
const double EPSELON = 0.00001;q3dpiechart::q3dpiechart(QWidget* parent /* = NULL */):QWidget(parent)
{m_startAngle = 0;m_totalValue = 0.0;m_isLegendVisible = true;m_isLabelVisible = true;m_isTransparentBg = false; m_isTurning = false;m_isClockWiseTurning = true;m_gridRowCount = m_gridColCount = 1;m_textPen = QPen(QColor(0, 0, 0));m_borderPen = QPen(QColor::fromRgb(0, 0, 0, 64));m_pieColorAlpha = 152;m_pieColorAlphaDark = 200;m_chartBgDarkColor = QColor::fromRgb(213, 224, 241, 255);m_chartBgLightColor = QColor::fromRgb(255, 255, 255, 255);m_chartBorderColor = QColor::fromRgb(119, 141, 173);m_roundRadius = 10;m_chartMargin = 5;m_3DoffsetY = 20;m_pieMargin = 40;m_explodedRadius = 15;m_pieWidthHeightRatio = 2;initDefaultColors();startTimer(60);
}q3dpiechart::~q3dpiechart()
{}void q3dpiechart::addPie(double v, const QString &desc)
{addPie(v, desc, getDefaultColor());
}void q3dpiechart::addPie(double v, const QString &desc, const QColor &fillColor)
{Pie pie;pie.value = fabs(v);pie.description = desc;pie.brush = QBrush(fillColor);pie.isExploded = false;m_pies.append(pie);refreshChart();
}void q3dpiechart::removePie(int pieIndex)
{if (pieIndex < m_pies.count()){m_pies.remove(pieIndex);refreshChart();}
}void q3dpiechart::clear()
{if (m_pies.count() > 0){m_pies.clear();refreshChart();}
}void q3dpiechart::refreshChart()
{m_chartRect = rect().adjusted(m_chartMargin, m_chartMargin, -m_chartMargin, -m_chartMargin);m_totalValue = 0.0;for (QVector<Pie>::iterator itr = m_pies.begin();itr != m_pies.end(); itr++){m_totalValue += itr->value;}if (m_isLegendVisible){calculatePieLegendGrid();}calculatePieRect();calculatePieData();update();
}void q3dpiechart::paintEvent(QPaintEvent *event)
{QPainter painter(this);painter.setRenderHint(QPainter::Antialiasing);int pieCount = m_pies.count();if (!m_isTransparentBg){drawBackground(painter);}if (m_pieRect.width() <=0 || m_pieRect.height() <=0){return;}if(pieCount <= 0 || m_totalValue < EPSELON){drawEmptyPieChart(painter);return;}//画底面for(int i=0; i<pieCount; i++){const Pie &pie = m_pies[i];if (pie.percentValue >= EPSELON){QColor clr = pie.brush.color();clr.setAlpha(m_pieColorAlpha);QBrush brush(clr);draw3DPieSuface(painter, pie, pie.boundShadowRect, brush, false);}}//画弧形侧面,背面的for(int i=0; i<pieCount; i++){const Pie &pie = m_pies[i];if (pie.percentValue >= EPSELON){draw3DPieArcSide(painter, pie, false);}}//画矩形侧面for(int i=0; pieCount>1 && i<pieCount; i++){const PieSide &ps = m_pieSides[i];if (ps.pie->percentValue < EPSELON){continue;}QColor clr = ps.pie->brush.color();clr.setAlpha(m_pieColorAlpha);clr.setAlpha(224);//clr = Qt::black;QBrush brush(clr);draw3DPieRectSide(painter, ps, brush);}//画弧形侧面,前面的for(int i=0; i<pieCount; i++){const Pie &pie = m_pies[i];if (pie.percentValue >= EPSELON){draw3DPieArcSide(painter, pie);}}//画表面for(int i=0; i<pieCount; i++){const Pie &pie = m_pies[i];if (pie.percentValue >= EPSELON){QColor clr = pie.brush.color();clr.setAlpha(m_pieColorAlphaDark);QBrush brush(clr);draw3DPieSuface(painter, pie, pie.boundRect, brush);if (m_isLabelVisible){draw3DPieLabel(painter, pie);}}}if (m_isLegendVisible){drawLegend(painter);}
}void q3dpiechart::drawBackground(QPainter &painter)
{drawBackground3DShadow(painter, m_chartRect);QRectF &rect = m_chartRect;QPainterPath path;path.addRoundRect(rect, m_roundRadius);QLinearGradient gradient(rect.topLeft(), rect.bottomRight());gradient.setStart(rect.center().x(), rect.top());gradient.setFinalStop(rect.center().x(), rect.bottom());gradient.setColorAt(0, m_chartBgDarkColor);gradient.setColorAt(1, m_chartBgLightColor);QBrush brush(gradient);painter.fillPath(path, brush);QPen pen(m_chartBorderColor);pen.setWidth(2);painter.setPen(pen);painter.drawPath(path);
}void q3dpiechart::drawBackground3DShadow(QPainter &painter, const QRectF &rect)
{float radius = rect.height() > rect.width() ? rect.width() / 20 : rect.height()/20;float offset = radius / 3;QColor clr(255, 255, 255, 0);QRectF rc = rect.translated(offset, offset);QRectF topRightArcRect = rc.adjusted(rc.width() - radius * 2, 0, 0, - rc.height() + radius * 2);QPainterPath topRightArcPath;topRightArcPath.moveTo(topRightArcRect.center());topRightArcPath.arcTo(topRightArcRect, 0, 90);QRadialGradient topRightRG(topRightArcRect.center(), radius);topRightRG.setColorAt(0, Qt::black);topRightRG.setColorAt(1, clr);QBrush topRightBrush(topRightRG);painter.fillPath(topRightArcPath, topRightBrush);QRectF rightRc = topRightArcRect.adjusted(radius, radius, 0, rect.height() - 3 * radius);QLinearGradient rightLG(rightRc.left(), 0, rightRc.right(), 0);rightLG.setColorAt(0, Qt::black);rightLG.setColorAt(1, clr);QBrush rightBrush(rightLG);QPainterPath rightPath;rightPath.addRect(rightRc);painter.fillPath(rightPath, rightBrush);QRectF leftBottomArcRect = rc.adjusted(0, rc.height() - radius * 2, -rc.width() + radius * 2, 0);QPainterPath leftBottomPath;leftBottomPath.moveTo(leftBottomArcRect.center());leftBottomPath.arcTo(leftBottomArcRect, -90, -180);QRadialGradient leftBottomRG(leftBottomArcRect.center(), radius);leftBottomRG.setColorAt(0, Qt::black);leftBottomRG.setColorAt(1, clr);QBrush leftBottomBrush(leftBottomRG);painter.fillPath(leftBottomPath, leftBottomBrush);QRectF bottomRc = leftBottomArcRect.adjusted(radius, radius, rect.width() - 3 * radius, 0);QLinearGradient bottomLG(0, bottomRc.top(), 0, bottomRc.bottom());bottomLG.setColorAt(0, Qt::black);bottomLG.setColorAt(1, clr);QBrush bottomBrush(bottomLG);QPainterPath bottomPath;bottomPath.addRect(bottomRc);painter.fillPath(bottomPath, bottomBrush);QRectF bottomRightArcRect = leftBottomArcRect.translated(rect.width() - radius * 2, 0);QPainterPath bottomRightPath;bottomRightPath.moveTo(bottomRightArcRect.center());bottomRightPath.arcTo(bottomRightArcRect, 0, -90);QRadialGradient bottomRightRG(bottomRightArcRect.center(), radius);bottomRightRG.setColorAt(0, Qt::black);bottomRightRG.setColorAt(1, clr);QBrush bottomRightBrush(bottomRightRG);painter.fillPath(bottomRightPath, bottomRightBrush);
}void q3dpiechart::draw3DPieSuface(QPainter &painter, const Pie &pie, const QRectF &rcBound, const QBrush &brush, bool isTop/* = true*/)
{QPainterPath path;QPoint ptOffset;if (!isTop){ptOffset.setY(m_3DoffsetY);}path.moveTo(rcBound.center());path.lineTo(pie.startPoint + ptOffset);path.arcTo(rcBound, pie.startAngle, pie.spanAngle);path.closeSubpath();painter.fillPath(path, brush);painter.setPen(m_borderPen);painter.drawArc(rcBound, pie.startAngle * 16, pie.spanAngle * 16);
}void q3dpiechart::draw3DPieLabel(QPainter &painter, const Pie &pie)
{painter.setFont(this->font());painter.setPen(m_textPen);if (1 == m_pies.count()){painter.drawText(m_pieRect, Qt::AlignCenter, pie.percentStr);return;}QString label = getPieLabel(pie);QRectF rc;rc = QRectF(pie.centerPoint, pie.centerPoint);QFontMetricsF metrics(this->font());double quarterW = metrics.width(label) / 2;//m_pieRect.width() / 4;double halfH = metrics.height() / 2;//m_pieRect.height() / 2;rc.adjust(-quarterW, -halfH, quarterW, halfH);QPainterPath path;path.moveTo(pie.boundRect.center());path.lineTo(pie.startPoint);path.arcTo(pie.boundRect, pie.startAngle, pie.spanAngle);path.closeSubpath();if (path.contains(rc)){painter.drawText(rc, Qt::AlignCenter, label);}else{draw3DPieLabelOut(painter, pie, rc);}
}void q3dpiechart::draw3DPieLabelOut(QPainter &painter, const Pie &pie, QRectF labelSizeRect)
{double centerAngle = pie.startAngle + pie.spanAngle / 2;if (centerAngle > 360){centerAngle -= 360;}QPainterPath path;path.arcMoveTo(pie.boundRect, centerAngle);if (centerAngle >= 0 && centerAngle <90){labelSizeRect.moveBottomLeft(path.currentPosition());painter.drawLine(pie.centerPoint, path.currentPosition());}else if (centerAngle >= 90 && centerAngle <180){labelSizeRect.moveBottomRight(path.currentPosition());painter.drawLine(pie.centerPoint, path.currentPosition());}else if (centerAngle >= 180 && centerAngle <270){labelSizeRect.moveTopRight(path.currentPosition());painter.drawLine(pie.centerPoint, labelSizeRect.bottomRight());}else if (centerAngle >= 270 && centerAngle <=360){labelSizeRect.moveTopLeft(path.currentPosition());painter.drawLine(pie.centerPoint, labelSizeRect.bottomLeft());}painter.drawLine(labelSizeRect.bottomLeft(), labelSizeRect.bottomRight());painter.drawText(labelSizeRect, Qt::AlignLeft, getPieLabel(pie));
}void q3dpiechart::drawLegend(QPainter &painter)
{}void q3dpiechart::drawLegendCell(QPainter &painter, const Pie &pie, const QRectF &rcBound)
{}void q3dpiechart::draw3DPieRectSide(QPainter &painter, const Pie &pie, QBrush &brush)
{QPainterPath path;QPoint offsetPt(0, m_3DoffsetY);path.moveTo(pie.boundRect.center());path.lineTo(pie.boundShadowRect.center());path.lineTo(pie.startPoint + offsetPt);path.lineTo(pie.startPoint);path.lineTo(pie.boundRect.center());painter.fillPath(path, brush);path = QPainterPath();path.moveTo(pie.boundRect.center());path.lineTo(pie.boundShadowRect.center());path.lineTo(pie.endPoint + offsetPt);path.lineTo(pie.endPoint);path.lineTo(pie.boundRect.center());painter.fillPath(path, brush);if (pie.spanAngle != 180){painter.setPen(m_borderPen);painter.drawLine(pie.boundRect.center(), pie.boundShadowRect.center());}
}void q3dpiechart::draw3DPieRectSide(QPainter &painter, const PieSide &pieSide, QBrush &brush)
{QPainterPath path;QPoint offsetPt(0, m_3DoffsetY);Pie &pie = *pieSide.pie;if (pie.startAngle == pieSide.angle){path.moveTo(pie.boundRect.center());path.lineTo(pie.boundShadowRect.center());path.lineTo(pie.startPoint + offsetPt);path.lineTo(pie.startPoint);path.lineTo(pie.boundRect.center());}else{path.moveTo(pie.boundRect.center());path.lineTo(pie.boundShadowRect.center());path.lineTo(pie.endPoint + offsetPt);path.lineTo(pie.endPoint);path.lineTo(pie.boundRect.center());}painter.fillPath(path, brush);if (pie.spanAngle != 180){painter.setPen(m_borderPen);painter.drawLine(pie.boundRect.center(), pie.boundShadowRect.center());}
}void q3dpiechart::draw3DPieArcSide(QPainter &painter, const Pie &pie, bool front/*=true*/)
{QColor clrDark = pie.brush.color();double darkRate = 0.7;clrDark = QColor::fromRgb(clrDark.red() * darkRate, clrDark.green() * darkRate, clrDark.blue() * darkRate);clrDark.setAlpha(224);QColor clrLight = pie.brush.color();clrLight.setAlpha(0);clrLight = QColor::fromRgb(255, 255, 255, 128);QLinearGradient lg(pie.boundRect.left(), 0, pie.boundRect.right(), 0);lg.setColorAt(0, clrDark);lg.setColorAt(0.5, clrLight);lg.setColorAt(1, clrDark);QBrush frontBrush(lg);//正面的刷子为发光状QBrush backBrush(pie.brush.color());//背面的刷子不发光QPointF offsetPt(0, m_3DoffsetY);QPainterPath path;//存放拆分后的的路径和画刷对std::vector< QPair<QPainterPath, QBrush> > arcSides;
//    std::vector< std::pair<QPainterPath, QBrush> > arcSides;double endAngle = pie.startAngle + pie.spanAngle;//出现与水平面相交时,进行拆分if ((pie.startAngle<180 && endAngle > 180)||(pie.startAngle>=180 && endAngle > 360)){double startAngle = pie.startAngle;//当前的起始角度if (pie.startAngle <180)//一二象限{path = make3DPieArcSidePath(startAngle, 180, pie);arcSides.push_back(QPair<QPainterPath, QBrush>(path, backBrush));
//            arcSides.push_back(std::make_pair<QPainterPath, QBrush>(path, backBrush));startAngle = 180;QBrush tempBrush = frontBrush;if (endAngle > 360){path = make3DPieArcSidePath(startAngle, 360, pie);arcSides.push_back(QPair<QPainterPath, QBrush>(path, frontBrush));
//                arcSides.push_back(std::make_pair<QPainterPath, QBrush>(path, frontBrush));startAngle = 360;tempBrush = backBrush;}path = make3DPieArcSidePath(startAngle, endAngle, pie);arcSides.push_back(QPair<QPainterPath, QBrush>(path, tempBrush));
//            arcSides.push_back(std::make_pair<QPainterPath, QBrush>(path, tempBrush));}else//三四象限{path = make3DPieArcSidePath(startAngle, 360, pie);arcSides.push_back(QPair<QPainterPath, QBrush>(path, frontBrush));
//            arcSides.push_back(std::make_pair<QPainterPath, QBrush>(path, frontBrush));startAngle = 360;QBrush tempBrush = backBrush;if (endAngle > 540){path = make3DPieArcSidePath(startAngle, 540, pie);arcSides.push_back(QPair<QPainterPath, QBrush>(path, backBrush));
//                arcSides.push_back(std::make_pair<QPainterPath, QBrush>(path, backBrush));startAngle = 540;tempBrush = frontBrush;}path = make3DPieArcSidePath(startAngle, endAngle, pie);arcSides.push_back(QPair<QPainterPath, QBrush>(path, tempBrush));
//            arcSides.push_back(std::make_pair<QPainterPath, QBrush>(path, tempBrush));}}else//不相交时{path = make3DPieArcSidePath(pie.startAngle, pie.startAngle + pie.spanAngle, pie);
//        arcSides.push_back(std::make_pair<QPainterPath, QBrush>(path, pie.startAngle >= 180 ? frontBrush : backBrush));arcSides.push_back(QPair<QPainterPath, QBrush>(path, pie.startAngle >= 180 ? frontBrush : backBrush));}for (int i=0; i<arcSides.size(); i++){QPair<QPainterPath, QBrush> &arcSide = arcSides.at(i);
//        std::pair<QPainterPath, QBrush> &arcSide = arcSides.at(i);if ((front && arcSide.second == frontBrush)|| (!front && arcSide.second == backBrush)){painter.fillPath(arcSide.first, arcSide.second);}}
}QPainterPath q3dpiechart::make3DPieArcSidePath(double startAngle, double endAngle, const Pie &pie)
{QPainterPath path;path.arcMoveTo(pie.boundRect, startAngle);path.arcTo(pie.boundRect, startAngle, endAngle - startAngle);QPointF pt = path.currentPosition();pt.setY(pt.y() + m_3DoffsetY);path.lineTo(pt);path.arcTo(pie.boundShadowRect, endAngle, startAngle - endAngle);path.closeSubpath();return path;
}void q3dpiechart::drawEmptyPieChart(QPainter &painter)
{QRectF rcBound = m_pieRect;rcBound.setHeight(rcBound.height() - m_3DoffsetY);QPointF pt1(rcBound.left(), rcBound.center().y());QPen borderPen(Qt::darkGray);painter.setPen(borderPen);painter.drawEllipse(rcBound);rcBound.translate(0, m_3DoffsetY);QPointF pt2(rcBound.left(), rcBound.center().y());painter.drawEllipse(rcBound);painter.drawLine(pt1, pt2);pt1.setX(pt1.x() + rcBound.width());pt2.setX(pt2.x() + rcBound.width());painter.drawLine(pt1, pt2);
}void q3dpiechart::showGridToolTip(const QPoint &pt)
{}void q3dpiechart::calculatePieRect()
{const int legendWidth = m_gridRect.width();if (0 == m_pies.count() || !m_isLegendVisible)//没有添加内容或不显示描述信息,以整个区域为饼图区域{m_pieRect = m_chartRect.adjusted(m_pieMargin, m_pieMargin, -m_pieMargin, -m_pieMargin);}else{m_pieRect = m_chartRect.adjusted(m_pieMargin, m_pieMargin, -m_pieMargin, -m_pieMargin - legendWidth);}double w = m_pieRect.width();double h = m_pieRect.height();if (h <= 0 || w <=0){m_pieRect.setRect(0, 0, 0, 0);return;}if (w / h > m_pieWidthHeightRatio){double half = (w - h * m_pieWidthHeightRatio) / 2;m_pieRect.adjust(half, 0, -half, 0);}else{double half = (h - w / m_pieWidthHeightRatio) / 2;m_pieRect.adjust(0, half, 0, -half);}m_3DoffsetY = m_pieRect.height() / 5;double maxOffsetY = m_pieMargin * 2;if (m_3DoffsetY > maxOffsetY){m_3DoffsetY = maxOffsetY;}m_explodedRadius = m_pieRect.height() / 6;double maxExplodedRadius = m_pieMargin * 1.5;if (m_explodedRadius > maxExplodedRadius){m_explodedRadius = maxExplodedRadius;}m_pieShadowRect = m_pieRect.translated(0, m_3DoffsetY);
}void q3dpiechart::calculatePieData()
{double startAngle = m_startAngle;double quarterW = m_pieRect.width() / 6;double quarterH = m_pieRect.height() / 6;QRectF halfPieRect = m_pieRect.adjusted(quarterW, quarterH, -quarterW, -quarterH);QRectF explodedRect(m_pieRect.center(), m_pieRect.center());explodedRect.adjust(-m_explodedRadius, -m_explodedRadius, m_explodedRadius, m_explodedRadius);for (int i=0; i<m_pies.count(); i++){Pie &pie = m_pies[i];pie.startAngle = startAngle;QPainterPath path;path.arcMoveTo(m_pieRect, startAngle);pie.startPoint = path.currentPosition();//起始点if (m_totalValue < EPSELON){pie.percentValue = 0;//1.0 / m_pies.count();}else{pie.percentValue = pie.value / m_totalValue;//所占比例}pie.percentStr = QString("%1%").arg(pie.percentValue*100, 3, 'G', 3);pie.spanAngle = 360 * pie.percentValue;//跨度if (i == m_pies.count()-1)//最后一个饼,用360度减去前面的总度数,以防止由于误差导致的合不上{pie.spanAngle = 360 - (startAngle - m_startAngle);}path.arcTo(m_pieRect, startAngle, pie.spanAngle);pie.endPoint = path.currentPosition();path.arcMoveTo(halfPieRect, startAngle + pie.spanAngle / 2);pie.centerPoint = path.currentPosition();if (pie.isExploded){path.arcMoveTo(explodedRect, startAngle + pie.spanAngle / 2);QPointF pt = path.currentPosition() - m_pieRect.center();pie.centerPoint += pt;pie.startPoint += pt;pie.endPoint += pt;pie.boundRect = m_pieRect.translated(pt);}else{pie.boundRect = m_pieRect;}pie.boundShadowRect = pie.boundRect.translated(0, m_3DoffsetY);startAngle += pie.spanAngle;while (pie.startAngle > 360){pie.startAngle -= 360;}}sortPieRectSide();
}void q3dpiechart::calculatePieLegendGrid()
{QFontMetricsF metrics(this->font());m_gridCellHeight = metrics.height() * 2;m_gridCellWidth = getMaxLegendWidth();//metrics.width("123456789012345");m_gridCellWidth += m_gridCellHeight + m_gridCellHeight / 4;//描述前留下一个高度的方块,用于显示颜色,后面留下1/4高度的宽度,以防止抵到表格右边if (m_gridCellWidth > m_chartRect.width() / 2){m_gridCellWidth = m_chartRect.width() / 2;}m_gridColCount = m_pies.count();m_gridRowCount = 1;m_gridRect = m_chartRect.adjusted(m_pieMargin, m_pieMargin, -m_pieMargin, -m_pieMargin);m_gridRect.setLeft(m_gridRect.width() - m_gridCellWidth);
}//排序规则:0-180度的越靠近90度越在后面(显示顺序),180-360的越靠近270度越在前面(显示顺序)
bool pieSideLesTthan(const PieSide &s1, const PieSide &s2)
{if (s1.angle <180){if (s2.angle >=180){return true;}else{return fabs(s1.angle - 90) < fabs(s2.angle - 90);}}else{if (s2.angle >=180){return fabs(s1.angle - 270) > fabs(s2.angle - 270);}else{return false;}}
}void q3dpiechart::sortPieRectSide()
{m_pieSides.clear();for (int i=0; i<m_pies.count(); i++){Pie &pie = m_pies[i];double startAngle = pie.startAngle;double endAngle = startAngle + pie.spanAngle;if (endAngle>=360){endAngle-=360;}//每个Pie有两边PieSide startSide = {&pie, startAngle};PieSide endSide = {&pie, endAngle};m_pieSides.push_back(startSide);m_pieSides.push_back(endSide);}qSort(m_pieSides.begin(), m_pieSides.end(), pieSideLesTthan);
}Pie *q3dpiechart::hitTest(QPoint pt)
{//优先判断是否点中上表面和三四象限的弧侧面for (int i=0; i<m_pies.count(); i++){Pie &pie = m_pies[i];QPainterPath path;path.moveTo(pie.boundRect.center());path.arcMoveTo(pie.boundRect, pie.startAngle);path.lineTo(path.currentPosition());path.arcTo(pie.boundRect, pie.startAngle, pie.spanAngle);path.lineTo(pie.boundRect.center());//点在了正面if (path.contains(pt)){return &pie;}//如果是点在180-360的弧形侧面,也算点中了,分几种情况进行判断double endAngle = pie.startAngle + pie.spanAngle;if (pie.startAngle < 180 && endAngle >180){if (make3DPieArcSidePath(180, qMin(360.0, endAngle), pie).contains(pt)){return &pie;}}else if (pie.startAngle >180){if (make3DPieArcSidePath(pie.startAngle, qMin(360.0, endAngle), pie).contains(pt)){return &pie;}if (endAngle > 540){if (make3DPieArcSidePath(540, endAngle, pie).contains(pt)){return &pie;}}}}//再判断Pie的矩形侧面for (int i=m_pieSides.count()- 1; i>-1; i--){PieSide &pieSide = m_pieSides[i];Pie &pie = *pieSide.pie;QPainterPath path;QPoint offsetPt(0, m_3DoffsetY);if (pie.startAngle == pieSide.angle){path.moveTo(pie.boundRect.center());path.lineTo(pie.boundShadowRect.center());path.lineTo(pie.startPoint + offsetPt);path.lineTo(pie.startPoint);path.lineTo(pie.boundRect.center());if (path.contains(pt)){return &pie;}}else{path.moveTo(pie.boundRect.center());path.lineTo(pie.boundShadowRect.center());path.lineTo(pie.endPoint + offsetPt);path.lineTo(pie.endPoint);path.lineTo(pie.boundRect.center());if (path.contains(pt)){return &pie;}}}return NULL;
}double q3dpiechart::getMaxLegendWidth()
{return 0;
}void q3dpiechart::mousePressEvent(QMouseEvent *event)
{m_mouseDownPoint = event->globalPos();
}void q3dpiechart::mouseReleaseEvent(QMouseEvent *event)
{if (event->button() == Qt::LeftButton && m_pies.count() >1 && m_mouseDownPoint == event->globalPos()){Pie *pie = hitTest(event->pos());if (pie != NULL){pie->isExploded = !pie->isExploded;refreshChart();}}
}void q3dpiechart::mouseMoveEvent(QMouseEvent *event)
{//Pie *pie = hitTest(event->pos());//if (pie != NULL)//{//    setToolTip(getPieToolTip(*pie));//}
}QString q3dpiechart::getPieToolTip(const Pie &pie)
{return QString("<font color=red>%1</font>:<font color=green>%2</font><font color=blue>(%3)</font>").arg(pie.description).arg(pie.value/*, 0, 'f', 2*/).arg(pie.percentStr);
}QString q3dpiechart::getPieLabel(const Pie &pie)
{return QString("%1:%2").arg(pie.description).arg(pie.percentStr);
}void q3dpiechart::resizeEvent(QResizeEvent *event)
{refreshChart();
}void q3dpiechart::timerEvent(QTimerEvent * event)
{if (m_isTurning){m_startAngle += m_isClockWiseTurning ? 1 : -1;if (m_startAngle < 0){m_startAngle += 360;}else if (m_startAngle > 360){m_startAngle -= 360;}calculatePieData();update();}
}void q3dpiechart::initDefaultColors()
{m_defaultColors.clear();m_defaultColors.append(QColor(192, 0, 0, 255));m_defaultColors.append(QColor(0, 192, 0, 255));m_defaultColors.append(QColor(192, 192, 0, 255));m_defaultColors.append(QColor(192, 0, 192, 255));m_defaultColors.append(QColor(0, 192, 192, 255));int r = 0;int g = 0;int b = 192;for (int i=0; i<20; i++){m_defaultColors.append(QColor(r, g, b, 255));r += 48;g += 24;b += 24;if (r > 255){r -=255;}if (g > 255){g -= 255;}if (b > 255){b -= 255;}}
}QColor q3dpiechart::getDefaultColor()
{if (m_defaultColors.count() == 0){return Qt::blue;}int index = m_pies.count();if (index >= m_defaultColors.count()){index = 0;}return m_defaultColors[index];
}

q3dpiechart.pro文件

QT       += core guigreaterThan(QT_MAJOR_VERSION, 4): QT += widgetsCONFIG += c++11
TEMPLATE = app
#TARGET = AutomationFORMS += \q3dpiechart_demo.uiHEADERS += \../q3dpiechart/pie.h \../q3dpiechart/q3dpiechart.h \q3dpiechart_demo.hSOURCES += \../q3dpiechart/q3dpiechart.cpp \main.cpp \q3dpiechart_demo.cpp

q3dpiechart_demo.cpp

#include "q3dpiechart_demo.h"#include "../q3dpiechart/q3dpiechart.h"#include <QBoxLayout>q3dpiechart_demo::q3dpiechart_demo(QWidget *parent, Qt::WindowFlags flags): QDialog(parent, flags)
{ui.setupUi(this);q3dpiechart *piechart = new q3dpiechart(this);piechart->addPie(10, "aaa");piechart->addPie(13, "bbb");piechart->addPie(11, "ccc");QHBoxLayout *layout = new QHBoxLayout(this);layout->addWidget(piechart);setLayout(layout);}q3dpiechart_demo::~q3dpiechart_demo()
{}

q3dpiechart_demo.h

#ifndef Q3DPIECHART_DEMO_H
#define Q3DPIECHART_DEMO_H#include <QDialog>
#include "ui_q3dpiechart_demo.h"class q3dpiechart_demo : public QDialog
{Q_OBJECTpublic:q3dpiechart_demo(QWidget *parent = 0, Qt::WindowFlags flags = 0);~q3dpiechart_demo();private:Ui::q3dpiechart_demoClass ui;
};#endif // Q3DPIECHART_DEMO_H

q3dpiechart_demo.ui

<ui version="4.0" ><class>q3dpiechart_demoClass</class><widget class="QDialog" name="q3dpiechart_demoClass" ><property name="geometry" ><rect><x>0</x><y>0</y><width>652</width><height>483</height></rect></property><property name="windowTitle" ><string>q3dpiechart_demo</string></property></widget><layoutdefault spacing="6" margin="11" /><resources/><connections/>
</ui>

main.cpp文件 

#include <QApplication>
#include "q3dpiechart_demo.h"int main(int argc, char *argv[])
{QApplication a(argc, argv);q3dpiechart_demo w;w.show();return a.exec();
}

 qt 3d 饼图 下载链接:

https://download.csdn.net/download/weixin_41882459/90407325

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/23321.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

VMWare安装Debian操作系统

参考链接 https://blog.csdn.net/weixin_61536532/article/details/129778310 注意 如果希望折腾Linux&#xff0c;建议缺省使用英语。在极端情况下&#xff0c;系统可能会只能输出ASCII码&#xff0c;使用中文可能会导致无法正常打印log 本文使用VMWare WorkStation Pro&a…

Compose 常用UI组件

Compose 常用UI组件 概述Modifier 修饰符常用Modifier修饰符作用域限定Modifier Modifier 实现原理Modifier.Element链的构建链的解析 常用基础组件文字组件图片组件按钮组件选择器对话框进度条 常用布局组件线性布局帧布局 列表组件 概述 Compose 预置了很多基础组件&#xf…

基于Python+django+mysql旅游数据爬虫采集可视化分析推荐系统

2024旅游推荐系统爬虫可视化&#xff08;协同过滤算法&#xff09; 基于Pythondjangomysql旅游数据爬虫采集可视化分析推荐系统 有文档说明 部署文档 视频讲解 ✅️基于用户的协同过滤推荐算法 卖价就是标价~ 项目技术栈 Python语言、Django框架、MySQL数据库、requests网络爬虫…

R 语言科研绘图 --- 散点图-汇总

在发表科研论文的过程中&#xff0c;科研绘图是必不可少的&#xff0c;一张好看的图形会是文章很大的加分项。 为了便于使用&#xff0c;本系列文章介绍的所有绘图都已收录到了 sciRplot 项目中&#xff0c;获取方式&#xff1a; R 语言科研绘图模板 --- sciRplothttps://mp.…

3分钟idea接入deepseek

DeepSeek简介 DeepSeek 是杭州深度求索人工智能基础技术研究有限公司开发的一系列大语言模型&#xff0c;背后是知名量化资管巨头幻方量化3。它专注于开发先进的大语言模型和相关技术&#xff0c;拥有多个版本的模型&#xff0c;如 DeepSeek-LLM、DeepSeek-V2、DeepSeek-V3 等&…

【数据结构】(12) 反射、枚举、lambda 表达式

一、反射 1、反射机制定义及作用 反射是允许程序在运行时检查和操作类、方法、属性等的机制&#xff0c;能够动态地获取信息、调用方法等。换句话说&#xff0c;在编写程序时&#xff0c;不需要知道要操作的类的具体信息&#xff0c;而是在程序运行时获取和使用。 2、反射机制…

基于Flask的去哪儿网海南旅游攻略数据分析系统的设计与实现

【Flask】基于Flask的去哪儿网海南旅游攻略数据分析系统的设计与实现&#xff08;完整系统源码开发笔记详细部署教程&#xff09;✅ 目录 一、项目简介二、项目界面展示三、项目视频展示 一、项目简介 随着旅游业的蓬勃发展&#xff0c;如何高效地整合、分析并呈现旅游数据&am…

从零开始构建一个小型字符级语言模型的完整详细教程(基于Transformer架构)

最近特别火的DeepSeek,是一个大语言模型,那一个模型是如何构建起来的呢?DeepSeek基于Transformer架构,接下来我们也从零开始构建一个基于Transformer架构的小型语言模型,并说明构建的详细步骤及内部组件说明。我们以构建一个字符级语言模型(Char-Level LM)为例,目标是通…

MyBatisPlus学习

MyBatisPlus 今日目标 基于MyBatisPlus完成标准Dao的增删改查功能 掌握MyBatisPlus中的分页及条件查询构建 掌握主键ID的生成策略 了解MyBatisPlus的代码生成器 本节主要讲的内容如下&#xff1a; 1&#xff0c;MyBatisPlus入门案例与简介 这一节我们来学习下MyBatisPlus的入…

golang内存泄漏

golang也用了好几年了&#xff0c;趁着有空 整理归纳下&#xff0c;以后忘了好看下 一般认为 Go 10次内存泄漏&#xff0c;8次goroutine泄漏&#xff0c;1次是真正内存泄漏&#xff0c;还有1次是cgo导致的内存泄漏 1:环境 go1.20 win10 2:goroutine泄漏 单个Goroutine占用内存&…

计算机毕业设计SpringBoot+Vue.jst房屋租赁系统(源码+LW文档+PPT+讲解)

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长联系方式的名片&#xff01; 作者简介&#xff1a;Java领…

DevEco Studio常用快捷键以及如何跟AndroidStudio的保持同步

DevEco Studio快捷键 DevEco Studio是华为推出的用于开发HarmonyOS应用的集成开发环境&#xff0c;它提供了丰富的快捷键以提高开发效率&#xff0c;以下为你详细介绍不同操作场景下的常用快捷键&#xff1a; 通用操作快捷键 操作描述Windows/Linux 快捷键Mac 快捷键打开设置窗…

qt5实现表盘的旋转效果,通过提升QLabel类

因为工作需要&#xff0c;需要实现温度的表盘展示效果 实现思路&#xff1a; 通过提示声QLabel控价类&#xff0c;实现报盘的旋转和展示效果 1. 编写一个QLabel的类MyQLabel,实现两个方法 1. void paintEvent(QPaintEvent *event); //重绘函数 2. void valueChanged(int va…

vscode settings(一):全局| 用户设置常用的设置项

参考资料 Visual Studio Code权威指南 by 韩骏 一. 全局设置与用户设置 1.1 Vscode支持两种不同范围的设置 用户设置(User Settings)&#xff1a;这是一个全局范围的设置&#xff0c;会应用到所有的Visual Studio Code实例中。工作区设置(Workspace Settings)&#xff1a;设…

C# 将非托管Dll嵌入exe中(一种实现方法)

一、环境准备 电脑系统&#xff1a;Windows 10 专业版 20H2 IDE&#xff1a;Microsoft Visual Studio Professional 2022 (64 位) - Current 版本 17.11.4 其他&#xff1a; 二、测试目的 将基于C创建DLL库&#xff0c;封装到C#生成的exe中。 一般C创建的库&#xff0c;在…

在 Mac mini M2 上使用Docker快速部署MaxKB:打造本地知识库问答系统

随着大语言模型的广泛应用&#xff0c;知识库问答系统逐渐成为提升工作效率和个人学习的有力工具。MaxKB是一款基于LLM&#xff08;Large Language Model&#xff09;大语言模型的知识库问答系统&#xff0c;支持多模型对接、文档上传和自动爬取等功能。本文将详细介绍如何在Ma…

Jenkins上无法查看已成功生成的Junit报告

如果你已确认 JUnit 报告在工作空间中被成功生成&#xff0c;但在 Jenkins 构建页面上却看不到 "Test Result" 或 "Test Report" 的链接&#xff0c;这通常意味着 Jenkins 没有正确地配置用来处理和显示这些报告的步骤。这里有几个可能的原因和解决方法&am…

vue+element-plus简洁完美实现淘宝网站模板

目录 一、项目介绍 二、项目截图 1.项目结构图 2.首页 3.详情 4.购物车 5.登陆页 三、源码实现 1.路由配置 2.依赖包 四、总结 一、项目介绍 项目在线预览&#xff1a;点击访问 本项目为vue项目&#xff0c;参考淘宝官方样式为主题来设计元素&#xff0c;简洁美观&…

stm32hal库寻迹+蓝牙智能车(STM32F103C8T6)

简介: 这个小车的芯片是STM32F103C8T6&#xff0c;其他的芯片也可以照猫画虎,基本配置差不多,要注意的就是,管脚复用,管脚的特殊功能,(这点不用担心,hal库每个管脚的功能都会给你罗列,很方便的.)由于我做的比较简单,只是用到了几个简单外设.主要是由带霍尔编码器电机的车模,电机…

红队内网攻防渗透:内网渗透之内网对抗:实战项目VPC2打靶父子域三层路由某绒免杀下载突破约束委派域控提权

红队内网攻防渗透 实战网络攻防靶场记录1.靶机配置信息讲解2.靶场渗透完整流程2.1 入口点:192.168.139.130(win2008 R2)2.1.1 tomcat后台war包获取权限2.1.2 tomcat使用后门上线CS平台2.1.3 信息收集获取数据库密码2.2 入口点横向:192.168.10.11 (win2012 SQL)2.2.1 SQLs…