##// END OF EJS Templates
Fix zooming in crash...
Fix zooming in crash The bounding rect has to fit inside int limits when zooming in, meaning that the height and width both have to be within the int limits (INT_MAX). QWidget::update() uses a region (based on bounding rect) that has to be compatible with QRect. The geometry change is only done if the bounding rect fulfills this requirement. Task-number: QTRD-1907 Change-Id: I4e874de355390c5fc983ac1e0976bf0d21e2d10c Reviewed-by: Miikka Heikkinen <miikka.heikkinen@digia.com>

File last commit:

r2603:1e4c29e111d4
r2603:1e4c29e111d4
Show More
linechartitem.cpp
385 lines | 16.8 KiB | text/x-c | CppLexer
Jani Honkonen
Add license headers
r794 /****************************************************************************
**
Miikka Heikkinen
Fixed the copyright year 2012 -> 2013
r2432 ** Copyright (C) 2013 Digia Plc
Jani Honkonen
Add license headers
r794 ** All rights reserved.
** For any questions to Digia, please use contact form at http://qt.digia.com
**
Miikka Heikkinen
Qt Commercial -> Qt Enterprise...
r2574 ** This file is part of the Qt Enterprise Charts Add-on.
Jani Honkonen
Add license headers
r794 **
** $QT_BEGIN_LICENSE$
Miikka Heikkinen
Qt Commercial -> Qt Enterprise...
r2574 ** Licensees holding valid Qt Enterprise licenses may use this file in
** accordance with the Qt Enterprise License Agreement provided with the
Jani Honkonen
Add license headers
r794 ** 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$
**
****************************************************************************/
Michal Klocek
Fix naming convention for lineseries...
r144 #include "linechartitem_p.h"
Michal Klocek
Rename QLineChartSeries to QLineSeries
r349 #include "qlineseries.h"
Michal Klocek
Adds big fat pimpl to series classes...
r938 #include "qlineseries_p.h"
Michal Klocek
Refactors qchart , adds line animation...
r131 #include "chartpresenter_p.h"
Miikka Heikkinen
Add Polar chart support...
r2483 #include "polardomain_p.h"
Michal Klocek
Refactor current draft to fit int current design specs...
r21 #include <QPainter>
Michal Klocek
Refactor xychartitem -> xychart
r1218 #include <QGraphicsSceneMouseEvent>
Michal Klocek
Fix naming convention for lineseries...
r144
Tero Ahola
Renamed to QtCommercialChart
r30 QTCOMMERCIALCHART_BEGIN_NAMESPACE
Michal Klocek
Refactor current draft to fit int current design specs...
r21
Tero Ahola
Fixed area series paint bug caused by mouse event fix on line series
r1792 const qreal mouseEventMinWidth(12);
Michal Klocek
Adds cliping and proper zoom handling for line chart
r150
Miikka Heikkinen
Add Polar chart support...
r2483 LineChartItem::LineChartItem(QLineSeries *series, QGraphicsItem *item)
Michal Klocek
Refactors internals...
r2273 : XYChart(series,item),
Jani Honkonen
astyle and manual coding style fixes for src-folder
r2097 m_series(series),
Miikka Heikkinen
Add Polar chart support...
r2483 m_pointsVisible(false),
m_chartType(QChart::ChartTypeUndefined)
Michal Klocek
Refactor current draft to fit int current design specs...
r21 {
Marek Rosa
Added hovered signal to QLineSeries. Updated callout example
r2255 setAcceptHoverEvents(true);
Michal Klocek
Adds ZOrder enum to presenter
r262 setZValue(ChartPresenter::LineChartZValue);
Jani Honkonen
astyle and manual coding style fixes for src-folder
r2097 QObject::connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
Tero Ahola
Visible property to abstract series
r1342 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
Tero Ahola
Added opacity property to QAbstractSeries
r2067 QObject::connect(series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
Michal Klocek
Refactor domain model...
r439 handleUpdated();
Michal Klocek
Refactor current draft to fit int current design specs...
r21 }
Michal Klocek
Fix naming convention for lineseries...
r144 QRectF LineChartItem::boundingRect() const
Tero Ahola
Refactored themes; now enabled for line, scatter and pies...
r103 {
Jani Honkonen
astyle and manual coding style fixes for src-folder
r2097 return m_rect;
Tero Ahola
Refactored themes; now enabled for line, scatter and pies...
r103 }
Michal Klocek
Refactora axis and line chart to use graphics items insted of painter.
r85
Michal Klocek
Fix naming convention for lineseries...
r144 QPainterPath LineChartItem::shape() const
Michal Klocek
Add zoom support...
r67 {
Miikka Heikkinen
Add Polar chart support...
r2483 return m_shapePath;
Michal Klocek
Refactors qchart , adds line animation...
r131 }
Michal Klocek
Refactora axis and line chart to use graphics items insted of painter.
r85
Michal Klocek
Refactors animation handling for xyseries
r1217 void LineChartItem::updateGeometry()
Michal Klocek
Refactors qchart , adds line animation...
r131 {
Michal Klocek
Fix line series graphic arefacts
r1819 m_points = geometryPoints();
Miikka Heikkinen
Add Polar chart support...
r2483 const QVector<QPointF> &points = m_points;
Michal Klocek
Refactors animation handling for xyseries
r1217
Miikka Heikkinen
Add Polar chart support...
r2483 if (points.size() == 0) {
Michal Klocek
Fix last post remove in qlineseries
r1269 prepareGeometryChange();
Miikka Heikkinen
Add Polar chart support...
r2483 m_fullPath = QPainterPath();
Miikka Heikkinen
Fix lineseries points related drawing problems...
r2451 m_linePath = QPainterPath();
Michal Klocek
Fix last post remove in qlineseries
r1269 m_rect = QRect();
Marek Rosa
Added support for adding and removing data with model. Updated the example
r545 return;
}
Michal Klocek
Refactors linechart update calls
r464
Miikka Heikkinen
Add Polar chart support...
r2483 QPainterPath linePath;
QPainterPath fullPath;
// Use worst case scenario to determine required margin.
qreal margin = m_linePen.width() * 1.42;
Michal Klocek
Refactors linechart update calls
r464
Miikka Heikkinen
Add Polar chart support...
r2483 // Area series use component line series that aren't necessarily added to the chart themselves,
// so check if chart type is forced before trying to obtain it from the chart.
QChart::ChartType chartType = m_chartType;
if (chartType == QChart::ChartTypeUndefined)
chartType = m_series->chart()->chartType();
Michal Klocek
Fix line series graphic arefacts
r1819
Miikka Heikkinen
Add Polar chart support...
r2483 // For polar charts, we need special handling for angular (horizontal)
// points that are off-grid.
if (chartType == QChart::ChartTypePolar) {
QPainterPath linePathLeft;
QPainterPath linePathRight;
QPainterPath *currentSegmentPath = 0;
QPainterPath *previousSegmentPath = 0;
qreal minX = domain()->minX();
qreal maxX = domain()->maxX();
qreal minY = domain()->minY();
Miikka Heikkinen
Refactor the new QXYSeries::pointAt() -> QXYSeries::at()...
r2491 QPointF currentSeriesPoint = m_series->at(0);
Miikka Heikkinen
Add Polar chart support...
r2483 QPointF currentGeometryPoint = points.at(0);
QPointF previousGeometryPoint = points.at(0);
Michal Klocek
Fix line series graphic arefacts
r1819 int size = m_linePen.width();
Miikka Heikkinen
Add Polar chart support...
r2483 bool pointOffGrid = false;
bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
qreal domainRadius = domain()->size().height() / 2.0;
const QPointF centerPoint(domainRadius, domainRadius);
if (!previousPointWasOffGrid) {
fullPath.moveTo(points.at(0));
if (m_pointsVisible && currentSeriesPoint.y() >= minY) {
// Do not draw ellipses for points below minimum Y.
linePath.addEllipse(points.at(0), size, size);
fullPath.addEllipse(points.at(0), size, size);
linePath.moveTo(points.at(0));
fullPath.moveTo(points.at(0));
}
Michal Klocek
Fix line series graphic arefacts
r1819 }
Miikka Heikkinen
Add Polar chart support...
r2483 qreal leftMarginLine = centerPoint.x() - margin;
qreal rightMarginLine = centerPoint.x() + margin;
qreal horizontal = centerPoint.y();
Michal Klocek
Refactors linechart update calls
r464
Miikka Heikkinen
Fix crash when adding/removing points during animation...
r2489 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
const int seriesLastIndex = m_series->count() - 1;
Miikka Heikkinen
Add Polar chart support...
r2483 for (int i = 1; i < points.size(); i++) {
// Interpolating line fragments would be ugly when thick pen is used,
// so we work around it by utilizing three separate
// paths for line segments and clip those with custom regions at paint time.
// "Right" path contains segments that cross the axis line with visible point on the
// right side of the axis line, as well as segments that have one point within the margin
// on the right side of the axis line and another point on the right side of the chart.
// "Left" path contains points with similarly on the left side.
// "Full" path contains rest of the points.
// This doesn't yield perfect results always. E.g. when segment covers more than 90
// degrees and both of the points are within the margin, one in the top half and one in the
// bottom half of the chart, the bottom one gets clipped incorrectly.
// However, this should be rare occurrence in any sensible chart.
Miikka Heikkinen
Refactor the new QXYSeries::pointAt() -> QXYSeries::at()...
r2491 currentSeriesPoint = m_series->at(qMin(seriesLastIndex, i));
Miikka Heikkinen
Add Polar chart support...
r2483 currentGeometryPoint = points.at(i);
pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
// Draw something unless both off-grid
if (!pointOffGrid || !previousPointWasOffGrid) {
QPointF intersectionPoint;
qreal y;
if (pointOffGrid != previousPointWasOffGrid) {
if (currentGeometryPoint.x() == previousGeometryPoint.x()) {
y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) / 2.0;
} else {
qreal ratio = (centerPoint.x() - currentGeometryPoint.x()) / (currentGeometryPoint.x() - previousGeometryPoint.x());
y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) * ratio;
}
intersectionPoint = QPointF(centerPoint.x(), y);
}
bool dummyOk; // We know points are ok, but this is needed
qreal currentAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(currentSeriesPoint.x(), dummyOk);
Miikka Heikkinen
Refactor the new QXYSeries::pointAt() -> QXYSeries::at()...
r2491 qreal previousAngle = static_cast<PolarDomain *>(domain())->toAngularCoordinate(m_series->at(i - 1).x(), dummyOk);
Miikka Heikkinen
Add Polar chart support...
r2483
if ((qAbs(currentAngle - previousAngle) > 180.0)) {
// If the angle between two points is over 180 degrees (half X range),
// any direct segment between them becomes meaningless.
// In this case two line segments are drawn instead, from previous
// point to the center and from center to current point.
if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine))
&& previousGeometryPoint.y() < horizontal) {
currentSegmentPath = &linePathRight;
} else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine))
&& previousGeometryPoint.y() < horizontal) {
currentSegmentPath = &linePathLeft;
} else if (previousAngle > 0.0 && previousAngle < 360.0) {
currentSegmentPath = &linePath;
} else {
currentSegmentPath = 0;
}
if (currentSegmentPath) {
if (previousSegmentPath != currentSegmentPath)
currentSegmentPath->moveTo(previousGeometryPoint);
if (previousPointWasOffGrid)
fullPath.moveTo(intersectionPoint);
currentSegmentPath->lineTo(centerPoint);
fullPath.lineTo(centerPoint);
}
previousSegmentPath = currentSegmentPath;
if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine))
&& currentGeometryPoint.y() < horizontal) {
currentSegmentPath = &linePathRight;
} else if ((currentAngle > 360.0 || (currentAngle > 180.0 &&currentGeometryPoint.x() > leftMarginLine))
&& currentGeometryPoint.y() < horizontal) {
currentSegmentPath = &linePathLeft;
} else if (currentAngle > 0.0 && currentAngle < 360.0) {
currentSegmentPath = &linePath;
} else {
currentSegmentPath = 0;
}
if (currentSegmentPath) {
if (previousSegmentPath != currentSegmentPath)
currentSegmentPath->moveTo(centerPoint);
if (!previousSegmentPath)
fullPath.moveTo(centerPoint);
currentSegmentPath->lineTo(currentGeometryPoint);
if (pointOffGrid)
fullPath.lineTo(intersectionPoint);
else
fullPath.lineTo(currentGeometryPoint);
}
} else {
if (previousAngle < 0.0 || currentAngle < 0.0
|| ((previousAngle <= 180.0 && currentAngle <= 180.0)
&& ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal)
|| (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) {
currentSegmentPath = &linePathRight;
} else if (previousAngle > 360.0 || currentAngle > 360.0
|| ((previousAngle > 180.0 && currentAngle > 180.0)
&& ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal)
|| (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) {
currentSegmentPath = &linePathLeft;
} else {
currentSegmentPath = &linePath;
}
if (currentSegmentPath != previousSegmentPath)
currentSegmentPath->moveTo(previousGeometryPoint);
if (previousPointWasOffGrid)
fullPath.moveTo(intersectionPoint);
if (pointOffGrid)
fullPath.lineTo(intersectionPoint);
else
fullPath.lineTo(currentGeometryPoint);
currentSegmentPath->lineTo(currentGeometryPoint);
}
} else {
currentSegmentPath = 0;
}
previousPointWasOffGrid = pointOffGrid;
if (m_pointsVisible && !pointOffGrid && currentSeriesPoint.y() >= minY) {
linePath.addEllipse(points.at(i), size, size);
fullPath.addEllipse(points.at(i), size, size);
linePath.moveTo(points.at(i));
fullPath.moveTo(points.at(i));
}
previousSegmentPath = currentSegmentPath;
previousGeometryPoint = currentGeometryPoint;
}
m_linePathPolarRight = linePathRight;
m_linePathPolarLeft = linePathLeft;
// Note: This construction of m_fullpath is not perfect. The partial segments that are
// outside left/right clip regions at axis boundary still generate hover/click events,
// because shape doesn't get clipped. It doesn't seem possible to do sensibly.
} else { // not polar
linePath.moveTo(points.at(0));
if (m_pointsVisible) {
int size = m_linePen.width();
linePath.addEllipse(points.at(0), size, size);
linePath.moveTo(points.at(0));
for (int i = 1; i < points.size(); i++) {
linePath.lineTo(points.at(i));
linePath.addEllipse(points.at(i), size, size);
linePath.moveTo(points.at(i));
}
} else {
for (int i = 1; i < points.size(); i++)
linePath.lineTo(points.at(i));
}
fullPath = linePath;
}
Tero Ahola
Finetuning shape of line chart item to remove drawing artifacts
r1823
Michal Klocek
Fix line series graphic arefacts
r1819 QPainterPathStroker stroker;
Tero Ahola
Finetuning shape of line chart item to remove drawing artifacts
r1823 // QPainter::drawLine does not respect join styles, for example BevelJoin becomes MiterJoin.
// This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and
// multiply line width with square root of two when defining shape and bounding rectangle.
Miikka Heikkinen
Add Polar chart support...
r2483 stroker.setWidth(margin);
Tero Ahola
Finetuning shape of line chart item to remove drawing artifacts
r1823 stroker.setJoinStyle(Qt::MiterJoin);
stroker.setCapStyle(Qt::SquareCap);
Michal Klocek
Adds magic number to fix some drawLine shape inaccuracy
r1821 stroker.setMiterLimit(m_linePen.miterLimit());
Tero Ahola
Fixed paint and mouse event issues with QLineSeries...
r1791
Titta Heikkala
Fix zooming in crash...
r2603 QPainterPath checkShapePath = stroker.createStroke(fullPath);
// Only zoom in if the bounding rects of the paths fit inside int limits. QWidget::update() uses
// a region that has to be compatible with QRect.
if (checkShapePath.boundingRect().height() <= INT_MAX
&& checkShapePath.boundingRect().width() <= INT_MAX
&& linePath.boundingRect().height() <= INT_MAX
&& linePath.boundingRect().width() <= INT_MAX
&& fullPath.boundingRect().height() <= INT_MAX
&& fullPath.boundingRect().width() <= INT_MAX) {
prepareGeometryChange();
Tero Ahola
Fixed paint and mouse event issues with QLineSeries...
r1791
Titta Heikkala
Fix zooming in crash...
r2603 m_linePath = linePath;
m_fullPath = fullPath;
m_shapePath = checkShapePath;
Miikka Heikkinen
Add Polar chart support...
r2483
Titta Heikkala
Fix zooming in crash...
r2603 m_rect = m_shapePath.boundingRect();
} else {
update();
}
Michal Klocek
Refactor current draft to fit int current design specs...
r21 }
Michal Klocek
Adds updated handling for line series
r392 void LineChartItem::handleUpdated()
Michal Klocek
Rewrite animation hadnling in line series...
r389 {
Miikka Heikkinen
Fix lineseries points related drawing problems...
r2451 // If points visiblity has changed, a geometry update is needed.
// Also, if pen changes when points are visible, geometry update is needed.
bool doGeometryUpdate =
(m_pointsVisible != m_series->pointsVisible())
|| (m_series->pointsVisible() && (m_linePen != m_series->pen()));
Tero Ahola
Fixed isVisible implementation in XY series
r1346 setVisible(m_series->isVisible());
Tero Ahola
Added opacity property to QAbstractSeries
r2067 setOpacity(m_series->opacity());
Michal Klocek
Adds clicked(Point) to lineSeries, changes visible points handling
r544 m_pointsVisible = m_series->pointsVisible();
m_linePen = m_series->pen();
Miikka Heikkinen
Fix lineseries points related drawing problems...
r2451 if (doGeometryUpdate)
updateGeometry();
Michal Klocek
Refactored for MVP...
r139 update();
}
Michal Klocek
Fix commit 7b90ec69ce9a3353820d295c222ef3f79537484d
r391 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
{
Tero Ahola
Squashed bunch of warnings
r611 Q_UNUSED(widget)
Q_UNUSED(option)
Miikka Heikkinen
Add Polar chart support...
r2483 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
Tero Ahola
Fixed clip rect bug in QLineSeries
r1828 painter->save();
Tero Ahola
Fixed isVisible implementation in XY series
r1346 painter->setPen(m_linePen);
Miikka Heikkinen
Add Polar chart support...
r2483 bool alwaysUsePath = false;
if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
qreal halfWidth = domain()->size().width() / 2.0;
QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
painter->setClipRegion(clipRegionLeft);
painter->drawPath(m_linePathPolarLeft);
painter->setClipRegion(clipRegionRight);
painter->drawPath(m_linePathPolarRight);
painter->setClipRegion(fullPolarClipRegion);
alwaysUsePath = true; // required for proper clipping
} else {
painter->setClipRect(clipRect);
}
Michal Klocek
Fix line series graphic arefacts
r1819
if (m_pointsVisible) {
Miikka Heikkinen
Fixed pen style pattern continuity for line series...
r2457 painter->setBrush(m_linePen.color());
Michal Klocek
Fix line series graphic arefacts
r1819 painter->drawPath(m_linePath);
Jani Honkonen
astyle and manual coding style fixes for src-folder
r2097 } else {
Miikka Heikkinen
Fixed pen style pattern continuity for line series...
r2457 painter->setBrush(QBrush(Qt::NoBrush));
Miikka Heikkinen
Add Polar chart support...
r2483 if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) {
Miikka Heikkinen
Fixed pen style pattern continuity for line series...
r2457 // If pen style is not solid line, always fall back to path painting
// to ensure proper continuity of the pattern
painter->drawPath(m_linePath);
} else {
for (int i(1); i < m_points.size(); i++)
painter->drawLine(m_points.at(i - 1), m_points.at(i));
}
Michal Klocek
Adds clicked(Point) to lineSeries, changes visible points handling
r544 }
Miikka Heikkinen
Fixed pen style pattern continuity for line series...
r2457
Tero Ahola
Fixed clip rect bug in QLineSeries
r1828 painter->restore();
Michal Klocek
Fix commit 7b90ec69ce9a3353820d295c222ef3f79537484d
r391 }
Michal Klocek
Refactor xychartitem -> xychart
r1218 void LineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
{
Michal Klocek
Refactors internals...
r2273 emit XYChart::clicked(domain()->calculateDomainPoint(event->pos()));
Michal Klocek
Fixes mouse handling in base class of chartseries
r1747 QGraphicsItem::mousePressEvent(event);
Michal Klocek
Refactor xychartitem -> xychart
r1218 }
Marek Rosa
Added hovered signal to QLineSeries. Updated callout example
r2255 void LineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
{
Michal Klocek
Refactors internals...
r2273 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
Marek Rosa
Negative values with log axis handled
r2356 // event->accept();
QGraphicsItem::hoverEnterEvent(event);
Marek Rosa
Added hovered signal to QLineSeries. Updated callout example
r2255 }
void LineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
{
Michal Klocek
Refactors internals...
r2273 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
Marek Rosa
Negative values with log axis handled
r2356 // event->accept();
QGraphicsItem::hoverEnterEvent(event);
Marek Rosa
Added hovered signal to QLineSeries. Updated callout example
r2255 }
Michal Klocek
Fix naming convention for lineseries...
r144 #include "moc_linechartitem_p.cpp"
Michal Klocek
Refactors qchart , adds line animation...
r131
Tero Ahola
Renamed to QtCommercialChart
r30 QTCOMMERCIALCHART_END_NAMESPACE