2022-08-25 18:37:24 +08:00
|
|
|
/****************************************************************************
|
|
|
|
** Copyright (c) 2013-2014 Debao Zhang <hello@debao.me>
|
|
|
|
** All right reserved.
|
|
|
|
**
|
|
|
|
** Permission is hereby granted, free of charge, to any person obtaining
|
|
|
|
** a copy of this software and associated documentation files (the
|
|
|
|
** "Software"), to deal in the Software without restriction, including
|
|
|
|
** without limitation the rights to use, copy, modify, merge, publish,
|
|
|
|
** distribute, sublicense, and/or sell copies of the Software, and to
|
|
|
|
** permit persons to whom the Software is furnished to do so, subject to
|
|
|
|
** the following conditions:
|
|
|
|
**
|
|
|
|
** The above copyright notice and this permission notice shall be
|
|
|
|
** included in all copies or substantial portions of the Software.
|
|
|
|
**
|
|
|
|
** THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
|
|
** EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
|
|
** MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
|
|
** NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
|
|
** LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
|
|
** OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
|
|
** WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
**
|
|
|
|
****************************************************************************/
|
|
|
|
|
|
|
|
#include "xlsxchart_p.h"
|
|
|
|
#include "xlsxworksheet.h"
|
|
|
|
#include "xlsxcellrange.h"
|
|
|
|
#include "xlsxutility_p.h"
|
|
|
|
|
|
|
|
#include <QIODevice>
|
|
|
|
#include <QXmlStreamReader>
|
|
|
|
#include <QXmlStreamWriter>
|
|
|
|
#include <QDebug>
|
|
|
|
|
|
|
|
QT_BEGIN_NAMESPACE_XLSX
|
|
|
|
|
|
|
|
ChartPrivate::ChartPrivate(Chart *q, Chart::CreateFlag flag)
|
|
|
|
: AbstractOOXmlFilePrivate(q, flag)
|
|
|
|
, chartType(static_cast<Chart::ChartType>(0))
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
ChartPrivate::~ChartPrivate()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \class Chart
|
|
|
|
* \inmodule QtXlsx
|
|
|
|
* \brief Main class for the charts.
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
\enum Chart::ChartType
|
|
|
|
|
|
|
|
\value CT_Area
|
|
|
|
\value CT_Area3D,
|
|
|
|
\value CT_Line,
|
|
|
|
\value CT_Line3D,
|
|
|
|
\value CT_Scatter,
|
|
|
|
\value CT_Pie,
|
|
|
|
\value CT_Pie3D,
|
|
|
|
\value CT_Doughnut,
|
|
|
|
\value CT_Bar,
|
|
|
|
\value CT_Bar3D,
|
|
|
|
|
|
|
|
\omitvalue CT_Stock,
|
|
|
|
\omitvalue CT_Radar,
|
|
|
|
\omitvalue CT_OfPie,
|
|
|
|
\omitvalue CT_Surface,
|
|
|
|
\omitvalue CT_Surface3D,
|
|
|
|
\omitvalue CT_Bubble
|
|
|
|
*/
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
*/
|
|
|
|
Chart::Chart(AbstractSheet *parent, CreateFlag flag)
|
|
|
|
: AbstractOOXmlFile(new ChartPrivate(this, flag))
|
|
|
|
{
|
|
|
|
d_func()->sheet = parent;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Destroys the chart.
|
|
|
|
*/
|
|
|
|
Chart::~Chart()
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Add the data series which is in the range \a range of the \a sheet.
|
|
|
|
*/
|
|
|
|
void Chart::addSeries(const CellRange &range, AbstractSheet *sheet)
|
|
|
|
{
|
|
|
|
Q_D(Chart);
|
|
|
|
if (!range.isValid())
|
|
|
|
return;
|
|
|
|
if (sheet && sheet->sheetType() != AbstractSheet::ST_WorkSheet)
|
|
|
|
return;
|
|
|
|
if (!sheet && d->sheet->sheetType() != AbstractSheet::ST_WorkSheet)
|
|
|
|
return;
|
|
|
|
|
|
|
|
QString sheetName = sheet ? sheet->sheetName() : d->sheet->sheetName();
|
|
|
|
// In case sheetName contains space or '
|
|
|
|
sheetName = escapeSheetName(sheetName);
|
|
|
|
|
|
|
|
if (range.columnCount() == 1 || range.rowCount() == 1) {
|
|
|
|
QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
|
|
|
|
series->numberDataSource_numRef =
|
|
|
|
sheetName + QLatin1String("!") + range.toString(true, true);
|
|
|
|
d->seriesList.append(series);
|
|
|
|
} else if (range.columnCount() < range.rowCount()) {
|
|
|
|
// Column based series
|
|
|
|
int firstDataColumn = range.firstColumn();
|
|
|
|
QString axDataSouruce_numRef;
|
|
|
|
if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) {
|
|
|
|
firstDataColumn += 1;
|
|
|
|
CellRange subRange(range.firstRow(), range.firstColumn(), range.lastRow(),
|
|
|
|
range.firstColumn());
|
|
|
|
axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int col = firstDataColumn; col <= range.lastColumn(); ++col) {
|
|
|
|
CellRange subRange(range.firstRow(), col, range.lastRow(), col);
|
|
|
|
QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
|
|
|
|
series->axDataSource_numRef = axDataSouruce_numRef;
|
|
|
|
series->numberDataSource_numRef =
|
|
|
|
sheetName + QLatin1String("!") + subRange.toString(true, true);
|
|
|
|
d->seriesList.append(series);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else {
|
|
|
|
// Row based series
|
|
|
|
int firstDataRow = range.firstRow();
|
|
|
|
QString axDataSouruce_numRef;
|
|
|
|
if (d->chartType == CT_Scatter || d->chartType == CT_Bubble) {
|
|
|
|
firstDataRow += 1;
|
|
|
|
CellRange subRange(range.firstRow(), range.firstColumn(), range.firstRow(),
|
|
|
|
range.lastColumn());
|
|
|
|
axDataSouruce_numRef = sheetName + QLatin1String("!") + subRange.toString(true, true);
|
|
|
|
}
|
|
|
|
|
|
|
|
for (int row = firstDataRow; row <= range.lastRow(); ++row) {
|
|
|
|
CellRange subRange(row, range.firstColumn(), row, range.lastColumn());
|
|
|
|
QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
|
|
|
|
series->axDataSource_numRef = axDataSouruce_numRef;
|
|
|
|
series->numberDataSource_numRef =
|
|
|
|
sheetName + QLatin1String("!") + subRange.toString(true, true);
|
|
|
|
d->seriesList.append(series);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* Set the type of the chart to \a type
|
|
|
|
*/
|
|
|
|
void Chart::setChartType(ChartType type)
|
|
|
|
{
|
|
|
|
Q_D(Chart);
|
|
|
|
d->chartType = type;
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
void Chart::setChartStyle(int id)
|
|
|
|
{
|
|
|
|
Q_UNUSED(id)
|
|
|
|
//! Todo
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
*/
|
|
|
|
void Chart::saveToXmlFile(QIODevice *device) const
|
|
|
|
{
|
|
|
|
Q_D(const Chart);
|
|
|
|
|
|
|
|
QXmlStreamWriter writer(device);
|
|
|
|
|
|
|
|
writer.writeStartDocument(QStringLiteral("1.0"), true);
|
|
|
|
writer.writeStartElement(QStringLiteral("c:chartSpace"));
|
|
|
|
writer.writeAttribute(QStringLiteral("xmlns:c"),
|
|
|
|
QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/chart"));
|
|
|
|
writer.writeAttribute(QStringLiteral("xmlns:a"),
|
|
|
|
QStringLiteral("http://schemas.openxmlformats.org/drawingml/2006/main"));
|
|
|
|
writer.writeAttribute(
|
|
|
|
QStringLiteral("xmlns:r"),
|
|
|
|
QStringLiteral("http://schemas.openxmlformats.org/officeDocument/2006/relationships"));
|
|
|
|
|
|
|
|
d->saveXmlChart(writer);
|
|
|
|
|
|
|
|
writer.writeEndElement(); // c:chartSpace
|
|
|
|
writer.writeEndDocument();
|
|
|
|
}
|
|
|
|
|
|
|
|
/*!
|
|
|
|
* \internal
|
|
|
|
*/
|
|
|
|
bool Chart::loadFromXmlFile(QIODevice *device)
|
|
|
|
{
|
|
|
|
Q_D(Chart);
|
|
|
|
|
|
|
|
QXmlStreamReader reader(device);
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
reader.readNextStartElement();
|
|
|
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
|
|
|
if (reader.name() == QLatin1String("chart")) {
|
|
|
|
if (!d->loadXmlChart(reader))
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChartPrivate::loadXmlChart(QXmlStreamReader &reader)
|
|
|
|
{
|
|
|
|
Q_ASSERT(reader.name() == QLatin1String("chart"));
|
|
|
|
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
reader.readNextStartElement();
|
|
|
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
|
|
|
if (reader.name() == QLatin1String("plotArea")) {
|
|
|
|
if (!loadXmlPlotArea(reader))
|
|
|
|
return false;
|
|
|
|
} else if (reader.name() == QLatin1String("legend")) {
|
|
|
|
//! Todo
|
|
|
|
}
|
|
|
|
} else if (reader.tokenType() == QXmlStreamReader::EndElement
|
|
|
|
&& reader.name() == QLatin1String("chart")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChartPrivate::loadXmlPlotArea(QXmlStreamReader &reader)
|
|
|
|
{
|
|
|
|
Q_ASSERT(reader.name() == QLatin1String("plotArea"));
|
|
|
|
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
reader.readNextStartElement();
|
|
|
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
|
|
|
if (reader.name() == QLatin1String("layout")) {
|
|
|
|
//! ToDo
|
|
|
|
} else if (reader.name().endsWith(QLatin1String("Chart"))) {
|
|
|
|
// For pieChart, barChart, ...
|
|
|
|
loadXmlXxxChart(reader);
|
|
|
|
} else if (reader.name().endsWith(QLatin1String("Ax"))) {
|
|
|
|
// For valAx, catAx, serAx, dateAx
|
|
|
|
loadXmlAxis(reader);
|
|
|
|
}
|
|
|
|
|
|
|
|
} else if (reader.tokenType() == QXmlStreamReader::EndElement
|
|
|
|
&& reader.name() == QLatin1String("plotArea")) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChartPrivate::loadXmlXxxChart(QXmlStreamReader &reader)
|
|
|
|
{
|
2023-10-20 18:43:30 +08:00
|
|
|
auto name = reader.name();
|
2022-08-25 18:37:24 +08:00
|
|
|
if (name == QLatin1String("pieChart"))
|
|
|
|
chartType = Chart::CT_Pie;
|
|
|
|
else if (name == QLatin1String("pie3DChart"))
|
|
|
|
chartType = Chart::CT_Pie3D;
|
|
|
|
else if (name == QLatin1String("barChart"))
|
|
|
|
chartType = Chart::CT_Bar;
|
|
|
|
else if (name == QLatin1String("bar3DChart"))
|
|
|
|
chartType = Chart::CT_Bar3D;
|
|
|
|
else if (name == QLatin1String("lineChart"))
|
|
|
|
chartType = Chart::CT_Line;
|
|
|
|
else if (name == QLatin1String("line3DChart"))
|
|
|
|
chartType = Chart::CT_Line3D;
|
|
|
|
else if (name == QLatin1String("scatterChart"))
|
|
|
|
chartType = Chart::CT_Scatter;
|
|
|
|
else if (name == QLatin1String("areaChart"))
|
|
|
|
chartType = Chart::CT_Area;
|
|
|
|
else if (name == QLatin1String("area3DChart"))
|
|
|
|
chartType = Chart::CT_Area3D;
|
|
|
|
else if (name == QLatin1String("doughnutChart"))
|
|
|
|
chartType = Chart::CT_Doughnut;
|
|
|
|
else
|
|
|
|
qDebug() << "Cann't load chart: " << name;
|
|
|
|
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
reader.readNextStartElement();
|
|
|
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
|
|
|
if (reader.name() == QLatin1String("ser")) {
|
|
|
|
loadXmlSer(reader);
|
|
|
|
} else if (reader.name() == QLatin1String("axId")) {
|
|
|
|
}
|
|
|
|
} else if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == name) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChartPrivate::loadXmlSer(QXmlStreamReader &reader)
|
|
|
|
{
|
|
|
|
Q_ASSERT(reader.name() == QLatin1String("ser"));
|
|
|
|
|
|
|
|
QSharedPointer<XlsxSeries> series = QSharedPointer<XlsxSeries>(new XlsxSeries);
|
|
|
|
seriesList.append(series);
|
|
|
|
|
|
|
|
while (!reader.atEnd()
|
|
|
|
&& !(reader.tokenType() == QXmlStreamReader::EndElement
|
|
|
|
&& reader.name() == QLatin1String("ser"))) {
|
|
|
|
if (reader.readNextStartElement()) {
|
2023-10-20 18:43:30 +08:00
|
|
|
auto name = reader.name();
|
2022-08-25 18:37:24 +08:00
|
|
|
if (name == QLatin1String("cat") || name == QLatin1String("xVal")) {
|
|
|
|
while (!reader.atEnd()
|
|
|
|
&& !(reader.tokenType() == QXmlStreamReader::EndElement
|
|
|
|
&& reader.name() == name)) {
|
|
|
|
if (reader.readNextStartElement()) {
|
|
|
|
if (reader.name() == QLatin1String("numRef"))
|
|
|
|
series->axDataSource_numRef = loadXmlNumRef(reader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (name == QLatin1String("val") || name == QLatin1String("yVal")) {
|
|
|
|
while (!reader.atEnd()
|
|
|
|
&& !(reader.tokenType() == QXmlStreamReader::EndElement
|
|
|
|
&& reader.name() == name)) {
|
|
|
|
if (reader.readNextStartElement()) {
|
|
|
|
if (reader.name() == QLatin1String("numRef"))
|
|
|
|
series->numberDataSource_numRef = loadXmlNumRef(reader);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
} else if (name == QLatin1String("extLst")) {
|
|
|
|
while (!reader.atEnd()
|
|
|
|
&& !(reader.tokenType() == QXmlStreamReader::EndElement
|
|
|
|
&& reader.name() == name)) {
|
|
|
|
reader.readNextStartElement();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString ChartPrivate::loadXmlNumRef(QXmlStreamReader &reader)
|
|
|
|
{
|
|
|
|
Q_ASSERT(reader.name() == QLatin1String("numRef"));
|
|
|
|
|
|
|
|
while (!reader.atEnd()
|
|
|
|
&& !(reader.tokenType() == QXmlStreamReader::EndElement
|
|
|
|
&& reader.name() == QLatin1String("numRef"))) {
|
|
|
|
if (reader.readNextStartElement()) {
|
|
|
|
if (reader.name() == QLatin1String("f"))
|
|
|
|
return reader.readElementText();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return QString();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlChart(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
writer.writeStartElement(QStringLiteral("c:chart"));
|
|
|
|
writer.writeStartElement(QStringLiteral("c:plotArea"));
|
|
|
|
switch (chartType) {
|
|
|
|
case Chart::CT_Pie:
|
|
|
|
case Chart::CT_Pie3D:
|
|
|
|
saveXmlPieChart(writer);
|
|
|
|
break;
|
|
|
|
case Chart::CT_Bar:
|
|
|
|
case Chart::CT_Bar3D:
|
|
|
|
saveXmlBarChart(writer);
|
|
|
|
break;
|
|
|
|
case Chart::CT_Line:
|
|
|
|
case Chart::CT_Line3D:
|
|
|
|
saveXmlLineChart(writer);
|
|
|
|
break;
|
|
|
|
case Chart::CT_Scatter:
|
|
|
|
saveXmlScatterChart(writer);
|
|
|
|
break;
|
|
|
|
case Chart::CT_Area:
|
|
|
|
case Chart::CT_Area3D:
|
|
|
|
saveXmlAreaChart(writer);
|
|
|
|
break;
|
|
|
|
case Chart::CT_Doughnut:
|
|
|
|
saveXmlDoughnutChart(writer);
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
saveXmlAxes(writer);
|
|
|
|
writer.writeEndElement(); // plotArea
|
|
|
|
|
|
|
|
// saveXmlLegend(writer);
|
|
|
|
|
|
|
|
writer.writeEndElement(); // chart
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlPieChart(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
QString name =
|
|
|
|
chartType == Chart::CT_Pie ? QStringLiteral("c:pieChart") : QStringLiteral("c:pie3DChart");
|
|
|
|
|
|
|
|
writer.writeStartElement(name);
|
|
|
|
|
|
|
|
// Do the same behavior as Excel, Pie prefer varyColors
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:varyColors"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
|
|
|
|
|
|
|
|
for (int i = 0; i < seriesList.size(); ++i)
|
|
|
|
saveXmlSer(writer, seriesList[i].data(), i);
|
|
|
|
|
|
|
|
writer.writeEndElement(); // pieChart, pie3DChart
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlBarChart(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
QString name =
|
|
|
|
chartType == Chart::CT_Bar ? QStringLiteral("c:barChart") : QStringLiteral("c:bar3DChart");
|
|
|
|
|
|
|
|
writer.writeStartElement(name);
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:barDir"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("col"));
|
|
|
|
|
|
|
|
for (int i = 0; i < seriesList.size(); ++i)
|
|
|
|
saveXmlSer(writer, seriesList[i].data(), i);
|
|
|
|
|
|
|
|
if (axisList.isEmpty()) {
|
|
|
|
// The order the axes??
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: Bar3D have 2~3 axes
|
|
|
|
Q_ASSERT(axisList.size() == 2 || (axisList.size() == 3 && chartType == Chart::CT_Bar3D));
|
|
|
|
|
|
|
|
for (int i = 0; i < axisList.size(); ++i) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:axId"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEndElement(); // barChart, bar3DChart
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlLineChart(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
QString name = chartType == Chart::CT_Line ? QStringLiteral("c:lineChart")
|
|
|
|
: QStringLiteral("c:line3DChart");
|
|
|
|
|
|
|
|
writer.writeStartElement(name);
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("grouping"));
|
|
|
|
|
|
|
|
for (int i = 0; i < seriesList.size(); ++i)
|
|
|
|
saveXmlSer(writer, seriesList[i].data(), i);
|
|
|
|
|
|
|
|
if (axisList.isEmpty()) {
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
|
|
|
|
if (chartType == Chart::CT_Line3D)
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Ser, XlsxAxis::Bottom, 2, 0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT((axisList.size() == 2 || chartType == Chart::CT_Line)
|
|
|
|
|| (axisList.size() == 3 && chartType == Chart::CT_Line3D));
|
|
|
|
|
|
|
|
for (int i = 0; i < axisList.size(); ++i) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:axId"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEndElement(); // lineChart, line3DChart
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlScatterChart(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
const QString name = QStringLiteral("c:scatterChart");
|
|
|
|
|
|
|
|
writer.writeStartElement(name);
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:scatterStyle"));
|
|
|
|
|
|
|
|
for (int i = 0; i < seriesList.size(); ++i)
|
|
|
|
saveXmlSer(writer, seriesList[i].data(), i);
|
|
|
|
|
|
|
|
if (axisList.isEmpty()) {
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Bottom, 0, 1)));
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
Q_ASSERT(axisList.size() == 2);
|
|
|
|
|
|
|
|
for (int i = 0; i < axisList.size(); ++i) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:axId"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEndElement(); // c:scatterChart
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlAreaChart(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
QString name = chartType == Chart::CT_Area ? QStringLiteral("c:areaChart")
|
|
|
|
: QStringLiteral("c:area3DChart");
|
|
|
|
|
|
|
|
writer.writeStartElement(name);
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("grouping"));
|
|
|
|
|
|
|
|
for (int i = 0; i < seriesList.size(); ++i)
|
|
|
|
saveXmlSer(writer, seriesList[i].data(), i);
|
|
|
|
|
|
|
|
if (axisList.isEmpty()) {
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Cat, XlsxAxis::Bottom, 0, 1)));
|
|
|
|
const_cast<ChartPrivate *>(this)->axisList.append(
|
|
|
|
QSharedPointer<XlsxAxis>(new XlsxAxis(XlsxAxis::T_Val, XlsxAxis::Left, 1, 0)));
|
|
|
|
}
|
|
|
|
|
|
|
|
// Note: Area3D have 2~3 axes
|
|
|
|
Q_ASSERT(axisList.size() == 2 || (axisList.size() == 3 && chartType == Chart::CT_Area3D));
|
|
|
|
|
|
|
|
for (int i = 0; i < axisList.size(); ++i) {
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:axId"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(axisList[i]->axisId));
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEndElement(); // lineChart, line3DChart
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlDoughnutChart(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
QString name = QStringLiteral("c:doughnutChart");
|
|
|
|
|
|
|
|
writer.writeStartElement(name);
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:varyColors"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("1"));
|
|
|
|
|
|
|
|
for (int i = 0; i < seriesList.size(); ++i)
|
|
|
|
saveXmlSer(writer, seriesList[i].data(), i);
|
|
|
|
|
|
|
|
writer.writeStartElement(QStringLiteral("c:holeSize"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(50));
|
|
|
|
|
|
|
|
writer.writeEndElement();
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlSer(QXmlStreamWriter &writer, XlsxSeries *ser, int id) const
|
|
|
|
{
|
|
|
|
writer.writeStartElement(QStringLiteral("c:ser"));
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:idx"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(id));
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:order"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(id));
|
|
|
|
|
|
|
|
if (!ser->axDataSource_numRef.isEmpty()) {
|
|
|
|
if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble)
|
|
|
|
writer.writeStartElement(QStringLiteral("c:xVal"));
|
|
|
|
else
|
|
|
|
writer.writeStartElement(QStringLiteral("c:cat"));
|
|
|
|
writer.writeStartElement(QStringLiteral("c:numRef"));
|
|
|
|
writer.writeTextElement(QStringLiteral("c:f"), ser->axDataSource_numRef);
|
|
|
|
writer.writeEndElement(); // c:numRef
|
|
|
|
writer.writeEndElement(); // c:cat or c:xVal
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!ser->numberDataSource_numRef.isEmpty()) {
|
|
|
|
if (chartType == Chart::CT_Scatter || chartType == Chart::CT_Bubble)
|
|
|
|
writer.writeStartElement(QStringLiteral("c:yVal"));
|
|
|
|
else
|
|
|
|
writer.writeStartElement(QStringLiteral("c:val"));
|
|
|
|
writer.writeStartElement(QStringLiteral("c:numRef"));
|
|
|
|
writer.writeTextElement(QStringLiteral("c:f"), ser->numberDataSource_numRef);
|
|
|
|
writer.writeEndElement(); // c:numRef
|
|
|
|
writer.writeEndElement(); // c:val or c:yVal
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeEndElement(); // c:ser
|
|
|
|
}
|
|
|
|
|
|
|
|
bool ChartPrivate::loadXmlAxis(QXmlStreamReader &reader)
|
|
|
|
{
|
|
|
|
Q_ASSERT(reader.name().endsWith(QLatin1String("Ax")));
|
|
|
|
QString name = reader.name().toString();
|
|
|
|
|
|
|
|
XlsxAxis *axis = new XlsxAxis;
|
|
|
|
if (name == QLatin1String("valAx"))
|
|
|
|
axis->type = XlsxAxis::T_Val;
|
|
|
|
else if (name == QLatin1String("catAx"))
|
|
|
|
axis->type = XlsxAxis::T_Cat;
|
|
|
|
else if (name == QLatin1String("serAx"))
|
|
|
|
axis->type = XlsxAxis::T_Ser;
|
|
|
|
else
|
|
|
|
axis->type = XlsxAxis::T_Date;
|
|
|
|
|
|
|
|
axisList.append(QSharedPointer<XlsxAxis>(axis));
|
|
|
|
|
|
|
|
while (!reader.atEnd()) {
|
|
|
|
reader.readNextStartElement();
|
|
|
|
if (reader.tokenType() == QXmlStreamReader::StartElement) {
|
|
|
|
if (reader.name() == QLatin1String("axPos")) {
|
|
|
|
QXmlStreamAttributes attrs = reader.attributes();
|
2023-10-20 18:43:30 +08:00
|
|
|
auto pos = attrs.value(QLatin1String("val"));
|
2022-08-25 18:37:24 +08:00
|
|
|
if (pos == QLatin1String("l"))
|
|
|
|
axis->axisPos = XlsxAxis::Left;
|
|
|
|
else if (pos == QLatin1String("r"))
|
|
|
|
axis->axisPos = XlsxAxis::Right;
|
|
|
|
else if (pos == QLatin1String("b"))
|
|
|
|
axis->axisPos = XlsxAxis::Bottom;
|
|
|
|
else
|
|
|
|
axis->axisPos = XlsxAxis::Top;
|
|
|
|
} else if (reader.name() == QLatin1String("axId")) {
|
|
|
|
axis->axisId = reader.attributes().value(QLatin1String("val")).toString().toInt();
|
|
|
|
} else if (reader.name() == QLatin1String("crossAx")) {
|
|
|
|
axis->crossAx = reader.attributes().value(QLatin1String("val")).toString().toInt();
|
|
|
|
}
|
|
|
|
} else if (reader.tokenType() == QXmlStreamReader::EndElement && reader.name() == name) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void ChartPrivate::saveXmlAxes(QXmlStreamWriter &writer) const
|
|
|
|
{
|
|
|
|
for (int i = 0; i < axisList.size(); ++i) {
|
|
|
|
XlsxAxis *axis = axisList[i].data();
|
|
|
|
QString name;
|
|
|
|
switch (axis->type) {
|
|
|
|
case XlsxAxis::T_Cat:
|
|
|
|
name = QStringLiteral("c:catAx");
|
|
|
|
break;
|
|
|
|
case XlsxAxis::T_Val:
|
|
|
|
name = QStringLiteral("c:valAx");
|
|
|
|
break;
|
|
|
|
case XlsxAxis::T_Ser:
|
|
|
|
name = QStringLiteral("c:serAx");
|
|
|
|
break;
|
|
|
|
case XlsxAxis::T_Date:
|
|
|
|
name = QStringLiteral("c:dateAx");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
QString pos;
|
|
|
|
switch (axis->axisPos) {
|
|
|
|
case XlsxAxis::Top:
|
|
|
|
pos = QStringLiteral("t");
|
|
|
|
break;
|
|
|
|
case XlsxAxis::Bottom:
|
|
|
|
pos = QStringLiteral("b");
|
|
|
|
break;
|
|
|
|
case XlsxAxis::Left:
|
|
|
|
pos = QStringLiteral("l");
|
|
|
|
break;
|
|
|
|
case XlsxAxis::Right:
|
|
|
|
pos = QStringLiteral("r");
|
|
|
|
break;
|
|
|
|
default:
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
|
|
|
writer.writeStartElement(name);
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:axId"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(axis->axisId));
|
|
|
|
|
|
|
|
writer.writeStartElement(QStringLiteral("c:scaling"));
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:orientation"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QStringLiteral("minMax"));
|
|
|
|
writer.writeEndElement(); // c:scaling
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:axPos"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), pos);
|
|
|
|
|
|
|
|
writer.writeEmptyElement(QStringLiteral("c:crossAx"));
|
|
|
|
writer.writeAttribute(QStringLiteral("val"), QString::number(axis->crossAx));
|
|
|
|
|
|
|
|
writer.writeEndElement(); // name
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
QT_END_NAMESPACE_XLSX
|