Merge pull request #390 from emil-sawicki9/feature/axis-range-scaling

Feature/new axis scaling
This commit is contained in:
Alexander Arin 2022-03-20 21:40:15 +03:00 committed by GitHub
commit 49b01991ac
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
15 changed files with 1011 additions and 85 deletions

View File

@ -30,6 +30,7 @@ SOURCES += \
$$REPORT_PATH/objectinspector/propertyItems/lrcontentpropitem.cpp \
$$REPORT_PATH/objectinspector/propertyItems/lrmarginpropitem.cpp \
$$REPORT_PATH/objectinspector/propertyItems/lrseriespropitem.cpp \
$$REPORT_PATH/objectinspector/propertyItems/lraxispropitem.cpp \
$$REPORT_PATH/objectinspector/editors/lrtextitempropertyeditor.cpp \
$$REPORT_PATH/objectinspector/editors/lrcomboboxeditor.cpp \
$$REPORT_PATH/objectinspector/editors/lrcheckboxeditor.cpp \
@ -80,6 +81,7 @@ HEADERS += \
$$REPORT_PATH/objectinspector/propertyItems/lrcolorpropitem.h \
$$REPORT_PATH/objectinspector/propertyItems/lrmarginpropitem.h \
$$REPORT_PATH/objectinspector/propertyItems/lrseriespropitem.h \
$$REPORT_PATH/objectinspector/propertyItems/lraxispropitem.h \
$$REPORT_PATH/objectinspector/editors/lrtextitempropertyeditor.h \
$$REPORT_PATH/objectinspector/editors/lrcomboboxeditor.h \
$$REPORT_PATH/objectinspector/editors/lrcheckboxeditor.h \

View File

@ -70,7 +70,12 @@ void LinesChart::drawDesignMode(QPainter* painter, qreal hStep, qreal vStep, qre
qreal LinesChart::calculatePos(const AxisData &data, qreal value, qreal rectSize) const
{
return (data.rangeMax() - value) / data.delta() * rectSize;
if (data.type() == AxisData::XAxis || (data.reverseDirection() && data.rangeMin() >= 0)) {
// Not flipping for minimum less than 0 because lower number is at the bottom.
return (1 - (data.rangeMax() - value) / data.delta()) * rectSize;
} else {
return (data.rangeMax() - value) / data.delta() * rectSize;
}
}
void LinesChart::paintSeries(QPainter *painter, SeriesItem *series, QRectF barsRect)

View File

@ -71,8 +71,6 @@ void VerticalBarChart::paintVerticalBars(QPainter *painter, QRectF barsRect)
qreal hStep = (barsRect.width() / valuesCount()) / (barSeriesCount == 0 ? 1 : barSeriesCount);
qreal topShift = (delta - (maxValue() - minValue())) * vStep + barsRect.top();
qDebug() << "vStep" << vStep << "hStep" << hStep;
if (!m_chartItem->series().isEmpty() && (m_chartItem->itemMode() != DesignMode)){
int curSeries = 0;
foreach (SeriesItem* series, m_chartItem->series()) {

View File

@ -0,0 +1,142 @@
#include "lrchartaxiseditor.h"
#include "ui_lrchartaxiseditor.h"
#include "lraxisdata.h"
#include "lrbasedesignintf.h"
ChartAxisEditor::ChartAxisEditor(LimeReport::ChartItem *item, LimeReport::PageDesignIntf *page, bool isXAxis, QSettings *settings, QWidget *parent):
QWidget(parent), ui(new Ui::ChartAxisEditor), m_chartItem(item), m_page(page),
m_settings(settings), m_isXAxis(isXAxis)
{
ui->setupUi(this);
readSetting();
init();
}
ChartAxisEditor::~ChartAxisEditor()
{
#if defined(Q_OS_WIN) || defined(Q_OS_MAC)
writeSetting();
#endif
delete ui;
}
QSettings* ChartAxisEditor::settings()
{
if (m_settings){
return m_settings;
}
m_settings = new QSettings("LimeReport",QCoreApplication::applicationName());
return m_settings;
}
void ChartAxisEditor::readSetting()
{
if (settings() == 0) return;
settings()->beginGroup("ChartAxisEditor");
QVariant v = settings()->value("Geometry");
if (v.isValid()) {
restoreGeometry(v.toByteArray());
}
settings()->endGroup();
}
void ChartAxisEditor::writeSetting()
{
if (settings() == 0) {
return;
}
settings()->beginGroup("ChartAxisEditor");
settings()->setValue("Geometry",saveGeometry());
settings()->endGroup();
}
void ChartAxisEditor::init()
{
ui->gbAxis->setTitle(m_isXAxis ? QObject::tr("X Axis") : QObject::tr("Y Axis"));
ui->direction_checkbox->setVisible(!m_isXAxis);
LimeReport::AxisData *axisData = m_isXAxis ? m_chartItem->xAxisData() : m_chartItem->yAxisData();
ui->minimumSpinBox->setValue(axisData->manualMinimum());
ui->maximumSpinBox->setValue(axisData->manualMaximum());
ui->stepSpinBox->setValue(axisData->manualStep());
ui->minimumCheckBox->setChecked(axisData->isMinimumAutomatic());
ui->maximumCheckBox->setChecked(axisData->isMaximumAutomatic());
ui->stepCheckBox->setChecked(axisData->isStepAutomatic());
ui->direction_checkbox->setChecked(axisData->reverseDirection());
const bool isScaleCalcEnabled = axisData->calculateAxisScale();
ui->enableScaleCalculation_checkbox->setChecked(isScaleCalcEnabled);
on_enableScaleCalculation_checkbox_stateChanged(isScaleCalcEnabled);
}
void ChartAxisEditor::on_minimumCheckBox_stateChanged(int arg1)
{
const bool isAutomatic = (bool)arg1;
ui->minimumSpinBox->setEnabled(!isAutomatic);
}
void ChartAxisEditor::on_maximumCheckBox_stateChanged(int arg1)
{
const bool isAutomatic = (bool)arg1;
ui->maximumSpinBox->setEnabled(!isAutomatic);
}
void ChartAxisEditor::on_stepCheckBox_stateChanged(int arg1)
{
const bool isAutomatic = (bool)arg1;
ui->stepSpinBox->setEnabled(!isAutomatic);
}
void ChartAxisEditor::on_pushButtonOk_clicked()
{
LimeReport::AxisData *axisData = m_isXAxis ? m_chartItem->xAxisData() : m_chartItem->yAxisData();
if (!m_isXAxis) {
axisData->setReverseDirection(ui->direction_checkbox->isChecked());
}
axisData->setIsStepAutomatic(ui->stepCheckBox->isChecked());
axisData->setManualStep(ui->stepSpinBox->value());
axisData->setIsMinimumAutomatic(ui->minimumCheckBox->isChecked());
axisData->setManualMinimum(ui->minimumSpinBox->value());
axisData->setIsMaximumAutomatic(ui->maximumCheckBox->isChecked());
axisData->setManualMaximum(ui->maximumSpinBox->value());
axisData->setCalculateAxisScale(ui->enableScaleCalculation_checkbox->isChecked());
if (m_chartItem->itemMode() == LimeReport::DesignMode) {
axisData->updateForDesignMode();
} else {
axisData->update();
}
m_chartItem->update();
close();
}
void ChartAxisEditor::on_enableScaleCalculation_checkbox_stateChanged(int arg1)
{
const bool isEnabled = (bool)arg1;
ui->minimumCheckBox->setEnabled(isEnabled);
ui->maximumCheckBox->setEnabled(isEnabled);
ui->stepCheckBox->setEnabled(isEnabled);
ui->minimumSpinBox->setEnabled(!ui->minimumCheckBox->isChecked() && isEnabled);
ui->maximumSpinBox->setEnabled(!ui->maximumCheckBox->isChecked() && isEnabled);
ui->stepSpinBox->setEnabled(!ui->stepCheckBox->isChecked() && isEnabled);
ui->minimumCheckBox->setEnabled(isEnabled);
ui->maximumCheckBox->setEnabled(isEnabled);
ui->stepCheckBox->setEnabled(isEnabled);
}
void ChartAxisEditor::on_cancelButton_clicked()
{
close();
}

View File

@ -0,0 +1,41 @@
#ifndef CHARTAXISEDITOR_H
#define CHARTAXISEDITOR_H
#include <QWidget>
#include "lrchartitem.h"
namespace Ui {
class ChartAxisEditor;
}
class ChartAxisEditor : public QWidget
{
Q_OBJECT
public:
ChartAxisEditor(LimeReport::ChartItem* item, LimeReport::PageDesignIntf* page, bool isXAxis,
QSettings* settings=0, QWidget *parent = 0);
~ChartAxisEditor();
QSettings *settings();
private slots:
void on_minimumCheckBox_stateChanged(int arg1);
void on_maximumCheckBox_stateChanged(int arg1);
void on_stepCheckBox_stateChanged(int arg1);
void on_pushButtonOk_clicked();
void on_enableScaleCalculation_checkbox_stateChanged(int arg1);
void on_cancelButton_clicked();
private:
void readSetting();
void writeSetting();
void init();
Ui::ChartAxisEditor *ui;
LimeReport::ChartItem* m_chartItem;
LimeReport::PageDesignIntf* m_page;
QSettings* m_settings;
bool m_isXAxis;
};
#endif // CHARTAXISEDITOR_H

View File

@ -0,0 +1,212 @@
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
<class>ChartAxisEditor</class>
<widget class="QWidget" name="ChartAxisEditor">
<property name="geometry">
<rect>
<x>0</x>
<y>0</y>
<width>380</width>
<height>268</height>
</rect>
</property>
<property name="windowTitle">
<string>Axis editor</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout_2">
<item>
<widget class="QGroupBox" name="gbAxis">
<property name="sizePolicy">
<sizepolicy hsizetype="Expanding" vsizetype="Expanding">
<horstretch>0</horstretch>
<verstretch>0</verstretch>
</sizepolicy>
</property>
<property name="title">
<string>Axis</string>
</property>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QCheckBox" name="direction_checkbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Reverse direction</string>
</property>
</widget>
</item>
<item>
<widget class="QCheckBox" name="enableScaleCalculation_checkbox">
<property name="enabled">
<bool>true</bool>
</property>
<property name="text">
<string>Enable scale calculation</string>
</property>
</widget>
</item>
<item>
<layout class="QGridLayout" name="gridLayout">
<property name="sizeConstraint">
<enum>QLayout::SetFixedSize</enum>
</property>
<item row="2" column="0">
<widget class="QLabel" name="stepLabel">
<property name="text">
<string>Step</string>
</property>
</widget>
</item>
<item row="1" column="0">
<widget class="QLabel" name="maximumLabel">
<property name="text">
<string>Maximum</string>
</property>
</widget>
</item>
<item row="0" column="2">
<widget class="QDoubleSpinBox" name="minimumSpinBox">
<property name="showGroupSeparator" stdset="0">
<bool>false</bool>
</property>
<property name="minimum">
<double>-99999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
</widget>
</item>
<item row="0" column="0">
<widget class="QLabel" name="minimumLabel">
<property name="text">
<string>Minimum</string>
</property>
</widget>
</item>
<item row="2" column="3">
<widget class="QCheckBox" name="stepCheckBox">
<property name="text">
<string>Automatic</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="0" column="3">
<widget class="QCheckBox" name="minimumCheckBox">
<property name="text">
<string>Automatic</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="1" column="3">
<widget class="QCheckBox" name="maximumCheckBox">
<property name="text">
<string>Automatic</string>
</property>
<property name="checked">
<bool>true</bool>
</property>
</widget>
</item>
<item row="2" column="2">
<widget class="QDoubleSpinBox" name="stepSpinBox">
<property name="minimum">
<double>0.000000000000000</double>
</property>
<property name="maximum">
<double>9999999.990000000223517</double>
</property>
</widget>
</item>
<item row="1" column="2">
<widget class="QDoubleSpinBox" name="maximumSpinBox">
<property name="minimum">
<double>-9999999.000000000000000</double>
</property>
<property name="maximum">
<double>99999999.000000000000000</double>
</property>
</widget>
</item>
<item row="1" column="4">
<spacer name="horizontalSpacer_2">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
</layout>
</item>
<item>
<spacer name="verticalSpacer">
<property name="orientation">
<enum>Qt::Vertical</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>20</width>
<height>40</height>
</size>
</property>
</spacer>
</item>
</layout>
</widget>
</item>
<item>
<layout class="QHBoxLayout" name="horizontalLayout_4">
<item>
<layout class="QHBoxLayout" name="horizontalLayout_3">
<item>
<spacer name="horizontalSpacer">
<property name="orientation">
<enum>Qt::Horizontal</enum>
</property>
<property name="sizeHint" stdset="0">
<size>
<width>40</width>
<height>20</height>
</size>
</property>
</spacer>
</item>
<item>
<widget class="QPushButton" name="cancelButton">
<property name="text">
<string>Cancel</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="pushButtonOk">
<property name="text">
<string>Ok</string>
</property>
</widget>
</item>
</layout>
</item>
</layout>
</item>
</layout>
</widget>
<resources/>
<connections/>
<slots>
<slot>slotAddSeries()</slot>
<slot>slotDeleteSeries()</slot>
</slots>
</ui>

View File

@ -8,6 +8,7 @@
#include "lrpagedesignintf.h"
#include "lrreportengine_p.h"
#include "lrdatadesignintf.h"
#include "lrchartaxiseditor.h"
#include "charts/lrpiechart.h"
#include "charts/lrverticalbarchart.h"
@ -147,6 +148,9 @@ ChartItem::ChartItem(QObject *owner, QGraphicsItem *parent)
m_horizontalAxisOnTop(false), m_gridChartLines(AllLines),
m_legendStyle(LegendPoints)
{
m_xAxisData = new AxisData(AxisData::XAxis, this);
m_xAxisData->setReverseDirection(true);
m_yAxisData = new AxisData(AxisData::YAxis, this);
m_labels<<"First"<<"Second"<<"Thrid";
m_chart = new PieChart(this);
m_chart->setTitleFont(font());
@ -224,6 +228,47 @@ void ChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option,
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;
}
void ChartItem::showAxisEditorDialog(bool isXAxis)
{
showDialog(new ChartAxisEditor(this, page(), isXAxis, settings()));
}
BaseDesignIntf *ChartItem::createSameTypeItem(QObject *owner, QGraphicsItem *parent)
{
ChartItem* result = new ChartItem(owner,parent);
@ -289,10 +334,7 @@ void ChartItem::fillLabels(IDataSource *dataSource)
QWidget *ChartItem::defaultEditor()
{
QSettings* l_settings = (page()->settings() != 0) ?
page()->settings() :
(page()->reportEditor()!=0) ? page()->reportEditor()->settings() : 0;
QWidget* editor = new ChartItemEditor(this, page(), l_settings);
QWidget* editor = new ChartItemEditor(this, page(), settings());
editor->setAttribute(Qt::WA_DeleteOnClose);
return editor;
}
@ -302,6 +344,18 @@ bool ChartItem::isNeedUpdateSize(RenderPass pass) const
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
{
return m_showLegend;
@ -724,56 +778,58 @@ AbstractSeriesChart::AbstractSeriesChart(ChartItem *chartItem)
qreal AbstractSeriesChart::maxValue()
{
return m_yAxisData.maxValue();
return m_chartItem->yAxisData()->maxValue();
}
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()
{
qreal maxYValue = 0;
qreal minYValue = 0;
qreal maxXValue = 0;
qreal minXValue = 0;
if (m_chartItem->itemMode() == DesignMode) {
maxYValue = 40;
maxXValue = 40;
} else {
for (SeriesItem* series : m_chartItem->series()){
for (qreal value : series->data()->values()){
minYValue = std::min(minYValue, value);
maxYValue = std::max(maxYValue, value);
}
if (series->data()->xAxisValues().isEmpty()) {
// Grid plot starts from 0 on x axis so x range must be decresed by 1
const bool startingFromZero = m_chartItem->chartType() == ChartItem::GridLines;
const qreal valuesCount = this->valuesCount() - (startingFromZero ? 1 : 0);
minXValue = std::min(0.0, minXValue);
maxXValue = std::max(valuesCount, maxXValue);
} else {
for (qreal value : series->data()->xAxisValues()){
minXValue = std::min(value, minXValue);
maxXValue = std::max(value, maxXValue);
}
m_chartItem->xAxisData()->updateForDesignMode();
m_chartItem->yAxisData()->updateForDesignMode();
return;
}
qreal maxYValue = 0;
qreal minYValue = std::numeric_limits<qreal>::max();
qreal maxXValue = 0;
qreal minXValue = std::numeric_limits<qreal>::max();
for (SeriesItem* series : m_chartItem->series()){
for (qreal value : series->data()->values()){
minYValue = std::min(minYValue, value);
maxYValue = std::max(maxYValue, value);
}
if (series->data()->xAxisValues().isEmpty()) {
// Grid plot starts from 0 on x axis so x range must be decresed by 1
const bool startingFromZero = m_chartItem->chartType() == ChartItem::GridLines;
const qreal valuesCount = this->valuesCount() - (startingFromZero ? 1 : 0);
minXValue = std::min(0.0, minXValue);
maxXValue = std::max(valuesCount, maxXValue);
} else {
for (qreal value : series->data()->xAxisValues()){
minXValue = std::min(value, minXValue);
maxXValue = std::max(value, maxXValue);
}
}
}
m_yAxisData = AxisData(minYValue, maxYValue);
m_xAxisData = AxisData(minXValue, maxXValue);
m_chartItem->xAxisData()->update(minXValue, maxXValue);
m_chartItem->yAxisData()->update(minYValue, maxYValue);
}
qreal AbstractSeriesChart::hPadding(QRectF chartRect)
@ -977,7 +1033,7 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect)
painter->save();
const AxisData &yAxisData = this->yAxisData();
const AxisData &xAxisData = this->xAxisData();
AxisData &xAxisData = this->xAxisData();
painter->setRenderHint(QPainter::Antialiasing,false);
@ -1035,7 +1091,6 @@ void AbstractSeriesChart::paintGrid(QPainter *painter, QRectF gridRect)
text);
}
}
painter->restore();
}
@ -1121,7 +1176,12 @@ QString AbstractSeriesChart::axisLabel(int i, const AxisData &axisData)
{
const qreal min = axisData.rangeMin();
const qreal step = axisData.step();
qreal value = min + i * step;
qreal value = 0;
if (axisData.type() == AxisData::YAxis && axisData.reverseDirection() && min >= 0) {
value = min + (axisData.segmentCount() - i) * step;
} else {
value = min + i * step;
}
if (std::floor(step) == step) {
return QString::number(value);
}

View File

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

View File

@ -47,6 +47,7 @@ SOURCES += \
$$REPORT_PATH/items/lrabstractlayout.cpp \
$$REPORT_PATH/items/lrchartitem.cpp \
$$REPORT_PATH/items/lrchartitemeditor.cpp \
$$REPORT_PATH/items/lrchartaxiseditor.cpp \
$$REPORT_PATH/items/charts/lrhorizontalbarchart.cpp \
$$REPORT_PATH/items/charts/lrlineschart.cpp \
$$REPORT_PATH/items/charts/lrgridlineschart.cpp \
@ -131,6 +132,7 @@ HEADERS += \
$$REPORT_PATH/items/lrabstractlayout.h \
$$REPORT_PATH/items/lrchartitem.h \
$$REPORT_PATH/items/lrchartitemeditor.h \
$$REPORT_PATH/items/lrchartaxiseditor.h \
$$REPORT_PATH/items/charts/lrhorizontalbarchart.h \
$$REPORT_PATH/items/charts/lrlineschart.h \
$$REPORT_PATH/items/charts/lrgridlineschart.h \
@ -195,6 +197,7 @@ FORMS += \
$$REPORT_PATH/lraboutdialog.ui \
$$REPORT_PATH/lrsettingdialog.ui \
$$REPORT_PATH/items/lrchartitemeditor.ui \
$$REPORT_PATH/items/lrchartaxiseditor.ui \
$$REPORT_PATH/items/lrimageitemeditor.ui \
$$REPORT_PATH/scripteditor/lrscripteditor.ui

View File

@ -1,21 +1,84 @@
#include "lraxisdata.h"
namespace LimeReport {
AxisData::AxisData()
: m_rangeMin(0), m_rangeMax(0),
m_minValue(0), m_maxValue(0), m_step(0),
m_delta(0), m_segmentCount(4)
{
#include <cmath>
#include <QDebug>
namespace LimeReport {
AxisData::AxisData(AxisType type, 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),
m_type(type)
{
}
AxisData::AxisData(qreal minValue, qreal maxValue)
: AxisData()
QString AxisData::toString() const
{
// Just for debug purposes
QString str;
QTextStream stream(&str);
stream << "{ "
<< "min: " << m_minValue << ", max: " << m_maxValue << ", step: " << m_step
<< ", range min: " << m_rangeMin << ", range max: " << m_rangeMax << ", segments: " << m_segmentCount
<< ", reverseDiection: " << m_reverseDirection << ", calculateAxisScale: " << m_calculateAxisScale
<< ", manualMaxEnabled: " << !m_isMaximumAutomatic << ", manualMinEnabled: " << !m_isMinimumAutomatic
<< ", manualStepEnabled: " << !m_isStepAutomatic << ", manualMax: " << m_manualMaximum
<< ", manualMin: " << m_manualMinimum << ", manualStep: " << m_manualStep
<< " }";
return str;
}
void AxisData::copy(AxisData *other)
{
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;
// Update manual values if they are automatic
if (m_isStepAutomatic) {
m_manualStep = m_step;
}
if (m_isMinimumAutomatic) {
m_manualMinimum = m_rangeMin;
}
if (m_isMaximumAutomatic) {
m_manualMaximum = m_rangeMax;
}
}
void AxisData::update(qreal minValue, qreal maxValue)
{
m_minValue = minValue;
m_maxValue = maxValue;
calculateValuesAboveMax(minValue, maxValue, 4);
m_delta = m_step * m_segmentCount;
update();
}
void AxisData::updateForDesignMode()
{
m_minValue = 0;
m_maxValue = 40;
const bool tmp = m_calculateAxisScale;
m_calculateAxisScale = false;
update();
m_calculateAxisScale = tmp;
}
int AxisData::segmentCount() const
@ -23,6 +86,11 @@ int AxisData::segmentCount() const
return m_segmentCount;
}
bool AxisData::calculateAxisScale() const
{
return m_calculateAxisScale;
}
qreal AxisData::rangeMin() const
{
return m_rangeMin;
@ -53,16 +121,229 @@ qreal AxisData::delta() const
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 = 0;
qreal temporaryMax = 0;
if (calculateMinimum) {
temporaryMin = qMin(0.0, minValue());
} else {
temporaryMin = qMin(manualMinimum(), minValue());
}
if (calculateMaximum) {
temporaryMax = maxValue();
} else {
temporaryMax = qMax(manualMaximum(), maxValue());
}
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;
// Check also if step is correctly calucalted. It is possible for float steps that
// there might be a difference. Recalculate the step in that case.
const qreal tmpStep = (m_rangeMax - m_rangeMin) / m_segmentCount;
isLoopFinished = m_segmentCount <= maximumSegmentCount && qFuzzyCompare(tmpStep, m_step);
if (!isLoopFinished) {
// Configured step may be invalid, calculating it automatically
calculateStep = true;
}
}
}
void AxisData::calculateSimpleAxisScale()
{
qreal min = 0;
if (m_minValue < 0) {
min = minValue();
}
m_segmentCount = 4;
const int delta = maxValue() - min;
int max = delta;
while (max % segments != 0){
while (max % m_segmentCount != 0){
max++;
}
m_rangeMax = max;
m_step = max / segments;
m_rangeMin = minValue;
m_segmentCount = segments;
m_rangeMax = minValue() + max;
m_step = max / m_segmentCount;
m_rangeMin = minValue();
}
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;
}
AxisData::AxisType AxisData::type() const
{
return m_type;
}
}

View File

@ -1,14 +1,33 @@
#ifndef AXISDATA_H
#define AXISDATA_H
#include <QtGlobal>
#include <QObject>
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:
AxisData();
AxisData(qreal minValue, qreal maxValue);
enum AxisType {
YAxis = 0,
XAxis = 1
};
AxisData(AxisType type, QObject *parent = nullptr);
QString toString() const;
void copy(AxisData *other);
void update();
void update(qreal minValue, qreal maxValue);
void updateForDesignMode();
int segmentCount() const;
@ -21,8 +40,36 @@ public:
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);
AxisType type() const;
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_rangeMax;
@ -31,6 +78,15 @@ private:
qreal m_step;
qreal m_delta;
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;
const AxisType m_type;
};
};

View File

@ -1290,32 +1290,41 @@ void BaseDesignIntf::setItemPos(const QPointF &newPos)
}
QWidget* findRootWidget(QWidget* widget){
QWidget* BaseDesignIntf::findRootWidget(QWidget* widget)
{
while (widget->parentWidget()) {
widget = widget->parentWidget();
}
return widget;
}
void BaseDesignIntf::showEditorDialog(){
QWidget *editor = defaultEditor();
if (editor) {
editor->setStyleSheet(findRootWidget(scene()->views().at(0))->styleSheet());
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();
void BaseDesignIntf::showDialog(QWidget *widget)
{
if (!widget) {
return;
}
widget->setStyleSheet(findRootWidget(scene()->views().at(0))->styleSheet());
QDialog *dialog = new QDialog(QApplication::activeWindow());
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();
dialog->deleteLater();
}
void BaseDesignIntf::showEditorDialog()
{
showDialog(defaultEditor());
}
void BaseDesignIntf::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)

View File

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

View File

@ -0,0 +1,57 @@
#include "lraxispropitem.h"
#include <QToolButton>
#include <lrchartitemeditor.h>
#include <lrpagedesignintf.h>
#include <lrreportengine_p.h>
namespace {
LimeReport::ObjectPropItem * createYAxisPropItem(
QObject *object, LimeReport::ObjectPropItem::ObjectsList* objects, const QString& name, const QString& displayName, const QVariant& data, LimeReport::ObjectPropItem* parent, bool readonly)
{
return new LimeReport::AxisPropItem(object, objects, name, displayName, data, parent, readonly, false);
}
LimeReport::ObjectPropItem * createXAxisPropItem(
QObject *object, LimeReport::ObjectPropItem::ObjectsList* objects, const QString& name, const QString& displayName, const QVariant& data, LimeReport::ObjectPropItem* parent, bool readonly)
{
return new LimeReport::AxisPropItem(object, objects, name, displayName, data, parent, readonly, true);
}
bool VARIABLE_IS_NOT_USED registredXAxisProp = LimeReport::ObjectPropFactory::instance().registerCreator(LimeReport::APropIdent("xAxisSettings", "LimeReport::ChartItem"), QObject::tr("X axis"), createXAxisPropItem);
bool VARIABLE_IS_NOT_USED registredYAxisProp = LimeReport::ObjectPropFactory::instance().registerCreator(LimeReport::APropIdent("yAxisSettings", "LimeReport::ChartItem"), QObject::tr("Y axis"), createYAxisPropItem);
}
namespace LimeReport {
QWidget *AxisPropItem::createProperyEditor(QWidget *parent) const
{
return new AxisPropEditor(qobject_cast<ChartItem*>(object()), m_isXAxis, parent);
}
QString AxisPropItem::displayValue() const
{
return QObject::tr("Axis");
}
AxisPropEditor::AxisPropEditor(ChartItem *chart, bool isXAxis, QWidget *parent)
: QWidget(parent), m_button(new QPushButton(this)), m_chart(chart), m_isXAxis(isXAxis)
{
m_button->setText("...");
QHBoxLayout* layout = new QHBoxLayout(this);
layout->addWidget(m_button);
layout->setSpacing(1);
layout->setContentsMargins(1,0,1,1);
setLayout(layout);
setFocusProxy(m_button);
setAutoFillBackground(true);
connect(m_button,SIGNAL(clicked()),this,SLOT(slotButtonClicked()));
}
void AxisPropEditor::slotButtonClicked()
{
m_chart->showAxisEditorDialog(m_isXAxis);
emit editingFinished();
}
}

View File

@ -0,0 +1,43 @@
#ifndef AXISPROPITEM_H
#define AXISPROPITEM_H
#include <QPushButton>
#include <QHBoxLayout>
#include <lrobjectpropitem.h>
#include <lrchartitem.h>
namespace LimeReport {
class AxisPropEditor : public QWidget
{
Q_OBJECT
public:
AxisPropEditor(ChartItem* chart, bool isXAxis, QWidget *parent = 0);
signals:
void editingFinished();
private slots:
void slotButtonClicked();
private:
QPushButton* m_button;
ChartItem* m_chart;
bool m_isXAxis;
};
class AxisPropItem: public LimeReport::ObjectPropItem
{
Q_OBJECT
public:
AxisPropItem():ObjectPropItem(){}
AxisPropItem(QObject* object, ObjectsList* objects, const QString& name, const QString& displayName, const QVariant& value, ObjectPropItem* parent, bool readonly, bool isXAxis)
:ObjectPropItem(object, objects, name, displayName, value, parent, readonly), m_isXAxis(isXAxis){}
QWidget* createProperyEditor(QWidget *parent) const;
QString displayValue() const;
private:
bool m_isXAxis = false;
};
} // namespace LimeReport
#endif // AXISPROPITEM_H