##// END OF EJS Templates
Added possibility to set labels position for QCategoryAxis...
Added possibility to set labels position for QCategoryAxis The position of the labels in QCategyAxis can now be set to center of the category or to the high end of the category. The first and the last labels may overlap with other axes labels when positioned on value. Change-Id: Ide0f12b723ffabf6682001e03ea5080f9642da22 Task-number: QTRD-1715 Reviewed-by: Miikka Heikkinen <miikka.heikkinen@theqtcompany.com>

File last commit:

r2776:bc1f6aa59d42
r2780:740f4f94adf8
Show More
chartaxiselement.cpp
391 lines | 14.5 KiB | text/x-c | CppLexer
/ src / charts / axis / chartaxiselement.cpp
/****************************************************************************
**
** Copyright (C) 2015 The Qt Company Ltd
** All rights reserved.
** For any questions to The Qt Company, please use contact form at http://qt.io
**
** This file is part of the Qt Charts module.
**
** Licensees holding valid commercial license for Qt may use this file in
** accordance with the Qt License Agreement provided with the Software
** or, alternatively, in accordance with the terms contained in a written
** agreement between you and The Qt Company.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.io
**
****************************************************************************/
#include <private/chartaxiselement_p.h>
#include <private/qabstractaxis_p.h>
#include <private/chartpresenter_p.h>
#include <private/abstractchartlayout_p.h>
#include <QtCore/QtMath>
#include <QtCore/QDateTime>
#include <QtGui/QTextDocument>
#include <cmath>
QT_CHARTS_BEGIN_NAMESPACE
static const char *labelFormatMatchString = "%[\\-\\+#\\s\\d\\.\\'lhjztL]*([dicuoxfegXFEG])";
static const char *labelFormatMatchLocalizedString = "^([^%]*)%\\.(\\d+)([defgiEG])(.*)$";
static QRegExp *labelFormatMatcher = 0;
static QRegExp *labelFormatMatcherLocalized = 0;
class StaticLabelFormatMatcherDeleter
{
public:
StaticLabelFormatMatcherDeleter() {}
~StaticLabelFormatMatcherDeleter() {
delete labelFormatMatcher;
delete labelFormatMatcherLocalized;
}
};
static StaticLabelFormatMatcherDeleter staticLabelFormatMatcherDeleter;
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)),
m_shades(new QGraphicsItemGroup(item)),
m_labels(new QGraphicsItemGroup(item)),
m_title(new QGraphicsTextItem(item)),
m_intervalAxis(intervalAxis)
{
//initial initialization
m_arrow->setHandlesChildEvents(false);
m_arrow->setZValue(ChartPresenter::AxisZValue);
m_labels->setZValue(ChartPresenter::AxisZValue);
m_shades->setZValue(ChartPresenter::ShadesZValue);
m_grid->setZValue(ChartPresenter::GridZValue);
m_title->setZValue(ChartPresenter::GridZValue);
m_title->document()->setDocumentMargin(ChartPresenter::textMargin());
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)));
}
void ChartAxisElement::handleArrowVisibleChanged(bool visible)
{
m_arrow->setVisible(visible);
}
void ChartAxisElement::handleGridVisibleChanged(bool visible)
{
m_grid->setVisible(visible);
}
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())
static_cast<QGraphicsTextItem *>(item)->setDefaultTextColor(brush.color());
}
void ChartAxisElement::handleLabelsFontChanged(const QFont &font)
{
foreach (QGraphicsItem *item, m_labels->childItems())
static_cast<QGraphicsTextItem *>(item)->setFont(font);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
void ChartAxisElement::handleTitleTextChanged(const QString &title)
{
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
if (title.isEmpty() || !m_title->isVisible())
m_title->setHtml(title);
}
void ChartAxisElement::handleTitleBrushChanged(const QBrush &brush)
{
m_title->setDefaultTextColor(brush.color());
}
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);
m_shades->setVisible(visible);
m_labels->setVisible(visible);
m_title->setVisible(visible);
} else {
m_grid->setVisible(axis()->isGridLineVisible());
m_arrow->setVisible(axis()->isLineVisible());
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());
}
}
}
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();
}
QString ChartAxisElement::formatLabel(const QString &formatSpec, const QByteArray &array,
qreal value, int precision, const QString &preStr,
const QString &postStr) const
{
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);
}
}
}
return retVal;
}
QStringList ChartAxisElement::createValueLabels(qreal min, qreal max, int ticks,
const QString &format) const
{
QStringList labels;
if (max <= min || ticks < 1)
return labels;
if (format.isNull()) {
int n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0) + 1;
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << presenter()->numberToString(value, 'f', n);
}
} else {
QByteArray array = format.toLatin1();
QString formatSpec;
QString preStr;
QString postStr;
int precision = 6; // Six is the default precision in Qt API
if (presenter()->localizeNumbers()) {
if (!labelFormatMatcherLocalized)
labelFormatMatcherLocalized
= new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString));
if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) {
preStr = labelFormatMatcherLocalized->cap(1);
if (!labelFormatMatcherLocalized->cap(2).isEmpty())
precision = labelFormatMatcherLocalized->cap(2).toInt();
formatSpec = labelFormatMatcherLocalized->cap(3);
postStr = labelFormatMatcherLocalized->cap(4);
}
} else {
if (!labelFormatMatcher)
labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString));
if (labelFormatMatcher->indexIn(format, 0) != -1)
formatSpec = labelFormatMatcher->cap(1);
}
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
}
}
return labels;
}
QStringList ChartAxisElement::createLogValueLabels(qreal min, qreal max, qreal base, int ticks,
const QString &format) const
{
QStringList labels;
if (max <= min || ticks < 1)
return labels;
int firstTick;
if (base > 1)
firstTick = qCeil(std::log10(min) / std::log10(base));
else
firstTick = qCeil(std::log10(max) / std::log10(base));
if (format.isNull()) {
int n = 0;
if (ticks > 1)
n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0);
n++;
for (int i = firstTick; i < ticks + firstTick; i++) {
qreal value = qPow(base, i);
labels << presenter()->numberToString(value, 'f', n);
}
} else {
QByteArray array = format.toLatin1();
QString formatSpec;
QString preStr;
QString postStr;
int precision = 6; // Six is the default precision in Qt API
if (presenter()->localizeNumbers()) {
if (!labelFormatMatcherLocalized)
labelFormatMatcherLocalized =
new QRegExp(QString::fromLatin1(labelFormatMatchLocalizedString));
if (labelFormatMatcherLocalized->indexIn(format, 0) != -1) {
preStr = labelFormatMatcherLocalized->cap(1);
if (!labelFormatMatcherLocalized->cap(2).isEmpty())
precision = labelFormatMatcherLocalized->cap(2).toInt();
formatSpec = labelFormatMatcherLocalized->cap(3);
postStr = labelFormatMatcherLocalized->cap(4);
}
} else {
if (!labelFormatMatcher)
labelFormatMatcher = new QRegExp(QString::fromLatin1(labelFormatMatchString));
if (labelFormatMatcher->indexIn(format, 0) != -1)
formatSpec = labelFormatMatcher->cap(1);
}
for (int i = firstTick; i < ticks + firstTick; i++) {
qreal value = qPow(base, i);
labels << formatLabel(formatSpec, array, value, precision, preStr, postStr);
}
}
return labels;
}
QStringList ChartAxisElement::createDateTimeLabels(qreal min, qreal max,int ticks,
const QString &format) const
{
QStringList labels;
if (max <= min || ticks < 1)
return labels;
int n = qMax(int(-qFloor(std::log10((max - min) / (ticks - 1)))), 0);
n++;
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << presenter()->locale().toString(QDateTime::fromMSecsSinceEpoch(value), format);
}
return labels;
}
void ChartAxisElement::axisSelected()
{
emit clicked();
}
#include "moc_chartaxiselement_p.cpp"
QT_CHARTS_END_NAMESPACE