chartaxiselement.cpp
448 lines
| 16.9 KiB
| text/x-c
|
CppLexer
|
r2845 | /****************************************************************************** | ||
|
r2483 | ** | ||
|
r2845 | ** Copyright (C) 2015 The Qt Company Ltd. | ||
** Contact: http://www.qt.io/licensing/ | ||||
|
r2483 | ** | ||
|
r2740 | ** This file is part of the Qt Charts module. | ||
|
r2483 | ** | ||
|
r2845 | ** $QT_BEGIN_LICENSE:COMM$ | ||
|
r2483 | ** | ||
|
r2845 | ** Commercial License Usage | ||
** Licensees holding valid commercial Qt licenses may use this file in | ||||
** accordance with the commercial license agreement provided with the | ||||
** Software or, alternatively, in accordance with the terms contained in | ||||
** a written agreement between you and The Qt Company. For licensing terms | ||||
** and conditions see http://www.qt.io/terms-conditions. For further | ||||
** information use the contact form at http://www.qt.io/contact-us. | ||||
|
r2483 | ** | ||
|
r2845 | ** $QT_END_LICENSE$ | ||
** | ||||
******************************************************************************/ | ||||
|
r2483 | |||
|
r2714 | #include <private/chartaxiselement_p.h> | ||
#include <private/qabstractaxis_p.h> | ||||
#include <private/chartpresenter_p.h> | ||||
#include <private/abstractchartlayout_p.h> | ||||
|
r2801 | #include <QtCharts/QCategoryAxis> | ||
|
r2714 | #include <QtCore/QtMath> | ||
#include <QtCore/QDateTime> | ||||
#include <QtGui/QTextDocument> | ||||
|
r2775 | #include <cmath> | ||
|
r2483 | |||
|
r2712 | QT_CHARTS_BEGIN_NAMESPACE | ||
|
r2483 | |||
|
r2715 | static const char *labelFormatMatchString = "%[\\-\\+#\\s\\d\\.\\'lhjztL]*([dicuoxfegXFEG])"; | ||
|
r2710 | static const char *labelFormatMatchLocalizedString = "^([^%]*)%\\.(\\d+)([defgiEG])(.*)$"; | ||
|
r2539 | static QRegExp *labelFormatMatcher = 0; | ||
|
r2707 | static QRegExp *labelFormatMatcherLocalized = 0; | ||
|
r2539 | class StaticLabelFormatMatcherDeleter | ||
|
r2534 | { | ||
public: | ||||
|
r2539 | StaticLabelFormatMatcherDeleter() {} | ||
|
r2707 | ~StaticLabelFormatMatcherDeleter() { | ||
delete labelFormatMatcher; | ||||
delete labelFormatMatcherLocalized; | ||||
} | ||||
|
r2534 | }; | ||
|
r2543 | static StaticLabelFormatMatcherDeleter staticLabelFormatMatcherDeleter; | ||
|
r2534 | |||
|
r2483 | ChartAxisElement::ChartAxisElement(QAbstractAxis *axis, QGraphicsItem *item, bool intervalAxis) | ||
: ChartElement(item), | ||||
m_axis(axis), | ||||
m_animation(0), | ||||
m_grid(new QGraphicsItemGroup(item)), | ||||
m_arrow(new QGraphicsItemGroup(item)), | ||||
|
r2795 | m_minorGrid(new QGraphicsItemGroup(item)), | ||
m_minorArrow(new QGraphicsItemGroup(item)), | ||||
|
r2483 | m_shades(new QGraphicsItemGroup(item)), | ||
m_labels(new QGraphicsItemGroup(item)), | ||||
|
r2539 | m_title(new QGraphicsTextItem(item)), | ||
|
r2483 | m_intervalAxis(intervalAxis) | ||
{ | ||||
//initial initialization | ||||
m_arrow->setHandlesChildEvents(false); | ||||
m_arrow->setZValue(ChartPresenter::AxisZValue); | ||||
|
r2795 | m_minorArrow->setHandlesChildEvents(false); | ||
m_minorArrow->setZValue(ChartPresenter::AxisZValue); | ||||
|
r2483 | m_labels->setZValue(ChartPresenter::AxisZValue); | ||
m_shades->setZValue(ChartPresenter::ShadesZValue); | ||||
m_grid->setZValue(ChartPresenter::GridZValue); | ||||
|
r2795 | m_minorGrid->setZValue(ChartPresenter::GridZValue); | ||
|
r2483 | m_title->setZValue(ChartPresenter::GridZValue); | ||
|
r2592 | m_title->document()->setDocumentMargin(ChartPresenter::textMargin()); | ||
|
r2483 | handleVisibleChanged(axis->isVisible()); | ||
connectSlots(); | ||||
setFlag(QGraphicsItem::ItemHasNoContents, true); | ||||
} | ||||
ChartAxisElement::~ChartAxisElement() | ||||
{ | ||||
} | ||||
void ChartAxisElement::connectSlots() | ||||
{ | ||||
QObject::connect(axis(), SIGNAL(visibleChanged(bool)), this, SLOT(handleVisibleChanged(bool))); | ||||
QObject::connect(axis(), SIGNAL(lineVisibleChanged(bool)), this, SLOT(handleArrowVisibleChanged(bool))); | ||||
QObject::connect(axis(), SIGNAL(gridVisibleChanged(bool)), this, SLOT(handleGridVisibleChanged(bool))); | ||||
QObject::connect(axis(), SIGNAL(labelsVisibleChanged(bool)), this, SLOT(handleLabelsVisibleChanged(bool))); | ||||
QObject::connect(axis(), SIGNAL(shadesVisibleChanged(bool)), this, SLOT(handleShadesVisibleChanged(bool))); | ||||
QObject::connect(axis(), SIGNAL(labelsAngleChanged(int)), this, SLOT(handleLabelsAngleChanged(int))); | ||||
QObject::connect(axis(), SIGNAL(linePenChanged(const QPen&)), this, SLOT(handleArrowPenChanged(const QPen&))); | ||||
QObject::connect(axis(), SIGNAL(labelsBrushChanged(const QBrush&)), this, SLOT(handleLabelsBrushChanged(const QBrush&))); | ||||
QObject::connect(axis(), SIGNAL(labelsFontChanged(const QFont&)), this, SLOT(handleLabelsFontChanged(const QFont&))); | ||||
QObject::connect(axis(), SIGNAL(gridLinePenChanged(const QPen&)), this, SLOT(handleGridPenChanged(const QPen&))); | ||||
QObject::connect(axis(), SIGNAL(shadesPenChanged(const QPen&)), this, SLOT(handleShadesPenChanged(const QPen&))); | ||||
QObject::connect(axis(), SIGNAL(shadesBrushChanged(const QBrush&)), this, SLOT(handleShadesBrushChanged(const QBrush&))); | ||||
QObject::connect(axis(), SIGNAL(titleTextChanged(const QString&)), this, SLOT(handleTitleTextChanged(const QString&))); | ||||
QObject::connect(axis(), SIGNAL(titleFontChanged(const QFont&)), this, SLOT(handleTitleFontChanged(const QFont&))); | ||||
QObject::connect(axis(), SIGNAL(titleBrushChanged(const QBrush&)), this, SLOT(handleTitleBrushChanged(const QBrush&))); | ||||
QObject::connect(axis(), SIGNAL(titleVisibleChanged(bool)), this, SLOT(handleTitleVisibleChanged(bool))); | ||||
QObject::connect(axis()->d_ptr.data(), SIGNAL(rangeChanged(qreal, qreal)), this, SLOT(handleRangeChanged(qreal, qreal))); | ||||
|
r2781 | QObject::connect(axis(), SIGNAL(reverseChanged(bool)), this, SLOT(handleReverseChanged(bool))); | ||
|
r2795 | QObject::connect(axis(), SIGNAL(lineVisibleChanged(bool)), | ||
this, SLOT(handleMinorArrowVisibleChanged(bool))); | ||||
QObject::connect(axis(), SIGNAL(linePenChanged(const QPen&)), this, | ||||
SLOT(handleMinorArrowPenChanged(const QPen&))); | ||||
QObject::connect(axis(), SIGNAL(minorGridVisibleChanged(bool)), | ||||
this, SLOT(handleMinorGridVisibleChanged(bool))); | ||||
QObject::connect(axis(), SIGNAL(minorGridLinePenChanged(const QPen&)), | ||||
this, SLOT(handleMinorGridPenChanged(const QPen&))); | ||||
|
r2810 | QObject::connect(axis(), SIGNAL(gridLineColorChanged(const QColor&)), | ||
this, SLOT(handleGridLineColorChanged(const QColor&))); | ||||
QObject::connect(axis(), SIGNAL(minorGridLineColorChanged(const QColor&)), | ||||
this, SLOT(handleMinorGridLineColorChanged(const QColor&))); | ||||
|
r2801 | |||
if (axis()->type() == QAbstractAxis::AxisTypeCategory) { | ||||
QCategoryAxis *categoryAxis = static_cast<QCategoryAxis *>(axis()); | ||||
QObject::connect(categoryAxis, | ||||
SIGNAL(labelsPositionChanged(QCategoryAxis::AxisLabelsPosition)), | ||||
this, SLOT(handleLabelsPositionChanged())); | ||||
} | ||||
|
r2483 | } | ||
void ChartAxisElement::handleArrowVisibleChanged(bool visible) | ||||
{ | ||||
m_arrow->setVisible(visible); | ||||
} | ||||
|
r2795 | void ChartAxisElement::handleMinorArrowVisibleChanged(bool visible) | ||
{ | ||||
m_minorArrow->setVisible(visible); | ||||
} | ||||
|
r2483 | void ChartAxisElement::handleGridVisibleChanged(bool visible) | ||
{ | ||||
m_grid->setVisible(visible); | ||||
} | ||||
|
r2795 | void ChartAxisElement::handleMinorGridVisibleChanged(bool visible) | ||
{ | ||||
m_minorGrid->setVisible(visible); | ||||
} | ||||
|
r2801 | void ChartAxisElement::handleLabelsPositionChanged() | ||
{ | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
presenter()->layout()->invalidate(); | ||||
} | ||||
|
r2483 | void ChartAxisElement::handleLabelsVisibleChanged(bool visible) | ||
{ | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
presenter()->layout()->invalidate(); | ||||
m_labels->setVisible(visible); | ||||
} | ||||
void ChartAxisElement::handleShadesVisibleChanged(bool visible) | ||||
{ | ||||
m_shades->setVisible(visible); | ||||
} | ||||
void ChartAxisElement::handleTitleVisibleChanged(bool visible) | ||||
{ | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
presenter()->layout()->invalidate(); | ||||
m_title->setVisible(visible); | ||||
} | ||||
void ChartAxisElement::handleLabelsAngleChanged(int angle) | ||||
{ | ||||
foreach (QGraphicsItem *item, m_labels->childItems()) | ||||
item->setRotation(angle); | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
presenter()->layout()->invalidate(); | ||||
} | ||||
void ChartAxisElement::handleLabelsBrushChanged(const QBrush &brush) | ||||
{ | ||||
foreach (QGraphicsItem *item, m_labels->childItems()) | ||||
|
r2539 | static_cast<QGraphicsTextItem *>(item)->setDefaultTextColor(brush.color()); | ||
|
r2483 | } | ||
void ChartAxisElement::handleLabelsFontChanged(const QFont &font) | ||||
{ | ||||
foreach (QGraphicsItem *item, m_labels->childItems()) | ||||
|
r2539 | static_cast<QGraphicsTextItem *>(item)->setFont(font); | ||
|
r2483 | QGraphicsLayoutItem::updateGeometry(); | ||
presenter()->layout()->invalidate(); | ||||
} | ||||
void ChartAxisElement::handleTitleTextChanged(const QString &title) | ||||
{ | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
presenter()->layout()->invalidate(); | ||||
|
r2608 | if (title.isEmpty() || !m_title->isVisible()) | ||
m_title->setHtml(title); | ||||
|
r2483 | } | ||
void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush) | ||||
{ | ||||
|
r2539 | m_title->setDefaultTextColor(brush.color()); | ||
|
r2483 | } | ||
void ChartAxisElement::handleTitleFontChanged(const QFont &font) | ||||
{ | ||||
if (m_title->font() != font) { | ||||
m_title->setFont(font); | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
presenter()->layout()->invalidate(); | ||||
} | ||||
} | ||||
void ChartAxisElement::handleVisibleChanged(bool visible) | ||||
{ | ||||
setVisible(visible); | ||||
if (!visible) { | ||||
m_grid->setVisible(visible); | ||||
m_arrow->setVisible(visible); | ||||
|
r2795 | m_minorGrid->setVisible(visible); | ||
m_minorArrow->setVisible(visible); | ||||
|
r2483 | m_shades->setVisible(visible); | ||
m_labels->setVisible(visible); | ||||
m_title->setVisible(visible); | ||||
} else { | ||||
m_grid->setVisible(axis()->isGridLineVisible()); | ||||
m_arrow->setVisible(axis()->isLineVisible()); | ||||
|
r2795 | m_minorGrid->setVisible(axis()->isMinorGridLineVisible()); | ||
m_minorArrow->setVisible(axis()->isLineVisible()); | ||||
|
r2483 | m_shades->setVisible(axis()->shadesVisible()); | ||
m_labels->setVisible(axis()->labelsVisible()); | ||||
m_title->setVisible(axis()->isTitleVisible()); | ||||
} | ||||
if (presenter()) presenter()->layout()->invalidate(); | ||||
} | ||||
void ChartAxisElement::handleRangeChanged(qreal min, qreal max) | ||||
{ | ||||
Q_UNUSED(min); | ||||
Q_UNUSED(max); | ||||
if (!isEmpty()) { | ||||
QVector<qreal> layout = calculateLayout(); | ||||
updateLayout(layout); | ||||
QSizeF before = effectiveSizeHint(Qt::PreferredSize); | ||||
QSizeF after = sizeHint(Qt::PreferredSize); | ||||
if (before != after) { | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
// We don't want to call invalidate on layout, since it will change minimum size of | ||||
// component, which we would like to avoid since it causes nasty flips when scrolling | ||||
// or zooming, instead recalculate layout and use plotArea for extra space. | ||||
presenter()->layout()->setGeometry(presenter()->layout()->geometry()); | ||||
} | ||||
} | ||||
} | ||||
|
r2781 | void ChartAxisElement::handleReverseChanged(bool reverse) | ||
{ | ||||
Q_UNUSED(reverse); | ||||
QGraphicsLayoutItem::updateGeometry(); | ||||
presenter()->layout()->invalidate(); | ||||
} | ||||
|
r2483 | bool ChartAxisElement::isEmpty() | ||
{ | ||||
return axisGeometry().isEmpty() | ||||
|| gridGeometry().isEmpty() | ||||
|| qFuzzyCompare(min(), max()); | ||||
} | ||||
qreal ChartAxisElement::min() const | ||||
{ | ||||
return m_axis->d_ptr->min(); | ||||
} | ||||
qreal ChartAxisElement::max() const | ||||
{ | ||||
return m_axis->d_ptr->max(); | ||||
} | ||||
|
r2707 | QString ChartAxisElement::formatLabel(const QString &formatSpec, const QByteArray &array, | ||
qreal value, int precision, const QString &preStr, | ||||
const QString &postStr) const | ||||
|
r2539 | { | ||
|
r2707 | QString retVal; | ||
if (!formatSpec.isEmpty()) { | ||||
if (formatSpec.at(0) == QLatin1Char('d') | ||||
|| formatSpec.at(0) == QLatin1Char('i') | ||||
|| formatSpec.at(0) == QLatin1Char('c')) { | ||||
if (presenter()->localizeNumbers()) | ||||
retVal = preStr + presenter()->locale().toString(qint64(value)) + postStr; | ||||
else | ||||
retVal = QString().sprintf(array, qint64(value)); | ||||
} else if (formatSpec.at(0) == QLatin1Char('u') | ||||
|| formatSpec.at(0) == QLatin1Char('o') | ||||
|| formatSpec.at(0) == QLatin1Char('x') | ||||
|| formatSpec.at(0) == QLatin1Char('X')) { | ||||
// These formats are not supported by localized numbers | ||||
retVal = QString().sprintf(array, quint64(value)); | ||||
} else if (formatSpec.at(0) == QLatin1Char('f') | ||||
|| formatSpec.at(0) == QLatin1Char('F') | ||||
|| formatSpec.at(0) == QLatin1Char('e') | ||||
|| formatSpec.at(0) == QLatin1Char('E') | ||||
|| formatSpec.at(0) == QLatin1Char('g') | ||||
|| formatSpec.at(0) == QLatin1Char('G')) { | ||||
if (presenter()->localizeNumbers()) { | ||||
retVal = preStr | ||||
+ presenter()->locale().toString(value, formatSpec.at(0).toLatin1(), | ||||
precision) | ||||
+ postStr; | ||||
} else { | ||||
retVal = QString().sprintf(array, value); | ||||
} | ||||
} | ||||
|
r2539 | } | ||
|
r2707 | return retVal; | ||
|
r2539 | } | ||
|
r2707 | QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks, | ||
const QString &format) const | ||||
|
r2483 | { | ||
QStringList labels; | ||||
if (max <= min || ticks < 1) | ||||
return labels; | ||||
if (format.isNull()) { | ||||
|
r2775 | int n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0) + 1; | ||
|
r2483 | for (int i = 0; i < ticks; i++) { | ||
qreal value = min + (i * (max - min) / (ticks - 1)); | ||||
|
r2707 | labels << presenter()->numberToString(value, 'f', n); | ||
|
r2483 | } | ||
} else { | ||||
QByteArray array = format.toLatin1(); | ||||
|
r2707 | QString formatSpec; | ||
QString preStr; | ||||
QString postStr; | ||||
|
r2709 | int precision = 6; // Six is the default precision in Qt API | ||
|
r2707 | if (presenter()->localizeNumbers()) { | ||
if (!labelFormatMatcherLocalized) | ||||
|
r2712 | labelFormatMatcherLocalized | ||
= new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString)); | ||||
|
r2707 | if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) { | ||
preStr = labelFormatMatcherLocalized->cap(1); | ||||
|
r2709 | if (!labelFormatMatcherLocalized->cap(2).isEmpty()) | ||
precision = labelFormatMatcherLocalized->cap(2).toInt(); | ||||
|
r2707 | formatSpec = labelFormatMatcherLocalized->cap(3); | ||
postStr = labelFormatMatcherLocalized->cap(4); | ||||
} | ||||
} else { | ||||
if (!labelFormatMatcher) | ||||
|
r2712 | labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString)); | ||
|
r2707 | if (labelFormatMatcher->indexIn(format, 0) != -1) | ||
formatSpec = labelFormatMatcher->cap(1); | ||||
} | ||||
|
r2483 | for (int i = 0; i < ticks; i++) { | ||
qreal value = min + (i * (max - min) / (ticks - 1)); | ||||
|
r2707 | labels << formatLabel(formatSpec, array, value, precision, preStr, postStr); | ||
|
r2483 | } | ||
} | ||||
return labels; | ||||
} | ||||
|
r2707 | QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks, | ||
const QString &format) const | ||||
|
r2483 | { | ||
QStringList labels; | ||||
if (max <= min || ticks < 1) | ||||
return labels; | ||||
int firstTick; | ||||
if (base > 1) | ||||
|
r2775 | firstTick = qCeil(std::log10(min) / std::log10(base)); | ||
|
r2483 | else | ||
|
r2775 | firstTick = qCeil(std::log10(max) / std::log10(base)); | ||
|
r2483 | |||
if (format.isNull()) { | ||||
|
r2707 | int n = 0; | ||
if (ticks > 1) | ||||
|
r2775 | n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0); | ||
|
r2707 | n++; | ||
|
r2483 | for (int i = firstTick; i < ticks + firstTick; i++) { | ||
qreal value = qPow(base, i); | ||||
|
r2707 | labels << presenter()->numberToString(value, 'f', n); | ||
|
r2483 | } | ||
} else { | ||||
QByteArray array = format.toLatin1(); | ||||
|
r2707 | QString formatSpec; | ||
QString preStr; | ||||
QString postStr; | ||||
|
r2709 | int precision = 6; // Six is the default precision in Qt API | ||
|
r2707 | if (presenter()->localizeNumbers()) { | ||
if (!labelFormatMatcherLocalized) | ||||
|
r2712 | labelFormatMatcherLocalized = | ||
new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString)); | ||||
|
r2707 | if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) { | ||
preStr = labelFormatMatcherLocalized->cap(1); | ||||
|
r2709 | if (!labelFormatMatcherLocalized->cap(2).isEmpty()) | ||
precision = labelFormatMatcherLocalized->cap(2).toInt(); | ||||
|
r2707 | formatSpec = labelFormatMatcherLocalized->cap(3); | ||
postStr = labelFormatMatcherLocalized->cap(4); | ||||
} | ||||
} else { | ||||
if (!labelFormatMatcher) | ||||
|
r2712 | labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString)); | ||
|
r2707 | if (labelFormatMatcher->indexIn(format, 0) != -1) | ||
formatSpec = labelFormatMatcher->cap(1); | ||||
} | ||||
|
r2483 | for (int i = firstTick; i < ticks + firstTick; i++) { | ||
qreal value = qPow(base, i); | ||||
|
r2707 | labels << formatLabel(formatSpec, array, value, precision, preStr, postStr); | ||
|
r2483 | } | ||
} | ||||
return labels; | ||||
} | ||||
|
r2707 | QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks, | ||
const QString &format) const | ||||
|
r2483 | { | ||
QStringList labels; | ||||
if (max <= min || ticks < 1) | ||||
return labels; | ||||
|
r2775 | int n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0); | ||
|
r2483 | n++; | ||
for (int i = 0; i < ticks; i++) { | ||||
qreal value = min + (i * (max - min) / (ticks - 1)); | ||||
|
r2708 | labels << presenter()->locale().toString(QDateTime::fromMSecsSinceEpoch(value), format); | ||
|
r2483 | } | ||
return labels; | ||||
} | ||||
void ChartAxisElement::axisSelected() | ||||
{ | ||||
emit clicked(); | ||||
} | ||||
#include "moc_chartaxiselement_p.cpp" | ||||
|
r2712 | QT_CHARTS_END_NAMESPACE | ||