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

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