#include "stdafx.h"
#include "PieChart.h"
struct PieItemPrivate
{
PieItem item;
QPainterPath path;
QPoint labelPos;
QRect m_LegendRect;
};
struct PieChartPrivate
{
bool m_bLegendVisible = true;
int m_Minx = 25;
int m_Miny = 25;
int m_MinDiameter = 130;
int m_RingWidth = 40;
int m_StartRotationAngle = 0;
int m_LegendWidth = 100;
int m_LegendHeight = 30;
double m_SumValue = 0;
QRect m_PieRect;
QColor m_LabelColor = QColor(0, 0, 0);
QString m_RingLabel = QStringLiteral("饼图");
QVector<PieItemPrivate> m_Items;
};
PieChart::PieChart(QWidget * parent)
: QWidget(parent)
, d_ptr(new PieChartPrivate)
{
setWindowTitle("Pie example");
setMinimumSize(d_ptr->m_MinDiameter + d_ptr->m_LegendWidth * 2 + d_ptr->m_Minx * 2, d_ptr->m_MinDiameter + d_ptr->m_Miny * 2);
}
PieChart::~PieChart()
{
}
void PieChart::SetStartX(int x)
{
d_ptr->m_Minx = x;
}
void PieChart::SetStartY(int y)
{
d_ptr->m_Miny = y;
}
void PieChart::SetMinDiameter(int diameter)
{
d_ptr->m_MinDiameter = diameter;
}
void PieChart::SetStartAngle(int angle)
{
d_ptr->m_StartRotationAngle = angle;
}
void PieChart::SetRingWidth(int width)
{
d_ptr->m_RingWidth = width;
}
void PieChart::SetLegendVisible(bool visible)
{
if (d_ptr->m_bLegendVisible != visible)
{
d_ptr->m_bLegendVisible = visible;
ConstructData();
update();
}
}
void PieChart::AddData(double value, const QColor & color, const QString & label)
{
PieItem item;
item.value = qFabs(value);
item.color = color;
item.label = label;
AddData(item);
}
void PieChart::AddData(const PieItem & item)
{
d_ptr->m_SumValue += item.value;
PieItemPrivate pitem;
pitem.item = item;
d_ptr->m_Items.append(pitem);
ConstructData();
}
void PieChart::paintEvent(QPaintEvent * event)
{
if ( qFabs(d_ptr->m_SumValue) < 0.000001)
{
return;
}
QPainter painter(this);
//反走样
painter.setRenderHint(QPainter::Antialiasing, true);
int pos = 0;
int angle;
for (auto iter = d_ptr->m_Items.begin(); iter != d_ptr->m_Items.end(); ++iter)
{
painter.save();
painter.setBrush(iter->item.color);
painter.drawPath(iter->path);
painter.setPen(QPen(d_ptr->m_LabelColor, 1));
painter.drawText(iter->labelPos, QString("%1%").arg(iter->item.value / d_ptr->m_SumValue * 100));
//绘制legend
if (d_ptr->m_bLegendVisible)
{
painter.fillRect(iter->m_LegendRect, iter->item.color);
painter.drawText(iter->m_LegendRect, iter->item.label, QTextOption(Qt::AlignCenter));
QLine line(iter->labelPos, iter->m_LegendRect.center());
double angle = atan2(line.dy(), line.dx());
QLine line2(line.p1() + QPoint(d_ptr->m_RingWidth * qCos(angle), d_ptr->m_RingWidth * qSin(angle))
, line.p2() - QPoint(d_ptr->m_RingWidth * qCos(angle), d_ptr->m_RingWidth * qSin(angle)));
painter.drawLine(line2);
painter.translate(line2.p2());
painter.rotate(qRadiansToDegrees(angle) + 30);
painter.drawLine(QPoint(0, 0), QPoint(-10, 0));
painter.rotate(- 60);
painter.drawLine(QPoint(0, 0), QPoint(-10, 0));
}
pos += angle;
painter.restore();
}
painter.drawText(d_ptr->m_PieRect, d_ptr->m_RingLabel, QTextOption(Qt::AlignCenter));
}
void PieChart::resizeEvent(QResizeEvent * event)
{
ConstructRect(event->size());
ConstructData();
}
void PieChart::ConstructData()
{
int pos = d_ptr->m_StartRotationAngle;
int angle;
for (auto iter = d_ptr->m_Items.begin(); iter != d_ptr->m_Items.end(); ++iter)
{
angle = 16 * iter->item.value / d_ptr->m_SumValue * 360;
QPainterPath path;
path.moveTo(d_ptr->m_PieRect.center());
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);
path.closeSubpath();
QPainterPath subPath;
subPath.addEllipse(d_ptr->m_PieRect.adjusted(d_ptr->m_RingWidth, d_ptr->m_RingWidth, -d_ptr->m_RingWidth, -d_ptr->m_RingWidth));
path -= subPath;
iter->path = path;
double labelAngle = (pos + angle / 2) / 16;
double tx = (d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / 2 * qCos(labelAngle / 360 * 2 * 3.1415926);
double ty = -(d_ptr->m_PieRect.width() - d_ptr->m_RingWidth) / 2 * qSin(labelAngle / 360 * 2 * 3.1415926);
iter->labelPos = QPoint(tx, ty) + d_ptr->m_PieRect.center();
pos += angle;
}
}
void PieChart::ConstructRect(const QSize & size)
{
switch (d_ptr->m_Items.size())
{
case 4:
ConstructCornerLayout(size);
default:
break;
}
}
void PieChart::ConstructCornerLayout(const QSize & size)
{
int currentR = d_ptr->m_MinDiameter;
int diameter;
int horiWidth = size.width();
if (d_ptr->m_bLegendVisible)
{
horiWidth -= d_ptr->m_LegendWidth * 2;
}
if (horiWidth > size.height())
{
diameter = size.height();
}
else
{
diameter = horiWidth;
}
int x, y;
int r = diameter - d_ptr->m_Minx * 2;
if (d_ptr->m_bLegendVisible)
{
currentR = r > currentR ? r : currentR;
x = d_ptr->m_Minx + d_ptr->m_LegendWidth;
y = (size.height() - currentR) / 2;
d_ptr->m_Items[1].m_LegendRect = QRect(d_ptr->m_Minx, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
d_ptr->m_Items[0].m_LegendRect = QRect(x + r, d_ptr->m_Miny, d_ptr->m_LegendWidth, d_ptr->m_LegendHeight);
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);
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);
}
else
{
x = d_ptr->m_Minx;
y = d_ptr->m_Miny;
}
d_ptr->m_PieRect = QRect(x, y, currentR, currentR);
}
评论16