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 const auto items = m_labels->childItems();
242 for (QGraphicsItem *item : items)
243 item->setRotation(angle);
244
245 QGraphicsLayoutItem::updateGeometry();
246 presenter()->layout()->invalidate();
247}
248
249void ChartAxisElement::handleLabelsBrushChanged(const QBrush &brush)
250{
251 const auto items = m_labels->childItems();
252 for (QGraphicsItem *item : items)
253 static_cast<QGraphicsTextItem *>(item)->setDefaultTextColor(brush.color());
254}
255
256void ChartAxisElement::handleLabelsFontChanged(const QFont &font)
257{
258 const auto items = m_labels->childItems();
259 for (QGraphicsItem *item : items)
260 static_cast<QGraphicsTextItem *>(item)->setFont(font);
261 QGraphicsLayoutItem::updateGeometry();
262 presenter()->layout()->invalidate();
263}
264
265void ChartAxisElement::handleTitleTextChanged(const QString &title)
266{
267 QGraphicsLayoutItem::updateGeometry();
268 presenter()->layout()->invalidate();
269 if (title.isEmpty() || !m_title->isVisible())
270 m_title->setHtml(title);
271}
272
273void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush)
274{
275 m_title->setDefaultTextColor(brush.color());
276}
277
278void ChartAxisElement::handleTitleFontChanged(const QFont &font)
279{
280 if (m_title->font() != font) {
281 m_title->setFont(font);
282 QGraphicsLayoutItem::updateGeometry();
283 presenter()->layout()->invalidate();
284 }
285}
286
287void ChartAxisElement::handleVisibleChanged(bool visible)
288{
289 setVisible(visible);
290 if (!visible) {
291 m_grid->setVisible(visible);
292 m_arrow->setVisible(visible);
293 m_minorGrid->setVisible(visible);
294 m_minorArrow->setVisible(visible);
295 m_shades->setVisible(visible);
296 m_labels->setVisible(visible);
297 m_title->setVisible(visible);
298 if (m_colorScale)
299 m_colorScale->setVisible(visible);
300 } else {
301 m_grid->setVisible(axis()->isGridLineVisible());
302 m_arrow->setVisible(axis()->isLineVisible());
303 m_minorGrid->setVisible(axis()->isMinorGridLineVisible());
304 m_minorArrow->setVisible(axis()->isLineVisible());
305 m_shades->setVisible(axis()->shadesVisible());
306 m_labels->setVisible(axis()->labelsVisible());
307 m_title->setVisible(axis()->isTitleVisible());
308 }
309 if (presenter()) {
310 if (visible) {
311 QSizeF before = effectiveSizeHint(which: Qt::PreferredSize);
312 QSizeF after = sizeHint(which: Qt::PreferredSize);
313 if (before != after)
314 QGraphicsLayoutItem::updateGeometry();
315 }
316 presenter()->layout()->invalidate();
317 }
318}
319
320void ChartAxisElement::handleRangeChanged(qreal min, qreal max)
321{
322 Q_UNUSED(min);
323 Q_UNUSED(max);
324
325 if (!emptyAxis()) {
326 const QList<qreal> layout = calculateLayout();
327 updateLayout(layout);
328 QSizeF before = effectiveSizeHint(which: Qt::PreferredSize);
329 QSizeF after = sizeHint(which: Qt::PreferredSize);
330
331 if (before != after) {
332 QGraphicsLayoutItem::updateGeometry();
333 // We don't want to call invalidate on layout, since it will change minimum size of
334 // component, which we would like to avoid since it causes nasty flips when scrolling
335 // or zooming, instead recalculate layout and use plotArea for extra space.
336 presenter()->layout()->setGeometry(presenter()->layout()->geometry());
337 }
338 }
339}
340
341void ChartAxisElement::handleReverseChanged(bool reverse)
342{
343 Q_UNUSED(reverse);
344
345 QGraphicsLayoutItem::updateGeometry();
346 presenter()->layout()->invalidate();
347}
348
349bool ChartAxisElement::emptyAxis() const
350{
351 return axisGeometry().isEmpty()
352 || gridGeometry().isEmpty()
353 || qFuzzyIsNull(d: max() - min());
354}
355
356qreal ChartAxisElement::min() const
357{
358 return m_axis->d_ptr->min();
359}
360
361qreal ChartAxisElement::max() const
362{
363 return m_axis->d_ptr->max();
364}
365
366qreal ChartAxisElement::tickInterval() const
367{
368 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: m_axis);
369 if (valueAxis)
370 return valueAxis->tickInterval();
371 else
372 return 0.0;
373}
374
375qreal ChartAxisElement::tickAnchor() const
376{
377 QValueAxis *valueAxis = qobject_cast<QValueAxis *>(object: m_axis);
378 if (valueAxis)
379 return valueAxis->tickAnchor();
380 else
381 return 0.0;
382}
383
384QString ChartAxisElement::formatLabel(const QString &formatSpec, const QByteArray &array,
385 qreal value, int precision, const QString &preStr,
386 const QString &postStr) const
387{
388 QString retVal;
389 if (!formatSpec.isEmpty()) {
390 if (formatSpec.at(i: 0) == QLatin1Char('d')
391 || formatSpec.at(i: 0) == QLatin1Char('i')
392 || formatSpec.at(i: 0) == QLatin1Char('c')) {
393 if (presenter()->localizeNumbers())
394 retVal = preStr + presenter()->locale().toString(i: qint64(value)) + postStr;
395 else
396 retVal = QString::asprintf(format: array.constData(), qint64(value));
397 } else if (formatSpec.at(i: 0) == QLatin1Char('u')
398 || formatSpec.at(i: 0) == QLatin1Char('o')
399 || formatSpec.at(i: 0) == QLatin1Char('x')
400 || formatSpec.at(i: 0) == QLatin1Char('X')) {
401 // These formats are not supported by localized numbers
402 retVal = QString::asprintf(format: array.constData(), quint64(value));
403 } else if (formatSpec.at(i: 0) == QLatin1Char('f')
404 || formatSpec.at(i: 0) == QLatin1Char('F')
405 || formatSpec.at(i: 0) == QLatin1Char('e')
406 || formatSpec.at(i: 0) == QLatin1Char('E')
407 || formatSpec.at(i: 0) == QLatin1Char('g')
408 || formatSpec.at(i: 0) == QLatin1Char('G')) {
409 if (presenter()->localizeNumbers()) {
410 retVal = preStr
411 + presenter()->locale().toString(f: value, format: formatSpec.at(i: 0).toLatin1(),
412 precision)
413 + postStr;
414 } else {
415 retVal = QString::asprintf(format: array.constData(), value);
416 }
417 }
418 }
419 return retVal;
420}
421
422void ChartAxisElement::prepareColorScale(const qreal width, const qreal height)
423{
424 if (axis()->type() == QAbstractAxis::AxisTypeColor) {
425 QColorAxis *colorAxis = static_cast<QColorAxis *>(axis());
426
427 if (colorAxis->gradient() != QLinearGradient() && width != 0 && height != 0) {
428 m_colorScale->setVisible(true);
429
430 QImage image(width, height, QImage::Format_ARGB32);
431 QPainter painter(&image);
432
433 QLinearGradient gradient;
434 if (colorAxis->orientation() == Qt::Horizontal) {
435 gradient = QLinearGradient(QPointF(0, 0), QPointF(width, 0));
436 const auto &stops = colorAxis->gradient().stops();
437 for (const auto &stop : stops)
438 gradient.setColorAt(pos: stop.first, color: stop.second);
439 } else {
440 gradient = QLinearGradient(QPointF(0, 0), QPointF(0, height));
441 for (int i = colorAxis->gradient().stops().size() - 1; i >= 0; --i) {
442 auto stop = colorAxis->gradient().stops()[i];
443 gradient.setColorAt(pos: 1 - stop.first, color: stop.second);
444 }
445 }
446
447 painter.fillRect(image.rect(), gradient);
448
449 painter.setPen(axis()->linePen());
450 painter.drawRect(r: image.rect());
451
452 const QPixmap &pixmap = QPixmap::fromImage(image);
453 m_colorScale->setPixmap(pixmap);
454 }
455 }
456}
457
458static int precisionDigits(qreal min, qreal max, int ticks)
459{
460 // How many digits of each tick value should we display after the decimal point ?
461 // For example tick marks 1.002 and 1.003 have a difference of 0.001 and need 3 decimals.
462 if (ticks > 1) {
463 // Number of digits after decimal that *don't* change between ticks:
464 const int gap = -qFloor(v: std::log10(x: (max - min) / (ticks - 1)));
465 if (gap > 0)
466 return gap + 1;
467 }
468 // We want at least one digit after the decimal point even when digits
469 // before are changing, or when we only have a single tick-mark:
470 return 1;
471}
472
473QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks,
474 qreal tickInterval, qreal tickAnchor,
475 QValueAxis::TickType tickType,
476 const QString &format) const
477{
478 QStringList labels;
479
480 if (max <= min || ticks < 1)
481 return labels;
482
483 if (format.isEmpty()) {
484 const int n = precisionDigits(min, max, ticks);
485 if (tickType == QValueAxis::TicksFixed) {
486 for (int i = 0; i < ticks; i++) {
487 qreal value = min + (i * (max - min) / (ticks - 1));
488 labels << presenter()->numberToString(value, f: 'f', prec: n);
489 }
490 } else {
491 const qreal ticksFromAnchor = (tickAnchor - min) / tickInterval;
492 const qreal firstMajorTick = tickAnchor - std::floor(x: ticksFromAnchor) * tickInterval;
493
494 qreal value = firstMajorTick;
495 while (value <= max) {
496 labels << presenter()->numberToString(value, f: 'f', prec: n);
497 value += tickInterval;
498 }
499 }
500 } else {
501 QByteArray array = format.toLatin1();
502 QString formatSpec;
503 QString preStr;
504 QString postStr;
505 int precision = 6; // Six is the default precision in Qt API
506 if (presenter()->localizeNumbers()) {
507 QRegularExpressionMatch rmatch;
508 if (format.indexOf(re: labelFormatMatcherLocalized(), from: 0, rmatch: &rmatch) != -1) {
509 preStr = rmatch.captured(nth: 1);
510 if (!rmatch.captured(nth: 2).isEmpty())
511 precision = rmatch.captured(nth: 2).toInt();
512 formatSpec = rmatch.captured(nth: 3);
513 postStr = rmatch.captured(nth: 4);
514 }
515 } else {
516 QRegularExpressionMatch rmatch;
517 if (format.indexOf(re: labelFormatMatcher(), from: 0, rmatch: &rmatch) != -1)
518 formatSpec = rmatch.captured(nth: 1);
519 }
520 if (tickType == QValueAxis::TicksFixed) {
521 for (int i = 0; i < ticks; i++) {
522 qreal value = min + (i * (max - min) / (ticks - 1));
523 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
524 }
525 } else {
526 const qreal ticksFromAnchor = (tickAnchor - min) / tickInterval;
527 const qreal firstMajorTick = tickAnchor - std::floor(x: ticksFromAnchor) * tickInterval;
528
529 qreal value = firstMajorTick;
530 while (value <= max) {
531 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
532 value += tickInterval;
533 }
534 }
535 }
536
537 return labels;
538}
539
540QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks,
541 const QString &format) const
542{
543 QStringList labels;
544
545 if (max <= min || ticks < 1)
546 return labels;
547
548 const int firstTick = qCeil(v: qLn(v: base > 1 ? min : max) / qLn(v: base));
549 if (format.isEmpty()) {
550 const int n = precisionDigits(min, max, ticks);
551 for (int i = firstTick; i < ticks + firstTick; i++) {
552 qreal value = qPow(x: base, y: i);
553 labels << presenter()->numberToString(value, f: 'f', prec: n);
554 }
555 } else {
556 QByteArray array = format.toLatin1();
557 QString formatSpec;
558 QString preStr;
559 QString postStr;
560 int precision = 6; // Six is the default precision in Qt API
561 if (presenter()->localizeNumbers()) {
562 QRegularExpressionMatch rmatch;
563 if (format.indexOf(re: labelFormatMatcherLocalized(), from: 0, rmatch: &rmatch) != -1) {
564 preStr = rmatch.captured(nth: 1);
565 if (!rmatch.captured(nth: 2).isEmpty())
566 precision = rmatch.captured(nth: 2).toInt();
567 formatSpec = rmatch.captured(nth: 3);
568 postStr = rmatch.captured(nth: 4);
569 }
570 } else {
571 QRegularExpressionMatch rmatch;
572 if (format.indexOf(re: labelFormatMatcher(), from: 0, rmatch: &rmatch) != -1)
573 formatSpec = rmatch.captured(nth: 1);
574 }
575 for (int i = firstTick; i < ticks + firstTick; i++) {
576 qreal value = qPow(x: base, y: i);
577 labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
578 }
579 }
580
581 return labels;
582}
583
584QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks,
585 const QString &format) const
586{
587 QStringList labels;
588
589 if (max <= min || ticks < 1)
590 return labels;
591
592 for (int i = 0; i < ticks; i++) {
593 qreal value = min + (i * (max - min) / (ticks - 1));
594 labels << presenter()->locale().toString(dateTime: QDateTime::fromMSecsSinceEpoch(msecs: value), format);
595 }
596 return labels;
597}
598
599QStringList ChartAxisElement::createColorLabels(qreal min, qreal max, int ticks) const
600{
601 QStringList labels;
602
603 if (max <= min || ticks < 1)
604 return labels;
605
606 const int n = precisionDigits(min, max, ticks);
607 for (int i = 0; i < ticks; ++i) {
608 qreal value = min + (i * (max - min) / (ticks - 1));
609 labels << presenter()->numberToString(value, f: 'f', prec: n);
610 }
611
612 return labels;
613}
614
615bool ChartAxisElement::labelsEditable() const
616{
617 return m_labelsEditable;
618}
619
620void ChartAxisElement::setLabelsEditable(bool labelsEditable)
621{
622 if (axis()->type() == QAbstractAxis::AxisTypeValue
623 || axis()->type() == QAbstractAxis::AxisTypeDateTime) {
624 labelGroup()->setHandlesChildEvents(!labelsEditable);
625 const QList<QGraphicsItem *> childItems = labelGroup()->childItems();
626 for (auto item : childItems) {
627 switch (axis()->type()) {
628 case QAbstractAxis::AxisTypeValue:
629 static_cast<ValueAxisLabel *>(item)->setEditable(labelsEditable);
630 break;
631 case QAbstractAxis::AxisTypeDateTime:
632 static_cast<DateTimeAxisLabel *>(item)->setEditable(labelsEditable);
633 break;
634 default:
635 break;
636 }
637 }
638 m_labelsEditable = labelsEditable;
639 }
640}
641
642bool ChartAxisElement::labelsVisible() const
643{
644 return m_labels->isVisible();
645}
646
647void ChartAxisElement::axisSelected()
648{
649 emit clicked();
650}
651
652QT_END_NAMESPACE
653
654#include "moc_chartaxiselement_p.cpp"
655

Provided by KDAB

Privacy Policy
Learn to use CMake with our Intro Training
Find out more

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