##// END OF EJS Templates
Fix crash when removing pie series from chart...
Fix crash when removing pie series from chart Connections cleanup in PieChartItem destructor didn't account for the possibility of series already having been destroyed. Changed m_series to QPointer so that it'll notice when series gets destroyed, and also added cleaning of slices to series destructor. Task-number: QTRD-1946 Change-Id: I651347bfcfbe7cd78dd26220a811fea5c8071fce Reviewed-by: Mika Salmela Reviewed-by: Mika Salmela <mika.salmela@digia.com>

File last commit:

r2439:8946feb2c29e
r2475:9e35a3a169ed
Show More
chartaxis.cpp
547 lines | 16.8 KiB | text/x-c | CppLexer
/****************************************************************************
**
** Copyright (C) 2013 Digia Plc
** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com
**
** This file is part of the Qt Commercial Charts Add-on.
**
** $QT_BEGIN_LICENSE$
** Licensees holding valid Qt Commercial licenses may use this file in
** accordance with the Qt Commercial License Agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and Digia.
**
** If you have questions regarding the use of this file, please use
** contact form at http://qt.digia.com
** $QT_END_LICENSE$
**
****************************************************************************/
#include "chartaxis_p.h"
#include "qabstractaxis.h"
#include "qabstractaxis_p.h"
#include "chartpresenter_p.h"
#include "chartlayout_p.h"
#include "abstractdomain_p.h"
#include <qmath.h>
#include <QDateTime>
#include <QValueAxis>
#include <QLogValueAxis>
#include <QGraphicsLayout>
#include <QFontMetrics>
QTCOMMERCIALCHART_BEGIN_NAMESPACE
ChartAxis::ChartAxis(QAbstractAxis *axis, QGraphicsItem* item , bool intervalAxis)
: ChartElement(item),
m_axis(axis),
m_labelsAngle(axis->labelsAngle()),
m_grid(new QGraphicsItemGroup(item)),
m_arrow(new QGraphicsItemGroup(item)),
m_shades(new QGraphicsItemGroup(item)),
m_labels(new QGraphicsItemGroup(item)),
m_title(new QGraphicsSimpleTextItem(item)),
m_animation(0),
m_labelPadding(5),
m_intervalAxis(intervalAxis),
m_titlePadding(3)
{
Q_ASSERT(item);
//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);
handleVisibleChanged(m_axis->isVisible());
connectSlots();
setFlag(QGraphicsItem::ItemHasNoContents,true);
}
void ChartAxis::connectSlots()
{
QObject::connect(m_axis,SIGNAL(visibleChanged(bool)),this,SLOT(handleVisibleChanged(bool)));
QObject::connect(m_axis,SIGNAL(lineVisibleChanged(bool)),this,SLOT(handleArrowVisibleChanged(bool)));
QObject::connect(m_axis,SIGNAL(gridVisibleChanged(bool)),this,SLOT(handleGridVisibleChanged(bool)));
QObject::connect(m_axis,SIGNAL(labelsVisibleChanged(bool)),this,SLOT(handleLabelsVisibleChanged(bool)));
QObject::connect(m_axis,SIGNAL(shadesVisibleChanged(bool)),this,SLOT(handleShadesVisibleChanged(bool)));
QObject::connect(m_axis,SIGNAL(labelsAngleChanged(int)),this,SLOT(handleLabelsAngleChanged(int)));
QObject::connect(m_axis,SIGNAL(linePenChanged(const QPen&)),this,SLOT(handleArrowPenChanged(const QPen&)));
QObject::connect(m_axis,SIGNAL(labelsPenChanged(const QPen&)),this,SLOT(handleLabelsPenChanged(const QPen&)));
QObject::connect(m_axis,SIGNAL(labelsBrushChanged(const QBrush&)),this,SLOT(handleLabelsBrushChanged(const QBrush&)));
QObject::connect(m_axis,SIGNAL(labelsFontChanged(const QFont&)),this,SLOT(handleLabelsFontChanged(const QFont&)));
QObject::connect(m_axis,SIGNAL(gridLinePenChanged(const QPen&)),this,SLOT(handleGridPenChanged(const QPen&)));
QObject::connect(m_axis,SIGNAL(shadesPenChanged(const QPen&)),this,SLOT(handleShadesPenChanged(const QPen&)));
QObject::connect(m_axis,SIGNAL(shadesBrushChanged(const QBrush&)),this,SLOT(handleShadesBrushChanged(const QBrush&)));
QObject::connect(m_axis,SIGNAL(titleTextChanged(const QString&)),this,SLOT(handleTitleTextChanged(const QString&)));
QObject::connect(m_axis,SIGNAL(titleFontChanged(const QFont&)),this,SLOT(handleTitleFontChanged(const QFont&)));
QObject::connect(m_axis,SIGNAL(titlePenChanged(const QPen&)),this,SLOT(handleTitlePenChanged(const QPen&)));
QObject::connect(m_axis,SIGNAL(titleBrushChanged(const QBrush&)),this,SLOT(handleTitleBrushChanged(const QBrush&)));
QObject::connect(m_axis,SIGNAL(titleVisibleChanged(bool)),this,SLOT(handleTitleVisibleChanged(bool)));
QObject::connect(m_axis->d_ptr.data(),SIGNAL(rangeChanged(qreal,qreal)),this,SLOT(handleRangeChanged(qreal,qreal)));
}
ChartAxis::~ChartAxis()
{
}
void ChartAxis::setAnimation(AxisAnimation *animation)
{
m_animation = animation;
}
void ChartAxis::setLayout(QVector<qreal> &layout)
{
m_layoutVector = layout;
}
void ChartAxis::createItems(int count)
{
if (m_arrow->childItems().size() == 0){
QGraphicsLineItem* arrow = new ArrowItem(this, this);
arrow->setPen(m_axis->linePen());
m_arrow->addToGroup(arrow);
}
if (m_intervalAxis && m_grid->childItems().size() == 0) {
for (int i = 0 ; i < 2 ; i ++){
QGraphicsLineItem* item = new QGraphicsLineItem(this);
item->setPen(m_axis->gridLinePen());
m_grid->addToGroup(item);
}
}
for (int i = 0; i < count; ++i) {
QGraphicsLineItem* arrow = new QGraphicsLineItem(this);
arrow->setPen(m_axis->linePen());
QGraphicsLineItem* grid = new QGraphicsLineItem(this);
grid->setPen(m_axis->gridLinePen());
QGraphicsSimpleTextItem* label = new QGraphicsSimpleTextItem(this);
label->setFont(m_axis->labelsFont());
label->setPen(m_axis->labelsPen());
label->setBrush(m_axis->labelsBrush());
label->setRotation(m_labelsAngle);
m_arrow->addToGroup(arrow);
m_grid->addToGroup(grid);
m_labels->addToGroup(label);
if ((m_grid->childItems().size()) % 2 && m_grid->childItems().size() > 2){
QGraphicsRectItem* shades = new QGraphicsRectItem(this);
shades->setPen(m_axis->shadesPen());
shades->setBrush(m_axis->shadesBrush());
m_shades->addToGroup(shades);
}
}
}
void ChartAxis::deleteItems(int count)
{
QList<QGraphicsItem *> lines = m_grid->childItems();
QList<QGraphicsItem *> labels = m_labels->childItems();
QList<QGraphicsItem *> shades = m_shades->childItems();
QList<QGraphicsItem *> axis = m_arrow->childItems();
for (int i = 0; i < count; ++i) {
if (lines.size() % 2 && lines.size() > 1)
delete(shades.takeLast());
delete(lines.takeLast());
delete(labels.takeLast());
delete(axis.takeLast());
}
}
void ChartAxis::updateLayout(QVector<qreal> &layout)
{
int diff = m_layoutVector.size() - layout.size();
if (diff > 0)
deleteItems(diff);
else if (diff < 0)
createItems(-diff);
if (m_animation) {
switch (presenter()->state()) {
case ChartPresenter::ZoomInState:
m_animation->setAnimationType(AxisAnimation::ZoomInAnimation);
m_animation->setAnimationPoint(presenter()->statePoint());
break;
case ChartPresenter::ZoomOutState:
m_animation->setAnimationType(AxisAnimation::ZoomOutAnimation);
m_animation->setAnimationPoint(presenter()->statePoint());
break;
case ChartPresenter::ScrollUpState:
case ChartPresenter::ScrollLeftState:
m_animation->setAnimationType(AxisAnimation::MoveBackwordAnimation);
break;
case ChartPresenter::ScrollDownState:
case ChartPresenter::ScrollRightState:
m_animation->setAnimationType(AxisAnimation::MoveForwardAnimation);
break;
case ChartPresenter::ShowState:
m_animation->setAnimationType(AxisAnimation::DefaultAnimation);
break;
}
m_animation->setValues(m_layoutVector, layout);
presenter()->startAnimation(m_animation);
} else {
setLayout(layout);
updateGeometry();
}
}
void ChartAxis::setLabelPadding(int padding)
{
m_labelPadding = padding;
}
void ChartAxis::setTitlePadding(int padding)
{
m_titlePadding = padding;
}
bool ChartAxis::isEmpty()
{
return m_axisRect.isEmpty() || m_gridRect.isEmpty() || qFuzzyCompare(min(),max());
}
void ChartAxis::setGeometry(const QRectF &axis, const QRectF &grid)
{
m_gridRect = grid;
m_axisRect = axis;
if (isEmpty())
return;
QVector<qreal> layout = calculateLayout();
updateLayout(layout);
}
qreal ChartAxis::min() const
{
return m_axis->d_ptr->min();
}
qreal ChartAxis::max() const
{
return m_axis->d_ptr->max();
}
QFont ChartAxis::font() const
{
return m_axis->labelsFont();
}
QFont ChartAxis::titleFont() const
{
return m_axis->titleFont();
}
QString ChartAxis::titleText() const
{
return m_axis->titleText();
}
void ChartAxis::axisSelected()
{
emit clicked();
}
Qt::Orientation ChartAxis::orientation() const
{
return m_axis->orientation();
}
Qt::Alignment ChartAxis::alignment() const
{
return m_axis->alignment();
}
void ChartAxis::setLabels(const QStringList &labels)
{
m_labelsList = labels;
}
QSizeF ChartAxis::sizeHint(Qt::SizeHint which, const QSizeF &constraint) const
{
Q_UNUSED(which);
Q_UNUSED(constraint);
return QSizeF();
}
//handlers
void ChartAxis::handleArrowVisibleChanged(bool visible)
{
m_arrow->setVisible(visible);
}
void ChartAxis::handleGridVisibleChanged(bool visible)
{
m_grid->setVisible(visible);
}
void ChartAxis::handleLabelsVisibleChanged(bool visible)
{
m_labels->setVisible(visible);
}
void ChartAxis::handleShadesVisibleChanged(bool visible)
{
m_shades->setVisible(visible);
}
void ChartAxis::handleTitleVisibleChanged(bool visible)
{
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
m_title->setVisible(visible);
}
void ChartAxis::handleLabelsAngleChanged(int angle)
{
foreach (QGraphicsItem *item, m_labels->childItems())
item->setRotation(angle);
m_labelsAngle = angle;
}
void ChartAxis::handleLabelsPenChanged(const QPen &pen)
{
foreach (QGraphicsItem *item , m_labels->childItems())
static_cast<QGraphicsSimpleTextItem *>(item)->setPen(pen);
}
void ChartAxis::handleLabelsBrushChanged(const QBrush &brush)
{
foreach (QGraphicsItem *item , m_labels->childItems())
static_cast<QGraphicsSimpleTextItem *>(item)->setBrush(brush);
}
void ChartAxis::handleLabelsFontChanged(const QFont &font)
{
foreach (QGraphicsItem *item , m_labels->childItems())
static_cast<QGraphicsSimpleTextItem *>(item)->setFont(font);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
void ChartAxis::handleShadesBrushChanged(const QBrush &brush)
{
foreach (QGraphicsItem *item , m_shades->childItems())
static_cast<QGraphicsRectItem *>(item)->setBrush(brush);
}
void ChartAxis::handleShadesPenChanged(const QPen &pen)
{
foreach (QGraphicsItem *item , m_shades->childItems())
static_cast<QGraphicsRectItem *>(item)->setPen(pen);
}
void ChartAxis::handleArrowPenChanged(const QPen &pen)
{
foreach (QGraphicsItem *item , m_arrow->childItems())
static_cast<QGraphicsLineItem *>(item)->setPen(pen);
}
void ChartAxis::handleGridPenChanged(const QPen &pen)
{
foreach (QGraphicsItem *item , m_grid->childItems())
static_cast<QGraphicsLineItem *>(item)->setPen(pen);
}
void ChartAxis::handleTitleTextChanged(const QString &title)
{
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
m_title->setText(title);
}
void ChartAxis::handleTitlePenChanged(const QPen &pen)
{
m_title->setPen(pen);
}
void ChartAxis::handleTitleBrushChanged(const QBrush &brush)
{
m_title->setBrush(brush);
}
void ChartAxis::handleTitleFontChanged(const QFont &font)
{
if(m_title->font() != font){
m_title->setFont(font);
QGraphicsLayoutItem::updateGeometry();
presenter()->layout()->invalidate();
}
}
void ChartAxis::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(m_axis->isGridLineVisible());
m_arrow->setVisible(m_axis->isLineVisible());
m_shades->setVisible(m_axis->shadesVisible());
m_labels->setVisible(m_axis->labelsVisible());
m_title->setVisible(m_axis->isTitleVisible());
}
if(presenter()) presenter()->layout()->invalidate();
}
void ChartAxis::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());
}
}
}
//helpers
QStringList ChartAxis::createValueLabels(qreal min, qreal max, int ticks,const QString& format)
{
QStringList labels;
if (max <= min || ticks < 1)
return labels;
int n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0);
n++;
if (format.isNull()) {
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << QString::number(value, 'f', n);
}
} else {
QByteArray array = format.toLatin1();
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
if (format.contains("d")
|| format.contains("i")
|| format.contains("c"))
labels << QString().sprintf(array, (qint64)value);
else if (format.contains("u")
|| format.contains("o")
|| format.contains("x", Qt::CaseInsensitive))
labels << QString().sprintf(array, (quint64)value);
else if (format.contains("f", Qt::CaseInsensitive)
|| format.contains("e", Qt::CaseInsensitive)
|| format.contains("g", Qt::CaseInsensitive))
labels << QString().sprintf(array, value);
else
labels << QString();
}
}
return labels;
}
QStringList ChartAxis::createLogValueLabels(qreal min, qreal max, qreal base, int ticks, const QString& format)
{
// Q_ASSERT(m_max > m_min);
// Q_ASSERT(ticks > 1);
QStringList labels;
int n = 0;
if (ticks > 1)
n = qMax(int(-qFloor(log10((max - min) / (ticks - 1)))), 0);
n++;
int firstTick;
if (base > 1)
firstTick = ceil(log10(min) / log10(base));
else
firstTick = ceil(log10(max) / log10(base));
if (format.isNull()) {
for (int i = firstTick; i < ticks + firstTick; i++) {
qreal value = qPow(base, i);
labels << QString::number(value, 'f', n);
}
} else {
QByteArray array = format.toLatin1();
for (int i = firstTick; i < ticks + firstTick; i++) {
qreal value = qPow(base, i);
if (format.contains("d")
|| format.contains("i")
|| format.contains("c"))
labels << QString().sprintf(array, (qint64)value);
else if (format.contains("u")
|| format.contains("o")
|| format.contains("x", Qt::CaseInsensitive))
labels << QString().sprintf(array, (quint64)value);
else if (format.contains("f", Qt::CaseInsensitive)
|| format.contains("e", Qt::CaseInsensitive)
|| format.contains("g", Qt::CaseInsensitive))
labels << QString().sprintf(array, value);
else
labels << QString();
}
}
return labels;
}
QStringList ChartAxis::createDateTimeLabels(qreal min, qreal max,int ticks,const QString& format)
{
QStringList labels;
if (max <= min || ticks < 1) {
return labels;
}
int n = qMax(int(-floor(log10((max - min) / (ticks - 1)))), 0);
n++;
for (int i = 0; i < ticks; i++) {
qreal value = min + (i * (max - min) / (ticks - 1));
labels << QDateTime::fromMSecsSinceEpoch(value).toString(format);
}
return labels;
}
QRect ChartAxis::labelBoundingRect(const QFontMetrics &fn, const QString &label) const
{
QRect boundingRect = fn.boundingRect(label);
// Take label rotation into account
if (m_labelsAngle) {
QTransform transform;
transform.rotate(m_labelsAngle);
boundingRect = transform.mapRect(boundingRect);
}
return boundingRect;
}
#include "moc_chartaxis_p.cpp"
QTCOMMERCIALCHART_END_NAMESPACE