Extend Axis data class

This commit is contained in:
Emil Sawicki 2022-03-08 23:44:49 +01:00
parent 2ccb2faf18
commit 8d4b1aaadc
6 changed files with 392 additions and 55 deletions

View File

@ -147,6 +147,9 @@ ChartItem::ChartItem(QObject *owner, QGraphicsItem *parent)
m_horizontalAxisOnTop(false), m_gridChartLines(AllLines), m_horizontalAxisOnTop(false), m_gridChartLines(AllLines),
m_legendStyle(LegendPoints) m_legendStyle(LegendPoints)
{ {
m_xAxisData = new AxisData(this);
m_xAxisData->setReverseDirection(true);
m_yAxisData = new AxisData(this);
m_labels<<"First"<<"Second"<<"Thrid"; m_labels<<"First"<<"Second"<<"Thrid";
m_chart = new PieChart(this); m_chart = new PieChart(this);
m_chart->setTitleFont(font()); m_chart->setTitleFont(font());
@ -224,6 +227,42 @@ void ChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
ItemDesignIntf::paint(painter,option,widget); ItemDesignIntf::paint(painter,option,widget);
} }
QObject *ChartItem::xAxisSettings()
{
return m_xAxisData;
}
void ChartItem::setYAxisSettings(QObject *axis)
{
AxisData *data = dynamic_cast<AxisData*>(axis);
if (data) {
m_yAxisData->copy(data);
}
}
QObject *ChartItem::yAxisSettings()
{
return m_yAxisData;
}
void ChartItem::setXAxisSettings(QObject *axis)
{
AxisData *data = static_cast<AxisData*>(axis);
if (data) {
m_xAxisData->copy(data);
}
}
AxisData *ChartItem::xAxisData()
{
return m_xAxisData;
}
AxisData *ChartItem::yAxisData()
{
return m_yAxisData;
}
BaseDesignIntf *ChartItem::createSameTypeItem(QObject *owner, QGraphicsItem *parent) BaseDesignIntf *ChartItem::createSameTypeItem(QObject *owner, QGraphicsItem *parent)
{ {
ChartItem* result = new ChartItem(owner,parent); ChartItem* result = new ChartItem(owner,parent);
@ -289,10 +328,7 @@ void ChartItem::fillLabels(IDataSource *dataSource)
QWidget *ChartItem::defaultEditor() QWidget *ChartItem::defaultEditor()
{ {
QSettings* l_settings = (page()->settings() != 0) ? QWidget* editor = new ChartItemEditor(this, page(), settings());
page()->settings() :
(page()->reportEditor()!=0) ? page()->reportEditor()->settings() : 0;
QWidget* editor = new ChartItemEditor(this, page(), l_settings);
editor->setAttribute(Qt::WA_DeleteOnClose); editor->setAttribute(Qt::WA_DeleteOnClose);
return editor; return editor;
} }
@ -302,6 +338,18 @@ bool ChartItem::isNeedUpdateSize(RenderPass pass) const
return pass == FirstPass && m_isEmpty; return pass == FirstPass && m_isEmpty;
} }
QSettings *ChartItem::settings()
{
PageDesignIntf *page = this->page();
if (page->settings()) {
return page->settings();
}
if (page->reportEditor()) {
return page->reportEditor()->settings();
}
return 0;
}
bool ChartItem::showLegend() const bool ChartItem::showLegend() const
{ {
return m_showLegend; return m_showLegend;
@ -724,22 +772,22 @@ AbstractSeriesChart::AbstractSeriesChart(ChartItem *chartItem)
qreal AbstractSeriesChart::maxValue() qreal AbstractSeriesChart::maxValue()
{ {
return m_yAxisData.maxValue(); return m_chartItem->yAxisData()->maxValue();
} }
qreal AbstractSeriesChart::minValue() qreal AbstractSeriesChart::minValue()
{ {
return m_yAxisData.minValue(); return m_chartItem->yAxisData()->minValue();
} }
AxisData AbstractSeriesChart::yAxisData() AxisData &AbstractSeriesChart::xAxisData() const
{ {
return m_yAxisData; return *m_chartItem->xAxisData();
} }
AxisData AbstractSeriesChart::xAxisData() AxisData &AbstractSeriesChart::yAxisData() const
{ {
return m_xAxisData; return *m_chartItem->yAxisData();
} }
void AbstractSeriesChart::updateMinAndMaxValues() void AbstractSeriesChart::updateMinAndMaxValues()
@ -772,8 +820,8 @@ void AbstractSeriesChart::updateMinAndMaxValues()
} }
} }
m_yAxisData = AxisData(minYValue, maxYValue); m_chartItem->xAxisData()->update(minXValue, maxXValue);
m_xAxisData = AxisData(minXValue, maxXValue); m_chartItem->yAxisData()->update(minYValue, maxYValue);
} }
qreal AbstractSeriesChart::hPadding(QRectF chartRect) qreal AbstractSeriesChart::hPadding(QRectF chartRect)

View File

@ -97,8 +97,8 @@ class AbstractSeriesChart: public AbstractChart{
public: public:
AbstractSeriesChart(ChartItem* chartItem); AbstractSeriesChart(ChartItem* chartItem);
protected: protected:
AxisData yAxisData(); AxisData &xAxisData() const;
AxisData xAxisData(); AxisData &yAxisData() const;
qreal maxValue(); qreal maxValue();
qreal minValue(); qreal minValue();
void updateMinAndMaxValues(); void updateMinAndMaxValues();
@ -125,7 +125,6 @@ private:
bool calculateLegendColumnWidths(qreal indicatorWidth, qreal maxWidth, const QFontMetrics &fm); bool calculateLegendColumnWidths(qreal indicatorWidth, qreal maxWidth, const QFontMetrics &fm);
bool calculateLegendSingleColumnWidth(qreal &currentRowWidth, int &currentColumn, int &maxColumnCount, bool calculateLegendSingleColumnWidth(qreal &currentRowWidth, int &currentColumn, int &maxColumnCount,
const qreal itemWidth, const qreal maxRowWidth); const qreal itemWidth, const qreal maxRowWidth);
AxisData m_yAxisData, m_xAxisData;
qreal m_designValues [9]; qreal m_designValues [9];
}; };
@ -146,6 +145,8 @@ private:
class ChartItem : public LimeReport::ItemDesignIntf class ChartItem : public LimeReport::ItemDesignIntf
{ {
Q_OBJECT Q_OBJECT
Q_PROPERTY(QObject* xAxisSettings READ xAxisSettings WRITE setXAxisSettings)
Q_PROPERTY(QObject* yAxisSettings READ yAxisSettings WRITE setYAxisSettings)
Q_PROPERTY(ACollectionProperty series READ fakeCollectionReader WRITE setSeries) Q_PROPERTY(ACollectionProperty series READ fakeCollectionReader WRITE setSeries)
Q_PROPERTY(QString datasource READ datasource WRITE setDatasource) Q_PROPERTY(QString datasource READ datasource WRITE setDatasource)
Q_PROPERTY(QString chartTitle READ chartTitle WRITE setChartTitle) Q_PROPERTY(QString chartTitle READ chartTitle WRITE setChartTitle)
@ -201,6 +202,13 @@ public:
~ChartItem(); ~ChartItem();
virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget); virtual void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
QObject* xAxisSettings();
void setYAxisSettings(QObject *axis);
QObject* yAxisSettings();
void setXAxisSettings(QObject *axis);
AxisData *xAxisData();
AxisData *yAxisData();
QList<SeriesItem *> &series(); QList<SeriesItem *> &series();
void setSeries(const QList<SeriesItem *> &series); void setSeries(const QList<SeriesItem *> &series);
bool isSeriesExists(const QString& name); bool isSeriesExists(const QString& name);
@ -255,6 +263,8 @@ public:
void setTitleFont(QFont value); void setTitleFont(QFont value);
void setCharItemFont(QFont value); void setCharItemFont(QFont value);
QSettings *settings();
protected: protected:
void paintChartTitle(QPainter* painter, QRectF titleRect); void paintChartTitle(QPainter* painter, QRectF titleRect);
virtual BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent); virtual BaseDesignIntf* createSameTypeItem(QObject *owner, QGraphicsItem *parent);
@ -287,6 +297,7 @@ private:
bool m_horizontalAxisOnTop; bool m_horizontalAxisOnTop;
GridChartLines m_gridChartLines; GridChartLines m_gridChartLines;
LegendStyle m_legendStyle; LegendStyle m_legendStyle;
AxisData *m_xAxisData, *m_yAxisData;
}; };
} //namespace LimeReport } //namespace LimeReport
#endif // LRCHARTITEM_H #endif // LRCHARTITEM_H

View File

@ -1,21 +1,46 @@
#include "lraxisdata.h" #include "lraxisdata.h"
namespace LimeReport { #include <cmath>
AxisData::AxisData() #include <QDebug>
: m_rangeMin(0), m_rangeMax(0),
m_minValue(0), m_maxValue(0), m_step(0),
m_delta(0), m_segmentCount(4)
{
namespace LimeReport {
AxisData::AxisData(QObject *parent)
: QObject(parent), m_rangeMin(0), m_rangeMax(0),
m_minValue(0), m_maxValue(0), m_step(0),
m_delta(0), m_segmentCount(4), m_calculateAxisScale(false),
m_reverseDirection(false), m_manualMaximum(0),
m_manualMinimum(0), m_manualStep(0), m_isMaximumAutomatic(true),
m_isMinimumAutomatic(true), m_isStepAutomatic(true)
{
} }
AxisData::AxisData(qreal minValue, qreal maxValue) void AxisData::copy(AxisData *other)
: AxisData() {
m_calculateAxisScale = other->calculateAxisScale();
m_reverseDirection = other->reverseDirection();
m_manualMaximum = other->manualMaximum();
m_manualMinimum = other->manualMinimum();
m_manualStep = other->manualStep();
m_isMaximumAutomatic = other->isMaximumAutomatic();
m_isMinimumAutomatic = other->isMinimumAutomatic();
m_isStepAutomatic = other->isStepAutomatic();
}
void AxisData::update()
{
if (m_calculateAxisScale) {
calculateRoundedAxisScale();
} else {
calculateSimpleAxisScale();
}
m_delta = m_step * m_segmentCount;
}
void AxisData::update(qreal minValue, qreal maxValue)
{ {
m_minValue = minValue; m_minValue = minValue;
m_maxValue = maxValue; m_maxValue = maxValue;
calculateValuesAboveMax(minValue, maxValue, 4); update();
m_delta = m_step * m_segmentCount;
} }
int AxisData::segmentCount() const int AxisData::segmentCount() const
@ -23,6 +48,11 @@ int AxisData::segmentCount() const
return m_segmentCount; return m_segmentCount;
} }
bool AxisData::calculateAxisScale() const
{
return m_calculateAxisScale;
}
qreal AxisData::rangeMin() const qreal AxisData::rangeMin() const
{ {
return m_rangeMin; return m_rangeMin;
@ -53,16 +83,207 @@ qreal AxisData::delta() const
return m_delta; return m_delta;
} }
void AxisData::calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments) void AxisData::calculateRoundedAxisScale()
{ {
const int delta = maxValue - minValue; const int maximumSegmentCount = 10;
bool calculateStep = isStepAutomatic();
const bool calculateMinimum = isMinimumAutomatic();
const bool calculateMaximum = isMaximumAutomatic();
qreal temporaryMin = calculateMinimum ? minValue() < 0 ? minValue() : 0 : manualMinimum();
qreal temporaryMax = calculateMaximum ? maxValue() : manualMaximum();
m_step = calculateStep ? 0 : manualStep();
if (temporaryMax == temporaryMin) {
if (temporaryMax == 0) {
temporaryMax = 1;
} else {
temporaryMax *= 2;
}
}
const qreal minAndMaxSpacingOffset = 0.95;
qreal stepMagnitude = 0.0;
qreal normalizedStep = 0.0;
bool isStepNormalized = false;
bool isLoopFinished = false;
// Calculate until segment count is below maximum
while( !isLoopFinished ) {
if (calculateStep) {
if(isStepNormalized) {
if( normalizedStep == 1.0 ) {
normalizedStep = 2.0;
} else if( normalizedStep == 2.0 ) {
normalizedStep = 5.0;
} else {
normalizedStep = 1.0;
stepMagnitude *= 10;
}
} else {
const double startingStep = (temporaryMax - temporaryMin) / maximumSegmentCount;
const int exponent = static_cast< int >( floor( log10( startingStep ) ) );
stepMagnitude = pow(10.0, static_cast<double>(exponent));
normalizedStep = startingStep / stepMagnitude;
if( normalizedStep <= 1.0 ) {
normalizedStep = 1.0;
} else if( normalizedStep <= 2.0 ) {
normalizedStep = 2.0;
} else if( normalizedStep <= 5.0 ) {
normalizedStep = 5.0;
} else {
normalizedStep = 1.0;
stepMagnitude *= 10;
}
isStepNormalized = true;
}
m_step = normalizedStep * stepMagnitude;
}
qreal currentAxisMinimum = temporaryMin;
qreal currentAxisMaximum = temporaryMax;
if (calculateMinimum) {
currentAxisMinimum = calculateNewMinimum(currentAxisMinimum, m_step);
const qreal currentDelta = currentAxisMaximum - currentAxisMinimum;
const qreal actualDelta = currentAxisMaximum - minValue();
if ((currentAxisMinimum != 0.0) && ((actualDelta / currentDelta) > minAndMaxSpacingOffset)) {
currentAxisMinimum -= m_step;
}
}
if (calculateMaximum) {
currentAxisMaximum = calculateNewMaximum(currentAxisMaximum, m_step);
const qreal currentDelta = currentAxisMaximum - currentAxisMinimum;
const qreal actualDelta = maxValue() - currentAxisMinimum;
if ((currentAxisMaximum != 0.0) && ((actualDelta / currentDelta) > minAndMaxSpacingOffset)) {
currentAxisMaximum += m_step;
}
}
m_segmentCount = static_cast<int>(round((currentAxisMaximum - currentAxisMinimum) / m_step));
m_rangeMin = currentAxisMinimum;
m_rangeMax = currentAxisMaximum;
isLoopFinished = m_segmentCount <= maximumSegmentCount;
if (!isLoopFinished) {
// Configured step may be invalid, calculating it automatically
calculateStep = true;
}
}
}
void AxisData::calculateSimpleAxisScale()
{
m_segmentCount = 4;
const int delta = maxValue() - minValue();
int max = delta; int max = delta;
while (max % segments != 0){ while (max % m_segmentCount != 0){
max++; max++;
} }
m_rangeMax = max; m_rangeMax = max;
m_step = max / segments; m_step = max / m_segmentCount;
m_rangeMin = minValue; m_rangeMin = minValue();
m_segmentCount = segments;
} }
double AxisData::calculateNewMinimum(qreal min, qreal step) const
{
if (step <= 0.0)
return min;
double ret = floor(min / step) * step;
if (ret > min && !qFuzzyCompare(ret, min)) {
ret -= step;
}
return ret;
}
double AxisData::calculateNewMaximum(qreal max, qreal step) const
{
if (step <= 0.0)
return max;
double ret = floor(max / step) * step;
if (ret < max && !qFuzzyCompare(ret, max)) {
ret += step;
}
return ret;
}
void AxisData::setCalculateAxisScale(bool newCalculateAxisScale)
{
m_calculateAxisScale = newCalculateAxisScale;
}
bool AxisData::reverseDirection() const
{
return m_reverseDirection;
}
void AxisData::setReverseDirection(bool reverseDirection)
{
m_reverseDirection = reverseDirection;
}
qreal AxisData::manualMaximum() const
{
return m_manualMaximum;
}
void AxisData::setManualMaximum(qreal newManualMaximum)
{
m_manualMaximum = newManualMaximum;
}
qreal AxisData::manualMinimum() const
{
return m_manualMinimum;
}
void AxisData::setManualMinimum(qreal newManualMinimum)
{
m_manualMinimum = newManualMinimum;
}
qreal AxisData::manualStep() const
{
return m_manualStep;
}
void AxisData::setManualStep(qreal newManualStep)
{
m_manualStep = newManualStep;
}
bool AxisData::isMaximumAutomatic() const
{
return m_isMaximumAutomatic;
}
void AxisData::setIsMaximumAutomatic(bool newIsMaximumAutomatic)
{
m_isMaximumAutomatic = newIsMaximumAutomatic;
}
bool AxisData::isMinimumAutomatic() const
{
return m_isMinimumAutomatic;
}
void AxisData::setIsMinimumAutomatic(bool newIsMinimumAutomatic)
{
m_isMinimumAutomatic = newIsMinimumAutomatic;
}
bool AxisData::isStepAutomatic() const
{
return m_isStepAutomatic;
}
void AxisData::setIsStepAutomatic(bool newIsStepAutomatic)
{
m_isStepAutomatic = newIsStepAutomatic;
}
} }

View File

@ -1,14 +1,26 @@
#ifndef AXISDATA_H #ifndef AXISDATA_H
#define AXISDATA_H #define AXISDATA_H
#include <QtGlobal> #include <QObject>
namespace LimeReport { namespace LimeReport {
class AxisData class AxisData : public QObject
{ {
Q_OBJECT
Q_PROPERTY(bool reverseDirection READ reverseDirection WRITE setReverseDirection)
Q_PROPERTY(bool calculateAxisScale READ calculateAxisScale WRITE setCalculateAxisScale)
Q_PROPERTY(bool isStepAutomatic READ isStepAutomatic WRITE setIsStepAutomatic)
Q_PROPERTY(bool isMinimumAutomatic READ isMinimumAutomatic WRITE setIsMinimumAutomatic)
Q_PROPERTY(bool isMaximumAutomatic READ isMaximumAutomatic WRITE setIsMaximumAutomatic)
Q_PROPERTY(qreal manualStep READ manualStep WRITE setManualStep)
Q_PROPERTY(qreal manualMinimum READ manualMinimum WRITE setManualMinimum)
Q_PROPERTY(qreal manualMaximum READ manualMaximum WRITE setManualMaximum)
public: public:
AxisData(); AxisData(QObject *parent = nullptr);
AxisData(qreal minValue, qreal maxValue);
void copy(AxisData *other);
void update();
void update(qreal minValue, qreal maxValue);
int segmentCount() const; int segmentCount() const;
@ -21,8 +33,34 @@ public:
qreal delta() const; qreal delta() const;
bool reverseDirection() const;
void setReverseDirection(bool newReverseDirection);
bool calculateAxisScale() const;
void setCalculateAxisScale(bool newCalculateAxisScale);
qreal manualMaximum() const;
void setManualMaximum(qreal newManualMaximum);
qreal manualMinimum() const;
void setManualMinimum(qreal newManualMinimum);
qreal manualStep() const;
void setManualStep(qreal newManualStep);
bool isMaximumAutomatic() const;
void setIsMaximumAutomatic(bool newIsMaximumAutomatic);
bool isMinimumAutomatic() const;
void setIsMinimumAutomatic(bool newIsMinimumAutomatic);
bool isStepAutomatic() const;
void setIsStepAutomatic(bool newIsStepAutomatic);
private: private:
void calculateValuesAboveMax(qreal minValue, qreal maxValue, int segments); void calculateRoundedAxisScale();
void calculateSimpleAxisScale();
qreal calculateNewMinimum(qreal min, qreal step) const;
qreal calculateNewMaximum(qreal max, qreal step) const;
qreal m_rangeMin; qreal m_rangeMin;
qreal m_rangeMax; qreal m_rangeMax;
@ -31,6 +69,14 @@ private:
qreal m_step; qreal m_step;
qreal m_delta; qreal m_delta;
int m_segmentCount; int m_segmentCount;
bool m_calculateAxisScale;
bool m_reverseDirection;
qreal m_manualMaximum;
qreal m_manualMinimum;
qreal m_manualStep;
bool m_isMaximumAutomatic;
bool m_isMinimumAutomatic;
bool m_isStepAutomatic;
}; };
}; };

View File

@ -1290,32 +1290,40 @@ void BaseDesignIntf::setItemPos(const QPointF &newPos)
} }
QWidget* findRootWidget(QWidget* widget){ QWidget* BaseDesignIntf::findRootWidget(QWidget* widget)
{
while (widget->parentWidget()) { while (widget->parentWidget()) {
widget = widget->parentWidget(); widget = widget->parentWidget();
} }
return widget; return widget;
} }
void BaseDesignIntf::showEditorDialog(){ void BaseDesignIntf::showDialog(QWidget *widget)
QWidget *editor = defaultEditor(); {
if (editor) { if (!widget) {
editor->setStyleSheet(findRootWidget(scene()->views().at(0))->styleSheet()); return;
QDialog* dialog = new QDialog(QApplication::activeWindow());
dialog->setAttribute(Qt::WA_DeleteOnClose);
#ifdef Q_OS_MAC
dialog->setWindowModality(Qt::WindowModal);
#else
dialog->setWindowModality(Qt::ApplicationModal);
#endif
dialog->setLayout(new QVBoxLayout());
dialog->resize(editor->size());
dialog->layout()->setContentsMargins(2,2,2,2);
dialog->layout()->addWidget(editor);
connect(editor,SIGNAL(destroyed()),dialog,SLOT(close()));
dialog->setWindowTitle(editor->windowTitle());
dialog->exec();
} }
widget->setStyleSheet(findRootWidget(scene()->views().at(0))->styleSheet());
QDialog dialog;
widget->setParent(&dialog);
widget->setAttribute(Qt::WA_DeleteOnClose);
#ifdef Q_OS_MAC
dialog.setWindowModality(Qt::WindowModal);
#else
dialog.setWindowModality(Qt::ApplicationModal);
#endif
dialog.setLayout(new QVBoxLayout());
dialog.resize(widget->size());
dialog.layout()->setContentsMargins(2,2,2,2);
dialog.layout()->addWidget(widget);
connect(widget,SIGNAL(destroyed()),&dialog,SLOT(close()));
dialog.setWindowTitle(widget->windowTitle());
dialog.exec();
}
void BaseDesignIntf::showEditorDialog()
{
showDialog(defaultEditor());
} }
void BaseDesignIntf::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) void BaseDesignIntf::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)

View File

@ -397,6 +397,9 @@ protected:
qreal calcAbsolutePosY(qreal currentOffset, BaseDesignIntf* item); qreal calcAbsolutePosY(qreal currentOffset, BaseDesignIntf* item);
qreal calcAbsolutePosX(qreal currentOffset, BaseDesignIntf* item); qreal calcAbsolutePosX(qreal currentOffset, BaseDesignIntf* item);
QWidget* findRootWidget(QWidget* widget);
void showDialog(QWidget *widget);
private: private:
int resizeDirectionFlags(QPointF position); int resizeDirectionFlags(QPointF position);
void moveSelectedItems(QPointF delta); void moveSelectedItems(QPointF delta);