1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Charts module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtCharts/qabstractaxis.h>
31#include <QtCharts/qlogvalueaxis.h>
32#include <QtCharts/qvalueaxis.h>
33#include <QtCore/qmath.h>
34#include <QtGui/qtextdocument.h>
35#include <QtWidgets/qgraphicslayout.h>
36#include <private/abstractchartlayout_p.h>
37#include <private/abstractdomain_p.h>
38#include <private/cartesianchartaxis_p.h>
39#include <private/chartpresenter_p.h>
40#include <private/linearrowitem_p.h>
41#include <private/qabstractaxis_p.h>
42
43QT_CHARTS_BEGIN_NAMESPACE
44
45CartesianChartAxis::CartesianChartAxis(QAbstractAxis *axis, QGraphicsItem *item , bool intervalAxis)
46 : ChartAxisElement(axis, item, intervalAxis)
47{
48 Q_ASSERT(item);
49}
50
51
52CartesianChartAxis::~CartesianChartAxis()
53{
54}
55
56void CartesianChartAxis::createItems(int count)
57{
58 if (arrowItems().size() == 0) {
59 QGraphicsLineItem *arrow = new LineArrowItem(this, this);
60 arrow->setPen(axis()->linePen());
61 arrowGroup()->addToGroup(item: arrow);
62 }
63
64 if (intervalAxis() && gridItems().size() == 0) {
65 for (int i = 0 ; i < 2 ; i ++){
66 QGraphicsLineItem *item = new QGraphicsLineItem(this);
67 item->setPen(axis()->gridLinePen());
68 gridGroup()->addToGroup(item);
69 QGraphicsRectItem *shades = new QGraphicsRectItem(this);
70 shades->setPen(axis()->shadesPen());
71 shades->setBrush(axis()->shadesBrush());
72 shadeGroup()->addToGroup(item: shades);
73 }
74 }
75
76 QGraphicsTextItem *title = titleItem();
77 title->setFont(axis()->titleFont());
78 title->setDefaultTextColor(axis()->titleBrush().color());
79 title->setHtml(axis()->titleText());
80
81 for (int i = 0; i < count; ++i) {
82 QGraphicsLineItem *arrow = new QGraphicsLineItem(this);
83 QGraphicsLineItem *grid = new QGraphicsLineItem(this);
84 QGraphicsTextItem *label;
85 if (axis()->type() == QtCharts::QAbstractAxis::AxisTypeValue) {
86 label = new ValueAxisLabel(this);
87 connect(sender: static_cast<ValueAxisLabel *>(label), signal: &ValueAxisLabel::valueChanged,
88 receiver: this, slot: &ChartAxisElement::valueLabelEdited);
89 if (labelsEditable())
90 static_cast<ValueAxisLabel *>(label)->setEditable(true);
91 } else if (axis()->type() == QtCharts::QAbstractAxis::AxisTypeDateTime) {
92 DateTimeAxisLabel *dateTimeLabel = new DateTimeAxisLabel(this);
93 label = dateTimeLabel;
94 connect(sender: dateTimeLabel, signal: &DateTimeAxisLabel::dateTimeChanged,
95 receiver: this, slot: &ChartAxisElement::dateTimeLabelEdited);
96 if (labelsEditable())
97 dateTimeLabel->setEditable(true);
98 dateTimeLabel->setFormat(static_cast<QDateTimeAxis*>(axis())->format());
99 } else {
100 label = new QGraphicsTextItem(this);
101 }
102
103 label->document()->setDocumentMargin(ChartPresenter::textMargin());
104 arrow->setPen(axis()->linePen());
105 grid->setPen(axis()->gridLinePen());
106 label->setFont(axis()->labelsFont());
107 label->setDefaultTextColor(axis()->labelsBrush().color());
108 label->setRotation(axis()->labelsAngle());
109 arrowGroup()->addToGroup(item: arrow);
110 gridGroup()->addToGroup(item: grid);
111 labelGroup()->addToGroup(item: label);
112
113 if (gridItems().size() == 1 || (((gridItems().size() + 1) % 2) && gridItems().size() > 0)) {
114 QGraphicsRectItem *shades = new QGraphicsRectItem(this);
115 shades->setPen(axis()->shadesPen());
116 shades->setBrush(axis()->shadesBrush());
117 shadeGroup()->addToGroup(item: shades);
118 }
119 }
120}
121
122void CartesianChartAxis::updateMinorTickItems()
123{
124 int currentCount = minorArrowItems().size();
125 int expectedCount = 0;
126 if (axis()->type() == QAbstractAxis::AxisTypeValue) {
127 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: axis());
128 if (valueAxis->tickType() == QValueAxis::TicksFixed) {
129 expectedCount = valueAxis->minorTickCount() * (valueAxis->tickCount() - 1);
130 expectedCount = qMax(a: expectedCount, b: 0);
131 } else {
132 const qreal interval = valueAxis->tickInterval();
133 qreal firstMajorTick = valueAxis->tickAnchor();
134 const qreal max = valueAxis->max();
135 const qreal min = valueAxis->min();
136 const int _minorTickCount = valueAxis->minorTickCount();
137
138 if (min < firstMajorTick)
139 firstMajorTick = firstMajorTick - qCeil(v: (firstMajorTick - min) / interval) * interval;
140 else
141 firstMajorTick = firstMajorTick + int((min - firstMajorTick) / interval) * interval;
142
143 const qreal deltaMinor = interval / qreal(_minorTickCount + 1);
144 qreal minorTick = firstMajorTick + deltaMinor;
145 int minorCounter = 0;
146
147 while (minorTick < min) {
148 minorTick += deltaMinor;
149 minorCounter++;
150 }
151
152 QVector<qreal> points;
153
154 // Calculate the points on axis value space. Conversion to graphical points
155 // will be done on axis specific geometry update function
156 while (minorTick <= max || qFuzzyCompare(p1: minorTick, p2: max)) {
157 if (minorCounter < _minorTickCount) {
158 expectedCount++;
159 minorCounter++;
160 points << (minorTick - min);
161 } else {
162 minorCounter = 0;
163 }
164 minorTick += deltaMinor;
165 }
166
167 setDynamicMinorTickLayout(points);
168 }
169 } else if (axis()->type() == QAbstractAxis::AxisTypeLogValue) {
170 QLogValueAxis *logValueAxis = qobject_cast<QLogValueAxis *>(object: axis());
171
172 int minorTickCount = logValueAxis->minorTickCount();
173 if (minorTickCount < 0)
174 minorTickCount = qMax(a: int(qFloor(v: logValueAxis->base()) - 2.0), b: 0);
175
176 expectedCount = minorTickCount * (logValueAxis->tickCount() + 1);
177 expectedCount = qMax(a: expectedCount, b: logValueAxis->minorTickCount());
178 } else {
179 // minor ticks are not supported
180 return;
181 }
182
183 int diff = expectedCount - currentCount;
184 if (diff > 0) {
185 for (int i = 0; i < diff; ++i) {
186 QGraphicsLineItem *minorGridLineItem = new QGraphicsLineItem(this);
187 minorGridLineItem->setPen(axis()->minorGridLinePen());
188 minorGridGroup()->addToGroup(item: minorGridLineItem);
189
190 QGraphicsLineItem *minorArrowLineItem = new QGraphicsLineItem(this);
191 minorArrowLineItem->setPen(axis()->linePen());
192 minorArrowGroup()->addToGroup(item: minorArrowLineItem);
193 }
194 } else {
195 QList<QGraphicsItem *> minorGridItemsList = minorGridItems();
196 QList<QGraphicsItem *> minorArrowItemsList = minorArrowItems();
197 for (int i = 0; i > diff; --i) {
198 if (!minorGridItemsList.isEmpty())
199 delete minorGridItemsList.takeLast();
200
201 if (!minorArrowItemsList.isEmpty())
202 delete minorArrowItemsList.takeLast();
203 }
204 }
205}
206
207void CartesianChartAxis::deleteItems(int count)
208{
209 QList<QGraphicsItem *> lines = gridItems();
210 QList<QGraphicsItem *> labels = labelItems();
211 QList<QGraphicsItem *> shades = shadeItems();
212 QList<QGraphicsItem *> axis = arrowItems();
213
214 for (int i = 0; i < count; ++i) {
215 if (lines.size() == 1 || (((lines.size() + 1) % 2) && lines.size() > 0))
216 delete(shades.takeLast());
217 delete(lines.takeLast());
218 delete(labels.takeLast());
219 delete(axis.takeLast());
220 }
221}
222
223void CartesianChartAxis::updateLayout(QVector<qreal> &layout)
224{
225 int diff = ChartAxisElement::layout().size() - layout.size();
226
227 if (diff > 0)
228 deleteItems(count: diff);
229 else if (diff <= 0)
230 createItems(count: -diff);
231
232 updateMinorTickItems();
233
234 if (animation()) {
235 switch (presenter()->state()) {
236 case ChartPresenter::ZoomInState:
237 animation()->setAnimationType(AxisAnimation::ZoomInAnimation);
238 animation()->setAnimationPoint(presenter()->statePoint());
239 break;
240 case ChartPresenter::ZoomOutState:
241 animation()->setAnimationType(AxisAnimation::ZoomOutAnimation);
242 animation()->setAnimationPoint(presenter()->statePoint());
243 break;
244 case ChartPresenter::ScrollUpState:
245 case ChartPresenter::ScrollLeftState:
246 animation()->setAnimationType(AxisAnimation::MoveBackwordAnimation);
247 break;
248 case ChartPresenter::ScrollDownState:
249 case ChartPresenter::ScrollRightState:
250 animation()->setAnimationType(AxisAnimation::MoveForwardAnimation);
251 break;
252 case ChartPresenter::ShowState:
253 animation()->setAnimationType(AxisAnimation::DefaultAnimation);
254 break;
255 }
256 animation()->setValues(oldLayout&: ChartAxisElement::layout(), newLayout&: layout);
257 presenter()->startAnimation(animation: animation());
258 } else {
259 setLayout(layout);
260 updateGeometry();
261 }
262}
263
264bool CartesianChartAxis::isEmpty()
265{
266 return axisGeometry().isEmpty()
267 || gridGeometry().isEmpty()
268 || qFuzzyCompare(p1: min(), p2: max());
269}
270
271void CartesianChartAxis::setGeometry(const QRectF &axis, const QRectF &grid)
272{
273 m_gridRect = grid;
274 setAxisGeometry(axis);
275
276 if (isEmpty()) {
277 prepareGeometryChange();
278 return;
279 }
280
281 QVector<qreal> layout = calculateLayout();
282 updateLayout(layout);
283}
284
285QSizeF CartesianChartAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
286{
287 Q_UNUSED(which);
288 Q_UNUSED(constraint);
289 return QSizeF();
290}
291
292void CartesianChartAxis::setDateTimeLabelsFormat(const QString &format)
293{
294 if (max() <= min()
295 || layout().size() < 1
296 || axis()->type() != QAbstractAxis::AxisTypeDateTime) {
297 return;
298 }
299
300 for (int i = 0; i < layout().size(); i++)
301 static_cast<DateTimeAxisLabel *>(labelItems().at(i))->setFormat(format);
302}
303
304void CartesianChartAxis::handleArrowPenChanged(const QPen &pen)
305{
306 foreach (QGraphicsItem *item, arrowItems())
307 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
308}
309
310void CartesianChartAxis::handleGridPenChanged(const QPen &pen)
311{
312 foreach (QGraphicsItem *item, gridItems())
313 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
314}
315
316void CartesianChartAxis::handleMinorArrowPenChanged(const QPen &pen)
317{
318 foreach (QGraphicsItem *item, minorArrowItems())
319 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
320}
321
322void CartesianChartAxis::handleMinorGridPenChanged(const QPen &pen)
323{
324 foreach (QGraphicsItem *item, minorGridItems())
325 static_cast<QGraphicsLineItem *>(item)->setPen(pen);
326}
327
328void CartesianChartAxis::handleGridLineColorChanged(const QColor &color)
329{
330 foreach (QGraphicsItem *item, gridItems()) {
331 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
332 QPen pen = lineItem->pen();
333 pen.setColor(color);
334 lineItem->setPen(pen);
335 }
336}
337
338void CartesianChartAxis::handleMinorGridLineColorChanged(const QColor &color)
339{
340 foreach (QGraphicsItem *item, minorGridItems()) {
341 QGraphicsLineItem *lineItem = static_cast<QGraphicsLineItem *>(item);
342 QPen pen = lineItem->pen();
343 pen.setColor(color);
344 lineItem->setPen(pen);
345 }
346}
347
348void CartesianChartAxis::handleShadesBrushChanged(const QBrush &brush)
349{
350 foreach (QGraphicsItem *item, shadeItems())
351 static_cast<QGraphicsRectItem *>(item)->setBrush(brush);
352}
353
354void CartesianChartAxis::handleShadesPenChanged(const QPen &pen)
355{
356 foreach (QGraphicsItem *item, shadeItems())
357 static_cast<QGraphicsRectItem *>(item)->setPen(pen);
358}
359
360void CartesianChartAxis::updateLabelsValues(QValueAxis *axis)
361{
362 const QVector<qreal> &layout = ChartAxisElement::layout();
363 if (layout.isEmpty())
364 return;
365
366 if (axis->tickType() == QValueAxis::TicksFixed) {
367 for (int i = 0; i < layout.size(); ++i) {
368 qreal value = axis->isReverse()
369 ? min() + ((layout.size() - 1 - i) * (max() - min()) / (layout.size() - 1))
370 : min() + (i * (max() - min()) / (layout.size() - 1));
371 static_cast<ValueAxisLabel *>(labelItems().at(i))->setValue(value);
372 }
373 } else {
374 qreal value = axis->tickAnchor();
375 if (value > min())
376 value = value - int((value - min()) / axis->tickInterval()) * axis->tickInterval();
377 else
378 value = value + qCeil(v: (min() - value) / axis->tickInterval()) * axis->tickInterval();
379
380 int i = axis->isReverse() ? labelItems().count()-1 : 0;
381 while (value <= max() || qFuzzyCompare(p1: value, p2: max())) {
382 static_cast<ValueAxisLabel *>(labelItems().at(i))->setValue(value);
383 value += axis->tickInterval();
384 i += axis->isReverse() ? -1 : 1;
385 }
386 }
387}
388
389void CartesianChartAxis::updateLabelsDateTimes()
390{
391 if (max() <= min() || layout().size() < 1)
392 return;
393
394 for (int i = 0; i < layout().size(); i++) {
395 qreal value = min() + (i * (max() - min()) / (layout().size() - 1));
396 static_cast<DateTimeAxisLabel *>(labelItems().at(i))->setValue(
397 QDateTime::fromMSecsSinceEpoch(msecs: value));
398 }
399}
400
401QT_CHARTS_END_NAMESPACE
402
403#include "moc_cartesianchartaxis_p.cpp"
404

source code of qtcharts/src/charts/axis/cartesianchartaxis.cpp