1// Copyright (C) 2021 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only
3
4#include <private/chartaxiselement_p.h>
5#include <private/qabstractaxis_p.h>
6#include <private/chartpresenter_p.h>
7#include <private/abstractchartlayout_p.h>
8#include <QtCharts/QCategoryAxis>
9#include <QtCharts/QColorAxis>
10#include <QtCore/QtMath>
11#include <QtCore/QDateTime>
12#include <QtCore/QRegularExpression>
13#include <QtGui/QTextDocument>
14#include <QPainter>
15#include <cmath>
16
17QT_BEGIN_NAMESPACE
18
19static const QRegularExpression &labelFormatMatcher()
20{
21 static const QRegularExpression re(
22 QLatin1String("%[\\-\\+#\\s\\d\\.\\'lhjztL]*([dicuoxfegXFEG])"));
23 return re;
24}
25
26static const QRegularExpression &labelFormatMatcherLocalized()
27{
28 static const QRegularExpression re(QLatin1String("^([^%]*)%\\.(\\d+)([defgiEG])(.*)$"));
29 return re;
30}
31
32ChartAxisElement::ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis)
33 : ChartElement(item),
34 m_axis(axis),
35 m_animation(0),
36 m_grid(new QGraphicsItemGroup(item)),
37 m_arrow(new QGraphicsItemGroup(item)),
38 m_minorGrid(new QGraphicsItemGroup(item)),
39 m_minorArrow(new QGraphicsItemGroup(item)),
40 m_shades(new QGraphicsItemGroup(item)),
41 m_labels(new QGraphicsItemGroup(item)),
42 m_title(new QGraphicsTextItem(item)),
43 m_colorScale(nullptr),
44 m_intervalAxis(intervalAxis)
45
46{
47 //initial initialization
48 m_arrow->setHandlesChildEvents(false);
49 m_arrow->setZValue(ChartPresenter::AxisZValue);
50 m_minorArrow->setHandlesChildEvents(false);
51 m_minorArrow->setZValue(ChartPresenter::AxisZValue);
52 m_labels->setZValue(ChartPresenter::AxisZValue);
53 m_shades->setZValue(ChartPresenter::ShadesZValue);
54 m_grid->setZValue(ChartPresenter::GridZValue);
55 m_minorGrid->setZValue(ChartPresenter::GridZValue);
56 m_title->setZValue(ChartPresenter::GridZValue);
57 m_title->document()->setDocumentMargin(ChartPresenter::textMargin());
58 if (m_axis->type() == QAbstractAxis::AxisTypeColor) {
59 m_colorScale = std::make_unique<QGraphicsPixmapItem>(args: new QGraphicsPixmapItem(item));
60 m_colorScale->setZValue(ChartPresenter::GridZValue);
61 m_colorScale->setVisible(false);
62 }
63
64 handleVisibleChanged(visible: axis->isVisible());
65 connectSlots();
66
67 setFlag(flag: QGraphicsItem::ItemHasNoContents, enabled: true);
68}
69
70ChartAxisElement::~ChartAxisElement()
71{
72}
73
74void ChartAxisElement::connectSlots()
75{
76 QObject::connect(sender: axis(), SIGNAL(visibleChanged(bool)), receiver: this, SLOT(handleVisibleChanged(bool)));
77 QObject::connect(sender: axis(), SIGNAL(lineVisibleChanged(bool)), receiver: this, SLOT(handleArrowVisibleChanged(bool)));
78 QObject::connect(sender: axis(), SIGNAL(gridVisibleChanged(bool)), receiver: this, SLOT(handleGridVisibleChanged(bool)));
79 QObject::connect(sender: axis(), SIGNAL(labelsVisibleChanged(bool)), receiver: this, SLOT(handleLabelsVisibleChanged(bool)));
80 QObject::connect(sender: axis(), SIGNAL(shadesVisibleChanged(bool)), receiver: this, SLOT(handleShadesVisibleChanged(bool)));
81 QObject::connect(sender: axis(), SIGNAL(labelsAngleChanged(int)), receiver: this, SLOT(handleLabelsAngleChanged(int)));
82 QObject::connect(sender: axis(), SIGNAL(linePenChanged(QPen)),
83 receiver: this, SLOT(handleArrowPenChanged(QPen)));
84 QObject::connect(sender: axis(), SIGNAL(labelsBrushChanged(QBrush)),
85 receiver: this, SLOT(handleLabelsBrushChanged(QBrush)));
86 QObject::connect(sender: axis(), SIGNAL(labelsFontChanged(QFont)),
87 receiver: this, SLOT(handleLabelsFontChanged(QFont)));
88 QObject::connect(sender: axis(), SIGNAL(gridLinePenChanged(QPen)),
89 receiver: this, SLOT(handleGridPenChanged(QPen)));
90 QObject::connect(sender: axis(), SIGNAL(shadesPenChanged(QPen)),
91 receiver: this, SLOT(handleShadesPenChanged(QPen)));
92 QObject::connect(sender: axis(), SIGNAL(shadesBrushChanged(QBrush)),
93 receiver: this, SLOT(handleShadesBrushChanged(QBrush)));
94 QObject::connect(sender: axis(), SIGNAL(titleTextChanged(QString)),
95 receiver: this, SLOT(handleTitleTextChanged(QString)));
96 QObject::connect(sender: axis(), SIGNAL(titleFontChanged(QFont)),
97 receiver: this, SLOT(handleTitleFontChanged(QFont)));
98 QObject::connect(sender: axis(), SIGNAL(titleBrushChanged(QBrush)),
99 receiver: this, SLOT(handleTitleBrushChanged(QBrush)));
100 QObject::connect(sender: axis(), SIGNAL(titleVisibleChanged(bool)), receiver: this, SLOT(handleTitleVisibleChanged(bool)));
101 QObject::connect(sender: axis()->d_ptr.get(), SIGNAL(rangeChanged(qreal,qreal)),
102 receiver: this, SLOT(handleRangeChanged(qreal,qreal)));
103 QObject::connect(sender: axis(), SIGNAL(reverseChanged(bool)), receiver: this, SLOT(handleReverseChanged(bool)));
104 QObject::connect(sender: axis(), SIGNAL(lineVisibleChanged(bool)),
105 receiver: this, SLOT(handleMinorArrowVisibleChanged(bool)));
106 QObject::connect(sender: axis(), SIGNAL(linePenChanged(QPen)),
107 receiver: this, SLOT(handleMinorArrowPenChanged(QPen)));
108 QObject::connect(sender: axis(), SIGNAL(minorGridVisibleChanged(bool)),
109 receiver: this, SLOT(handleMinorGridVisibleChanged(bool)));
110 QObject::connect(sender: axis(), SIGNAL(minorGridLinePenChanged(QPen)),
111 receiver: this, SLOT(handleMinorGridPenChanged(QPen)));
112 QObject::connect(sender: axis(), SIGNAL(gridLineColorChanged(QColor)),
113 receiver: this, SLOT(handleGridLineColorChanged(QColor)));
114 QObject::connect(sender: axis(), SIGNAL(minorGridLineColorChanged(QColor)),
115 receiver: this, SLOT(handleMinorGridLineColorChanged(QColor)));
116 QObject::connect(sender: axis(), signal: &QAbstractAxis::truncateLabelsChanged,
117 context: this, slot: &ChartAxisElement::handleTruncateLabelsChanged);
118
119 if (axis()->type() == QAbstractAxis::AxisTypeCategory) {
120 QCategoryAxis *categoryAxis = static_cast<QCategoryAxis *>(axis());
121 QObject::connect(sender: categoryAxis,
122 SIGNAL(labelsPositionChanged(QCategoryAxis::AxisLabelsPosition)),
123 receiver: this, SLOT(handleLabelsPositionChanged()));
124 }
125
126 if (axis()->type() == QAbstractAxis::AxisTypeColor) {
127 QColorAxis *colorAxis = static_cast<QColorAxis *>(axis());
128 QObject::connect(sender: colorAxis, signal: &QColorAxis::sizeChanged, context: this, slot: &ChartAxisElement::handleColorScaleSizeChanged);
129 QObject::connect(sender: colorAxis, signal: &QColorAxis::gradientChanged, context: this, slot: &ChartAxisElement::handleColorScaleGradientChanged);
130 }
131}
132
133void ChartAxisElement::handleArrowVisibleChanged(bool visible)
134{
135 m_arrow->setVisible(visible);
136}
137
138void ChartAxisElement::handleMinorArrowVisibleChanged(bool visible)
139{
140 m_minorArrow->setVisible(visible);
141}
142
143void ChartAxisElement::handleGridVisibleChanged(bool visible)
144{
145 m_grid->setVisible(visible);
146}
147
148void ChartAxisElement::handleMinorGridVisibleChanged(bool visible)
149{
150 m_minorGrid->setVisible(visible);
151}
152
153void ChartAxisElement::handleLabelsPositionChanged()
154{
155 QGraphicsLayoutItem::updateGeometry();
156 presenter()->layout()->invalidate();
157}
158
159void ChartAxisElement::handleTruncateLabelsChanged()
160{
161 QGraphicsLayoutItem::updateGeometry();
162 presenter()->layout()->invalidate();
163}
164
165void ChartAxisElement::handleColorScaleSizeChanged()
166{
167 QGraphicsLayoutItem::updateGeometry();
168}
169
170void ChartAxisElement::handleColorScaleGradientChanged()
171{
172 const QPixmap &pixmap = m_colorScale->pixmap();
173 prepareColorScale(width: pixmap.width(), height: pixmap.height());
174}
175
176void ChartAxisElement::valueLabelEdited(qreal oldValue, qreal newValue)
177{
178 qreal range = max() - min();
179 qreal center = ((max() - min()) / 2.0) + min();
180 qreal newRange = 0.0;
181 auto label = static_cast<ValueAxisLabel *>(this->sender());
182 if ((oldValue >= center && newValue >= min())
183 || (oldValue < center && newValue >= max() && oldValue != min())) {
184 newRange = range * ((newValue - min()) / (oldValue - min()));
185 if (newRange > 0) {
186 m_axis->setRange(min: min(), max: min() + newRange);
187 return;
188 }
189 } else if ((oldValue >= center && newValue <= min() && max() != oldValue)
190 || (oldValue < center && newValue < max())) {
191 newRange = range * ((max() - newValue) / (max() - oldValue));
192 if (newRange > 0) {
193 m_axis->setRange(min: max() - newRange, max: max());
194 return;
195 }
196 }
197 label->reloadBeforeEditContent();
198}
199
200void ChartAxisElement::dateTimeLabelEdited(const QDateTime &oldTime, const QDateTime &newTime)
201{
202 qreal range = max() - min();
203 qreal center = ((max() - min()) / 2.0) + min();
204 qreal newRange = 0.0;
205 qint64 oldValue = oldTime.toMSecsSinceEpoch();
206 qint64 newValue = newTime.toMSecsSinceEpoch();
207 if ((oldValue >= center && newValue >= min())
208 || (oldValue < center && newValue >= max() && oldValue != min())) {
209 newRange = range * ((newValue - min()) / (oldValue - min()));
210 if (newRange > 0) {
211 m_axis->setRange(
212 min: QDateTime::fromMSecsSinceEpoch(msecs: min()),
213 max: QDateTime::fromMSecsSinceEpoch(msecs: min() + newRange));
214 return;
215 }
216 } else if ((oldValue >= center && newValue <= min() && max() != oldValue)
217 || (oldValue < center && newValue < max())) {
218 newRange = range * ((max() - newValue) / (max() - oldValue));
219 if (newRange > 0) {
220 m_axis->setRange(min: max() - newRange, max: max());
221 m_axis->setRange(
222 min: QDateTime::fromMSecsSinceEpoch(msecs: max() - newRange),
223 max: QDateTime::fromMSecsSinceEpoch(msecs: max()));
224 return;
225 }
226 }
227 static_cast<DateTimeAxisLabel *>(this->sender())->reloadBeforeEditContent();
228}
229
230void ChartAxisElement::handleLabelsVisibleChanged(bool visible)
231{
232 QGraphicsLayoutItem::updateGeometry();
233 presenter()->layout()->invalidate();
234 m_labels->setVisible(visible);
235}
236
237void ChartAxisElement::handleShadesVisibleChanged(bool visible)
238{
239 m_shades->setVisible(visible);
240}
241
242void ChartAxisElement::handleTitleVisibleChanged(bool visible)
243{
244 QGraphicsLayoutItem::updateGeometry();
245 presenter()->layout()->invalidate();
246 m_title->setVisible(visible);
247}
248
249void ChartAxisElement::handleLabelsAngleChanged(int angle)
250{
251 const auto items = m_labels->childItems();
252 for (QGraphicsItem *item : items)
253 item->setRotation(angle);
254
255 QGraphicsLayoutItem::updateGeometry();
256 presenter()->layout()->invalidate();
257}
258
259void ChartAxisElement::handleLabelsBrushChanged(const QBrush &brush)
260{
261 const auto items = m_labels->childItems();
262 for (QGraphicsItem *item : items)
263 static_cast<QGraphicsTextItem *>(item)->setDefaultTextColor(brush.color());
264}
265
266void ChartAxisElement::handleLabelsFontChanged(const QFont &font)
267{
268 const auto items = m_labels->childItems();
269 for (QGraphicsItem *item : items)
270 static_cast<QGraphicsTextItem *>(item)->setFont(font);
271 QGraphicsLayoutItem::updateGeometry();
272 presenter()->layout()->invalidate();
273}
274
275void ChartAxisElement::handleTitleTextChanged(const QString &title)
276{
277 QGraphicsLayoutItem::updateGeometry();
278 presenter()->layout()->invalidate();
279 if (title.isEmpty() || !m_title->isVisible())
280 m_title->setHtml(title);
281}
282
283void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush)
284{
285 m_title->setDefaultTextColor(brush.color());
286}
287
288void ChartAxisElement::handleTitleFontChanged(const QFont &font)
289{
290 if (m_title->font() != font) {
291 m_title->setFont(font);
292 QGraphicsLayoutItem::updateGeometry();
293 presenter()->layout()->invalidate();
294 }
295}
296
297void ChartAxisElement::handleVisibleChanged(bool visible)
298{
299 setVisible(visible);
300 if (!visible) {
301 m_grid->setVisible(visible);
302 m_arrow->setVisible(visible);
303 m_minorGrid->setVisible(visible);
304 m_minorArrow->setVisible(visible);
305 m_shades->setVisible(visible);
306 m_labels->setVisible(visible);
307 m_title->setVisible(visible);
308 if (m_colorScale)
309 m_colorScale->setVisible(visible);
310 } else {
311 m_grid->setVisible(axis()->isGridLineVisible());
312 m_arrow->setVisible(axis()->isLineVisible());
313 m_minorGrid->setVisible(axis()->isMinorGridLineVisible());
314 m_minorArrow->setVisible(axis()->isLineVisible());
315 m_shades->setVisible(axis()->shadesVisible());
316 m_labels->setVisible(axis()->labelsVisible());
317 m_title->setVisible(axis()->isTitleVisible());
318 }
319 if (presenter()) {
320 if (visible) {
321 QSizeF before = effectiveSizeHint(which: Qt::PreferredSize);
322 QSizeF after = sizeHint(which: Qt::PreferredSize);
323 if (before != after)
324 QGraphicsLayoutItem::updateGeometry();
325 }
326 presenter()->layout()->invalidate();
327 }
328}
329
330void ChartAxisElement::handleRangeChanged(qreal min, qreal max)
331{
332 Q_UNUSED(min);
333 Q_UNUSED(max);
334
335 if (!emptyAxis()) {
336 const QList<qreal> layout = calculateLayout();
337 updateLayout(layout);
338 QSizeF before = effectiveSizeHint(which: Qt::PreferredSize);
339 QSizeF after = sizeHint(which: Qt::PreferredSize);
340
341 if (before != after) {
342 QGraphicsLayoutItem::updateGeometry();
343 // We don't want to call invalidate on layout, since it will change minimum size of
344 // component, which we would like to avoid since it causes nasty flips when scrolling
345 // or zooming, instead recalculate layout and use plotArea for extra space.
346 presenter()->layout()->setGeometry(presenter()->layout()->geometry());
347 }
348 }
349}
350
351void ChartAxisElement::handleReverseChanged(bool reverse)
352{
353 Q_UNUSED(reverse);
354
355 QGraphicsLayoutItem::updateGeometry();
356 presenter()->layout()->invalidate();
357}
358
359bool ChartAxisElement::emptyAxis() const
360{
361 return axisGeometry().isEmpty()
362 || gridGeometry().isEmpty()
363 || qFuzzyIsNull(d: max() - min());
364}
365
366qreal ChartAxisElement::min() const
367{
368 return m_axis->d_ptr->min();
369}
370
371qreal ChartAxisElement::max() const
372{
373 return m_axis->d_ptr->max();
374}
375
376qreal ChartAxisElement::tickInterval() const
377{
378 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: m_axis);
379 if (valueAxis)
380 return valueAxis->tickInterval();
381 else
382 return 0.0;
383}
384
385qreal ChartAxisElement::tickAnchor() const
386{
387 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: m_axis);
388 if (valueAxis)
389 return valueAxis->tickAnchor();
390 else
391 return 0.0;
392}
393
394QString ChartAxisElement::formatLabel(const QString &formatSpec, const QByteArray &array,
395 qreal value, int precision, const QString &preStr,
396 const QString &postStr) const
397{
398 QString retVal;
399 if (!formatSpec.isEmpty()) {
400 if (formatSpec.at(i: 0) == QLatin1Char('d')
401 || formatSpec.at(i: 0) == QLatin1Char('i')
402 || formatSpec.at(i: 0) == QLatin1Char('c')) {
403 if (presenter()->localizeNumbers())
404 retVal = preStr + presenter()->locale().toString(i: qint64(value)) + postStr;
405 else
406 retVal = QString::asprintf(format: array.constData(), qint64(value));
407 } else if (formatSpec.at(i: 0) == QLatin1Char('u')
408 || formatSpec.at(i: 0) == QLatin1Char('o')
409 || formatSpec.at(i: 0) == QLatin1Char('x')
410 || formatSpec.at(i: 0) == QLatin1Char('X')) {
411 // These formats are not supported by localized numbers
412 retVal = QString::asprintf(format: array.constData(), quint64(value));
413 } else if (formatSpec.at(i: 0) == QLatin1Char('f')
414 || formatSpec.at(i: 0) == QLatin1Char('F')
415 || formatSpec.at(i: 0) == QLatin1Char('e')
416 || formatSpec.at(i: 0) == QLatin1Char('E')
417 || formatSpec.at(i: 0) == QLatin1Char('g')
418 || formatSpec.at(i: 0) == QLatin1Char('G')) {
419 if (presenter()->localizeNumbers()) {
420 retVal = preStr
421 + presenter()->locale().toString(f: value, format: formatSpec.at(i: 0).toLatin1(),
422 precision)
423 + postStr;
424 } else {
425 retVal = QString::asprintf(format: array.constData(), value);
426 }
427 }
428 }
429 return retVal;
430}
431
432void ChartAxisElement::prepareColorScale(const qreal width, const qreal height)
433{
434 if (axis()->type() == QAbstractAxis::AxisTypeColor) {
435 QColorAxis *colorAxis = static_cast<QColorAxis *>(axis());
436
437 if (colorAxis->gradient() != QLinearGradient() && width != 0 && height != 0) {
438 m_colorScale->setVisible(true);
439
440 QImage image(width, height, QImage::Format_ARGB32);
441 QPainter painter(&image);
442
443 QLinearGradient gradient;
444 if (colorAxis->orientation() == Qt::Horizontal) {
445 gradient = QLinearGradient(QPointF(0, 0), QPointF(width, 0));
446 const auto &stops = colorAxis->gradient().stops();
447 for (const auto &stop : stops)
448 gradient.setColorAt(pos: stop.first, color: stop.second);
449 } else {
450 gradient = QLinearGradient(QPointF(0, 0), QPointF(0, height));
451 for (int i = colorAxis->gradient().stops().size() - 1; i >= 0; --i) {
452 auto stop = colorAxis->gradient().stops()[i];
453 gradient.setColorAt(pos: 1 - stop.first, color: stop.second);
454 }
455 }
456
457 painter.fillRect(image.rect(), gradient);
458
459 painter.setPen(axis()->linePen());
460 painter.drawRect(r: image.rect());
461
462 const QPixmap &pixmap = QPixmap::fromImage(image);
463 m_colorScale->setPixmap(pixmap);
464 }
465 }
466}
467
468static int precisionDigits(qreal min, qreal max, int ticks)
469{
470 // How many digits of each tick value should we display after the decimal point ?
471 // For example tick marks 1.002 and 1.003 have a difference of 0.001 and need 3 decimals.
472 if (ticks > 1) {
473 // Number of digits after decimal that *don't* change between ticks:
474 const int gap = -qFloor(v: std::log10(x: (max - min) / (ticks - 1)));
475 if (gap > 0)
476 return gap + 1;
477 }
478 // We want at least one digit after the decimal point even when digits
479 // before are changing, or when we only have a single tick-mark:
480 return 1;
481}
482
483QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks,
484 qreal tickInterval, qreal tickAnchor,
485 QValueAxis::TickType tickType,
486 const QString &format) const
487{
488 QStringList labels;
489
490 if (max <= min || ticks < 1)
491 return labels;
492
493 if (format.isEmpty()) {
494 const int n = precisionDigits(min, max, ticks);
495 if (tickType == QValueAxis::TicksFixed) {
496 for (int i = 0; i < ticks; i++) {
497 qreal value = min + (i * (max - min) / (ticks - 1));
498 labels << presenter()->numberToString(value, f: 'f', prec: n);
499 }
500 } else {
501 const qreal ticksFromAnchor = (tickAnchor - min) / tickInterval;
502 const qreal firstMajorTick = tickAnchor - std::floor(x: ticksFromAnchor) * tickInterval;
503
504 qreal value = firstMajorTick;
505 while (value <= max) {
506 labels << presenter()->numberToString(value, f: 'f', prec: n);
507 value += tickInterval;
508 }
509 }
510 } else {
511 QByteArray array = format.toLatin1();
512 QString formatSpec;
513 QString preStr;
514 QString postStr;
515 int precision = 6; // Six is the default precision in Qt API
516 if (presenter()->localizeNumbers()) {
517 QRegularExpressionMatch rmatch;
518 if (format.indexOf(re: labelFormatMatcherLocalized(), from: 0, rmatch: &rmatch) != -1) {
519 preStr = rmatch.captured(nth: 1);
520 if (!rmatch.captured(nth: 2).isEmpty())
521 precision = rmatch.captured(nth: 2).toInt();
522 formatSpec = rmatch.captured(nth: 3);
523 postStr = rmatch.captured(nth: 4);
524 }
525 } else {
526 QRegularExpressionMatch rmatch;
527 if (format.indexOf(re: labelFormatMatcher(), from: 0, rmatch: &rmatch) != -1)
528 formatSpec = rmatch.captured(nth: 1);
529 }
530 if (tickType == QValueAxis::TicksFixed) {
531 for (int i = 0; i < ticks; i++) {
532 qreal value = min + (i * (max - min) / (ticks - 1));
533 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
534 }
535 } else {
536 const qreal ticksFromAnchor = (tickAnchor - min) / tickInterval;
537 const qreal firstMajorTick = tickAnchor - std::floor(x: ticksFromAnchor) * tickInterval;
538
539 qreal value = firstMajorTick;
540 while (value <= max) {
541 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
542 value += tickInterval;
543 }
544 }
545 }
546
547 return labels;
548}
549
550QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks,
551 const QString &format) const
552{
553 QStringList labels;
554
555 if (max <= min || ticks < 1)
556 return labels;
557
558 const int firstTick = qCeil(v: qLn(v: base > 1 ? min : max) / qLn(v: base));
559 if (format.isEmpty()) {
560 const int n = precisionDigits(min, max, ticks);
561 for (int i = firstTick; i < ticks + firstTick; i++) {
562 qreal value = qPow(x: base, y: i);
563 labels << presenter()->numberToString(value, f: 'f', prec: n);
564 }
565 } else {
566 QByteArray array = format.toLatin1();
567 QString formatSpec;
568 QString preStr;
569 QString postStr;
570 int precision = 6; // Six is the default precision in Qt API
571 if (presenter()->localizeNumbers()) {
572 QRegularExpressionMatch rmatch;
573 if (format.indexOf(re: labelFormatMatcherLocalized(), from: 0, rmatch: &rmatch) != -1) {
574 preStr = rmatch.captured(nth: 1);
575 if (!rmatch.captured(nth: 2).isEmpty())
576 precision = rmatch.captured(nth: 2).toInt();
577 formatSpec = rmatch.captured(nth: 3);
578 postStr = rmatch.captured(nth: 4);
579 }
580 } else {
581 QRegularExpressionMatch rmatch;
582 if (format.indexOf(re: labelFormatMatcher(), from: 0, rmatch: &rmatch) != -1)
583 formatSpec = rmatch.captured(nth: 1);
584 }
585 for (int i = firstTick; i < ticks + firstTick; i++) {
586 qreal value = qPow(x: base, y: i);
587 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
588 }
589 }
590
591 return labels;
592}
593
594QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks,
595 const QString &format) const
596{
597 QStringList labels;
598
599 if (max <= min || ticks < 1)
600 return labels;
601
602 for (int i = 0; i < ticks; i++) {
603 qreal value = min + (i * (max - min) / (ticks - 1));
604 labels << presenter()->locale().toString(dateTime: QDateTime::fromMSecsSinceEpoch(msecs: value), format);
605 }
606 return labels;
607}
608
609QStringList ChartAxisElement::createColorLabels(qreal min, qreal max, int ticks) const
610{
611 QStringList labels;
612
613 if (max <= min || ticks < 1)
614 return labels;
615
616 const int n = precisionDigits(min, max, ticks);
617 for (int i = 0; i < ticks; ++i) {
618 qreal value = min + (i * (max - min) / (ticks - 1));
619 labels << presenter()->numberToString(value, f: 'f', prec: n);
620 }
621
622 return labels;
623}
624
625bool ChartAxisElement::labelsEditable() const
626{
627 return m_labelsEditable;
628}
629
630void ChartAxisElement::setLabelsEditable(bool labelsEditable)
631{
632 if (axis()->type() == QAbstractAxis::AxisTypeValue
633 || axis()->type() == QAbstractAxis::AxisTypeDateTime) {
634 labelGroup()->setHandlesChildEvents(!labelsEditable);
635 const QList<QGraphicsItem *> childItems = labelGroup()->childItems();
636 for (auto item : childItems) {
637 switch (axis()->type()) {
638 case QAbstractAxis::AxisTypeValue:
639 static_cast<ValueAxisLabel *>(item)->setEditable(labelsEditable);
640 break;
641 case QAbstractAxis::AxisTypeDateTime:
642 static_cast<DateTimeAxisLabel *>(item)->setEditable(labelsEditable);
643 break;
644 default:
645 break;
646 }
647 }
648 m_labelsEditable = labelsEditable;
649 }
650}
651
652bool ChartAxisElement::labelsVisible() const
653{
654 return m_labels->isVisible();
655}
656
657void ChartAxisElement::axisSelected()
658{
659 emit clicked();
660}
661
662QT_END_NAMESPACE
663
664#include "moc_chartaxiselement_p.cpp"
665

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