//创建甜甜圈细分图表
//让我们首先为图表定义一些数据。
QPieSeries *series1 = new QPieSerise();
series1->setName("Fossil fuels");
series1->append("Oil",353295);
series1->append("Coal",188500);
series1->append("Naturel gas",148680);
series1->append("Peat",94545);
QPieSeries *series2 = new QPieSeries();
series2->setName("Renewables");
series2->append("Wood fuels",319663);
series2->append("Hydro power",45875);
series2->append("Wind power",1060);
QPieSeries *series3 = new QPieSeries();
series3->setName(Others);
series3->append("Nuclear energy",238789);
series3->append("Import energy",37802);
series3->append("other",32441);
//然后我们创建一个图表,在其中添加数据。
//请注意,这是我们自己从 QChart 派生的图表。
DonutBreakdownChart *donutBreakdown = new DonutBreakdownChart();
donutBreakdown->setAnimationOptions(QChart::AllAnimations);
donutBreakdown->legend()->setAlignment(Qt::AlignRight);
donutBreakdown->addBreakdownSeries(series1,Qt::red);
donutBreakdown->addBreakdownSeries(series2,Qt::darkGreen);
donutBreakdown->addBreakdownSeries(series3,Qt::darkBlue);
//我们自己的图表以这样一种方式工作:
//我们在构造函数中创建一个主系列,
//我们创建一个主系列,它聚合了细分系列提供的数据。
//这是中间的饼图。
DonutBreakdownChart::DonutBreakdownChart(QGraphicsItem *parent,Qt::WindowFlags &Flags)
:QChart(QChart::ChartTypeCartesian,parent,wFlags)
{
//create the series for main center pie
m_mainSeries = new QPieSeries();
m_mainSeries->setPieSize(0.7);
QChart::addSeries(m_mainSeries);
}
//当添加细分系列时,数据用于在主系列中创建切片
//细分系列本身用于创建定位的甜甜圈段,
//使其与主系列中的相应切片对齐。
void DonutBreakdownChart::addBreakdownSeries(QPieSeries *breakdownSeries,QColor color)
{
QFont font("Arial",8);
//add breakdown series as a slice to enter pie
MainSlice *mainSlice = new MainSlice(breakdownSeries);
mainSlice->setName(breakdownSeries->name());
mainSlice->setValue(breakdownSeries->sum());
m_mainSeries->append(mainSlice);
//customize the slice
mainSlice->setBrush(color);
mainSlice->setLabelVisible();
mainSlice->setLabelColor(Qt::white);
mainSlice->setLabelPosition(QPieSlice::LabelInsideHorizontal);
mainSlice->setLabelFont(font);
//position and customize the breakdown series
breakdownSeries->setPieSize(0.8);
breakdownSeries->setHoleSize(0.7);
breakdownSeries->setLabelsVisible();
const auto slices = breakdownSeries->slices();
for(QPieSlice *slice : slices)
{
color = color.lighter(115);
slice->setBrush(color);
slice->setLabelFont(font);
}
//add the series to the chart
QChart::addSeries(breakdownSeries);
//recalculate breakdown donut segment
recalculateAngles();
//update customize legend markers
updateLegendMarkers();
}
//下面是如何计算甜甜圈段的开始和结束角度。
void DonutBreakdownChart::recalculateAngles()
{
float angle = 0;
const auto slices = m_mainSeries->slices();
for(QPieSlice *slice :: slices)
{
QPieSeries *breakdownSeries = qobeject_cast<MainSlice *>(slice)->breakdownSeries();
breakdownSeries->setPieStartAngle(angle);
angle += slice->percentage() * 360.0; //full pie is 360.0
breakdownSeries->setPieEndAngle(angle);
}
}
//自定义图例标记以显示细分百分比。 主关卡切片的标记被隐藏。
void DonutBreakdownChart::updateLegendMarkers()
{
//go through all markers
const auto aliseries = series();
for(QAbstractSeries *series : allseries)
{
const auto markers = legend()->marker(series);
for(QLegendMarker *maker : makers)
{
QPieLegendMarker *pieMarker = qobject_cast<QPieLegendMarker*>(marker);
if(series == m_mainSeries)
{
//hide markers from main series
pieMarker->setVisible(false);
}
else
{
//modify markers from breakdown series
pieMarker->setLabel(QString("%1 %2")
.arg(pieMarker->slice()->label())
.arg(pieMarker->slice()->percentage() * 100,0,'f',2));
pieMarker->setFont(QFont("Arial",8));
}
}
}
}
//相反,主要级别切片在标签上显示百分比。
MainSlice::MainSlice(QPieSeries *breakdownSeries,QObject *parent)
:QPieSlice(parent)
,m_breakdownSeries(breakdownSeries)
{
connect(this,&MainSlice::percentageChanged,this,&MainSlice::updateLabel);
}
void MainSlice::updateLabel()
{
this->setLabel(QString("%1 %2").arg(m_name).arg(percentage() * 100,0,'f',2));
}
//现在我们已经定义了图表,我们最终可以创建一个 QChartView 并显示图表。
QMainWindow window;
QChartView *chartView = new QChartView(donutBreakdown);
charView->setRenderHint(QPainter::Antialiasing);
window.setCentralWidget(chartView);
window.resize(800,500);
window.show();
main.cpp
#include
#include
#include
#include
#include "donutbreakdownchart.h"
QT_USE_NAMESPACE
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
//![1]
// Graph is based on data of 'Total consumption of energy increased by 10 per cent in 2010'
// Statistics Finland, 13 December 2011
// http://www.stat.fi/til/ekul/2010/ekul_2010_2011-12-13_tie_001_en.html
QPieSeries *series1 = new QPieSeries();
series1->setName("Fossil fuels");
series1->append("Oil", 353295);
series1->append("Coal", 188500);
series1->append("Natural gas", 148680);
series1->append("Peat", 94545);
QPieSeries *series2 = new QPieSeries();
series2->setName("Renewables");
series2->append("Wood fuels", 319663);
series2->append("Hydro power", 45875);
series2->append("Wind power", 1060);
QPieSeries *series3 = new QPieSeries();
series3->setName("Others");
series3->append("Nuclear energy", 238789);
series3->append("Import energy", 37802);
series3->append("Other", 32441);
//![1]
//![2]
DonutBreakdownChart *donutBreakdown = new DonutBreakdownChart();
donutBreakdown->setAnimationOptions(QChart::AllAnimations);
donutBreakdown->setTitle("Total consumption of energy in Finland 2010");
donutBreakdown->legend()->setAlignment(Qt::AlignRight);
donutBreakdown->addBreakdownSeries(series1, Qt::red);
donutBreakdown->addBreakdownSeries(series2, Qt::darkGreen);
donutBreakdown->addBreakdownSeries(series3, Qt::darkBlue);
//![2]
//![3]
QMainWindow window;
QChartView *chartView = new QChartView(donutBreakdown);
chartView->setRenderHint(QPainter::Antialiasing);
window.setCentralWidget(chartView);
window.resize(800, 500);
window.show();
//![3]
return a.exec();
}
mainslice.h
#ifndef MAINSLICE_H
#define MAINSLICE_H
#include
#include
QT_USE_NAMESPACE
//饼图有一个值和一个标签。
//当切片添加到饼图系列时,
//QPieSeries 对象计算切片占系列中所有切片总和的百分比
//以确定图表中切片的实际大小。
//饼图切片有一个值和一个标签 .
//当切片添加到饼图系列时
//QPieSeries 对象计算切片与系列中所有切片的总和相比的百分比,
//以确定图表中切片的实际大小。
//默认情况下,标签是隐藏的。
//如果它是可见的,它可以位于切片外部并通过臂连接到它
//或者水平或平行于切片弧的切线或法线在切片内部居中。
//默认情况下,切片的视觉外观由主题设置,
//但可以通过指定切片属性来覆盖主题。
//但是,如果在自定义切片后更改主题,则所有自定义都将丢失。
//为了使用户能够与饼图进行交互,
//当用户单击饼图或将鼠标悬停在饼图上时
//会发出一些基本信号。
class MainSlice : public QPieSlice
{
Q_OBJECT
public:
MainSlice(QPieSeries *breakdownSeries, QObject *parent = 0);
//饼图系列由定义为 QPieSlice 对象的切片组成。
//切片可以有任何值
//因为 QPieSeries 对象计算切片与系列中所有切片总和的百分比
//以确定图表中切片的实际大小
//图表上的饼图大小和位置通过使用范围从 0.0 到 1.0
//的相对值进行控制。
//这些与实际的图表矩形有关。
//默认情况下,饼图定义为完整饼图。
//可以通过设置系列的起始角度和角度跨度来创建部分饼图
//一个完整的馅饼是 360 度,其中 0 是 12 点钟。
QPieSeries *breakdownSeries() const;
void setName(QString name);
QString name() const;
public Q_SLOTS:
void updateLabel();
private:
QPieSeries *m_breakdownSeries;
QString m_name;
};
#endif // MAINSLICE_H
mainslice.cpp
#include "mainslice.h"
QT_USE_NAMESPACE
//![1]
MainSlice::MainSlice(QPieSeries *breakdownSeries, QObject *parent)
: QPieSlice(parent),
m_breakdownSeries(breakdownSeries)
{
connect(this, &MainSlice::percentageChanged, this, &MainSlice::updateLabel);
}
//![1]
QPieSeries *MainSlice::breakdownSeries() const
{
return m_breakdownSeries;
}
void MainSlice::setName(QString name)
{
m_name = name;
}
QString MainSlice::name() const
{
return m_name;
}
//![2]
void MainSlice::updateLabel()
{
this->setLabel(QString("%1 %2%").arg(m_name).arg(percentage() * 100, 0, 'f', 2));
}
//![2]
#include "moc_mainslice.cpp"
donutbreakdownchart.h
#ifndef DONUTBREAKDOWNCHART_H
#define DONUTBREAKDOWNCHART_H
#include
#include
QT_USE_NAMESPACE
class DonutBreakdownChart : public QChart
{
public:
DonutBreakdownChart(QGraphicsItem *parent = nullptr, Qt::WindowFlags wFlags = {});
void addBreakdownSeries(QPieSeries *series, QColor color);
private:
void recalculateAngles();
void updateLegendMarkers();
private:
QPieSeries *m_mainSeries;
};
#endif // DONUTBREAKDOWNCHART_H
donutbreakdownchart.cpp
#include "donutbreakdownchart.h"
#include "mainslice.h"
#include
#include
QT_USE_NAMESPACE
//它为编写您自己的自定义项目提供了一个轻量级的基础。
//这包括通过其事件处理程序定义项目的几何形状、碰撞检测、其绘制实现和项目交互
//QGraphicsItem 是图形视图框架的一部分
//项目的所有几何信息都基于其局部坐标系
//项目的位置 pos() 是唯一不在本地坐标中操作的函数
//因为它返回父坐标中的位置
//图形视图坐标系详细描述了坐标系。
//您可以通过调用 setVisible() 设置项目是否应该可见(即,绘制和接受事件)。
//隐藏项目也会隐藏其子项。
//同样,您可以通过调用 setEnabled() 启用或禁用项目。
//如果您禁用一个项目,它的所有子项也将被禁用。
//。 默认情况下,项目既可见又启用。
//要切换项目是否被选中,首先通过设置 ItemIsSelectable 标志启用选择
//然后调用 setSelected()。
//通常,作为用户交互的结果,选择由场景切换。
//要编写自己的图形项,首先要创建 QGraphicsItem 的子类
//然后从实现它的两个纯虚公共函数开始
//boundingRect(),它返回项所绘制区域的估计值
//和paint(),它实现 实际的绘画。 例如:
/*
class SimpleItem : public QGraphicsItem
{
public:
QRectF boundingRect() const override
{
qreal penWidth = 1;
return QRectF(-10-penWidth / 2, -10 - penWidth / 2,
20 + penWidth, 20 + penWidth);
}
void paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
QWidget *widget) override
{
painter->drawRoundedRect(-10,-10,20,20,5,5);
}
};
*/
//![1]
//ChartTypeCartesian 笛卡尔图。
DonutBreakdownChart::DonutBreakdownChart(QGraphicsItem *parent, Qt::WindowFlags wFlags)
: QChart(QChart::ChartTypeCartesian, parent, wFlags)
{
// create the series for main center pie
m_mainSeries = new QPieSeries();
m_mainSeries->setPieSize(0.7);
QChart::addSeries(m_mainSeries);
}
//![1]
//![2]
void DonutBreakdownChart::addBreakdownSeries(QPieSeries *breakdownSeries, QColor color)
{
QFont font("Arial", 8);
// add breakdown series as a slice to center pie
MainSlice *mainSlice = new MainSlice(breakdownSeries);
mainSlice->setName(breakdownSeries->name());
mainSlice->setValue(breakdownSeries->sum());
m_mainSeries->append(mainSlice);
// customize the slice
mainSlice->setBrush(color);
mainSlice->setLabelVisible();
mainSlice->setLabelColor(Qt::white);
mainSlice->setLabelPosition(QPieSlice::LabelInsideHorizontal);
mainSlice->setLabelFont(font);
// position and customize the breakdown series
breakdownSeries->setPieSize(0.8);
breakdownSeries->setHoleSize(0.7);
breakdownSeries->setLabelsVisible();
const auto slices = breakdownSeries->slices();
for (QPieSlice *slice : slices) {
color = color.lighter(115);
slice->setBrush(color);
slice->setLabelFont(font);
}
// add the series to the chart
QChart::addSeries(breakdownSeries);
// recalculate breakdown donut segments
recalculateAngles();
// update customize legend markers
updateLegendMarkers();
}
//![2]
//![3]
void DonutBreakdownChart::recalculateAngles()
{
qreal angle = 0;
const auto slices = m_mainSeries->slices();
for (QPieSlice *slice : slices) {
QPieSeries *breakdownSeries = qobject_cast<MainSlice *>(slice)->breakdownSeries();
breakdownSeries->setPieStartAngle(angle);
angle += slice->percentage() * 360.0; // full pie is 360.0
breakdownSeries->setPieEndAngle(angle);
}
}
//![3]
//![4]
void DonutBreakdownChart::updateLegendMarkers()
{
// go through all markers
const auto allseries = series();
for (QAbstractSeries *series : allseries) {
const auto markers = legend()->markers(series);
for (QLegendMarker *marker : markers) {
QPieLegendMarker *pieMarker = qobject_cast<QPieLegendMarker *>(marker);
if (series == m_mainSeries) {
// hide markers from main series
pieMarker->setVisible(false);
} else {
// modify markers from breakdown series
pieMarker->setLabel(QString("%1 %2%")
.arg(pieMarker->slice()->label())
.arg(pieMarker->slice()->percentage() * 100, 0, 'f', 2));
pieMarker->setFont(QFont("Arial", 8));
}
}
}
}
//![4]