##// END OF EJS Templates
Revert previous change to LineChartItem...
Titta Heikkala -
r2818:d27e6a35ae04
parent child
Show More
@@ -1,438 +1,441
1 /****************************************************************************
1 /****************************************************************************
2 **
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
6 **
7 ** This file is part of the Qt Charts module.
7 ** This file is part of the Qt Charts module.
8 **
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
12 ** agreement between you and The Qt Company.
13 **
13 **
14 ** If you have questions regarding the use of this file, please use
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
15 ** contact form at http://qt.io
16 **
16 **
17 ****************************************************************************/
17 ****************************************************************************/
18
18
19 #include <private/linechartitem_p.h>
19 #include <private/linechartitem_p.h>
20 #include <QtCharts/QLineSeries>
20 #include <QtCharts/QLineSeries>
21 #include <private/qlineseries_p.h>
21 #include <private/qlineseries_p.h>
22 #include <private/chartpresenter_p.h>
22 #include <private/chartpresenter_p.h>
23 #include <private/polardomain_p.h>
23 #include <private/polardomain_p.h>
24 #include <private/chartthememanager_p.h>
24 #include <private/chartthememanager_p.h>
25 #include <private/charttheme_p.h>
25 #include <private/charttheme_p.h>
26 #include <QtGui/QPainter>
26 #include <QtGui/QPainter>
27 #include <QtWidgets/QGraphicsSceneMouseEvent>
27 #include <QtWidgets/QGraphicsSceneMouseEvent>
28
28
29 QT_CHARTS_BEGIN_NAMESPACE
29 QT_CHARTS_BEGIN_NAMESPACE
30
30
31 const qreal mouseEventMinWidth(12);
31 const qreal mouseEventMinWidth(12);
32
32
33 LineChartItem::LineChartItem(QLineSeries *series, QGraphicsItem *item)
33 LineChartItem::LineChartItem(QLineSeries *series, QGraphicsItem *item)
34 : XYChart(series,item),
34 : XYChart(series,item),
35 m_series(series),
35 m_series(series),
36 m_pointsVisible(false),
36 m_pointsVisible(false),
37 m_chartType(QChart::ChartTypeUndefined),
37 m_chartType(QChart::ChartTypeUndefined),
38 m_pointLabelsVisible(false),
38 m_pointLabelsVisible(false),
39 m_pointLabelsFormat(series->pointLabelsFormat()),
39 m_pointLabelsFormat(series->pointLabelsFormat()),
40 m_pointLabelsFont(series->pointLabelsFont()),
40 m_pointLabelsFont(series->pointLabelsFont()),
41 m_pointLabelsColor(series->pointLabelsColor()),
41 m_pointLabelsColor(series->pointLabelsColor()),
42 m_pointLabelsClipping(true),
42 m_pointLabelsClipping(true),
43 m_mousePressed(false)
43 m_mousePressed(false)
44 {
44 {
45 setAcceptHoverEvents(true);
45 setAcceptHoverEvents(true);
46 setFlag(QGraphicsItem::ItemIsSelectable);
46 setFlag(QGraphicsItem::ItemIsSelectable);
47 setZValue(ChartPresenter::LineChartZValue);
47 setZValue(ChartPresenter::LineChartZValue);
48 QObject::connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
48 QObject::connect(series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
49 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
49 QObject::connect(series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
50 QObject::connect(series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
50 QObject::connect(series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
51 QObject::connect(series, SIGNAL(pointLabelsFormatChanged(QString)),
51 QObject::connect(series, SIGNAL(pointLabelsFormatChanged(QString)),
52 this, SLOT(handleUpdated()));
52 this, SLOT(handleUpdated()));
53 QObject::connect(series, SIGNAL(pointLabelsVisibilityChanged(bool)),
53 QObject::connect(series, SIGNAL(pointLabelsVisibilityChanged(bool)),
54 this, SLOT(handleUpdated()));
54 this, SLOT(handleUpdated()));
55 QObject::connect(series, SIGNAL(pointLabelsFontChanged(QFont)), this, SLOT(handleUpdated()));
55 QObject::connect(series, SIGNAL(pointLabelsFontChanged(QFont)), this, SLOT(handleUpdated()));
56 QObject::connect(series, SIGNAL(pointLabelsColorChanged(QColor)), this, SLOT(handleUpdated()));
56 QObject::connect(series, SIGNAL(pointLabelsColorChanged(QColor)), this, SLOT(handleUpdated()));
57 QObject::connect(series, SIGNAL(pointLabelsClippingChanged(bool)), this, SLOT(handleUpdated()));
57 QObject::connect(series, SIGNAL(pointLabelsClippingChanged(bool)), this, SLOT(handleUpdated()));
58 handleUpdated();
58 handleUpdated();
59 }
59 }
60
60
61 QRectF LineChartItem::boundingRect() const
61 QRectF LineChartItem::boundingRect() const
62 {
62 {
63 return m_rect;
63 return m_rect;
64 }
64 }
65
65
66 QPainterPath LineChartItem::shape() const
66 QPainterPath LineChartItem::shape() const
67 {
67 {
68 return m_shapePath;
68 return m_shapePath;
69 }
69 }
70
70
71 void LineChartItem::updateGeometry()
71 void LineChartItem::updateGeometry()
72 {
72 {
73 const QVector<QPointF> &points = geometryPoints();
73 // Store the points to a local variable so that the old line gets properly cleared
74 // when animation starts.
75 m_linePoints = geometryPoints();
76 const QVector<QPointF> &points = m_linePoints;
74
77
75 if (points.size() == 0) {
78 if (points.size() == 0) {
76 prepareGeometryChange();
79 prepareGeometryChange();
77 m_fullPath = QPainterPath();
80 m_fullPath = QPainterPath();
78 m_linePath = QPainterPath();
81 m_linePath = QPainterPath();
79 m_rect = QRect();
82 m_rect = QRect();
80 return;
83 return;
81 }
84 }
82
85
83 QPainterPath linePath;
86 QPainterPath linePath;
84 QPainterPath fullPath;
87 QPainterPath fullPath;
85 // Use worst case scenario to determine required margin.
88 // Use worst case scenario to determine required margin.
86 qreal margin = m_linePen.width() * 1.42;
89 qreal margin = m_linePen.width() * 1.42;
87
90
88 // Area series use component line series that aren't necessarily added to the chart themselves,
91 // Area series use component line series that aren't necessarily added to the chart themselves,
89 // so check if chart type is forced before trying to obtain it from the chart.
92 // so check if chart type is forced before trying to obtain it from the chart.
90 QChart::ChartType chartType = m_chartType;
93 QChart::ChartType chartType = m_chartType;
91 if (chartType == QChart::ChartTypeUndefined)
94 if (chartType == QChart::ChartTypeUndefined)
92 chartType = m_series->chart()->chartType();
95 chartType = m_series->chart()->chartType();
93
96
94 // For polar charts, we need special handling for angular (horizontal)
97 // For polar charts, we need special handling for angular (horizontal)
95 // points that are off-grid.
98 // points that are off-grid.
96 if (chartType == QChart::ChartTypePolar) {
99 if (chartType == QChart::ChartTypePolar) {
97 QPainterPath linePathLeft;
100 QPainterPath linePathLeft;
98 QPainterPath linePathRight;
101 QPainterPath linePathRight;
99 QPainterPath *currentSegmentPath = 0;
102 QPainterPath *currentSegmentPath = 0;
100 QPainterPath *previousSegmentPath = 0;
103 QPainterPath *previousSegmentPath = 0;
101 qreal minX = domain()->minX();
104 qreal minX = domain()->minX();
102 qreal maxX = domain()->maxX();
105 qreal maxX = domain()->maxX();
103 qreal minY = domain()->minY();
106 qreal minY = domain()->minY();
104 QPointF currentSeriesPoint = m_series->at(0);
107 QPointF currentSeriesPoint = m_series->at(0);
105 QPointF currentGeometryPoint = points.at(0);
108 QPointF currentGeometryPoint = points.at(0);
106 QPointF previousGeometryPoint = points.at(0);
109 QPointF previousGeometryPoint = points.at(0);
107 int size = m_linePen.width();
110 int size = m_linePen.width();
108 bool pointOffGrid = false;
111 bool pointOffGrid = false;
109 bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
112 bool previousPointWasOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
110
113
111 qreal domainRadius = domain()->size().height() / 2.0;
114 qreal domainRadius = domain()->size().height() / 2.0;
112 const QPointF centerPoint(domainRadius, domainRadius);
115 const QPointF centerPoint(domainRadius, domainRadius);
113
116
114 if (!previousPointWasOffGrid) {
117 if (!previousPointWasOffGrid) {
115 fullPath.moveTo(points.at(0));
118 fullPath.moveTo(points.at(0));
116 if (m_pointsVisible && currentSeriesPoint.y() >= minY) {
119 if (m_pointsVisible && currentSeriesPoint.y() >= minY) {
117 // Do not draw ellipses for points below minimum Y.
120 // Do not draw ellipses for points below minimum Y.
118 linePath.addEllipse(points.at(0), size, size);
121 linePath.addEllipse(points.at(0), size, size);
119 fullPath.addEllipse(points.at(0), size, size);
122 fullPath.addEllipse(points.at(0), size, size);
120 linePath.moveTo(points.at(0));
123 linePath.moveTo(points.at(0));
121 fullPath.moveTo(points.at(0));
124 fullPath.moveTo(points.at(0));
122 }
125 }
123 }
126 }
124
127
125 qreal leftMarginLine = centerPoint.x() - margin;
128 qreal leftMarginLine = centerPoint.x() - margin;
126 qreal rightMarginLine = centerPoint.x() + margin;
129 qreal rightMarginLine = centerPoint.x() + margin;
127 qreal horizontal = centerPoint.y();
130 qreal horizontal = centerPoint.y();
128
131
129 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
132 // See ScatterChartItem::updateGeometry() for explanation why seriesLastIndex is needed
130 const int seriesLastIndex = m_series->count() - 1;
133 const int seriesLastIndex = m_series->count() - 1;
131
134
132 for (int i = 1; i < points.size(); i++) {
135 for (int i = 1; i < points.size(); i++) {
133 // Interpolating line fragments would be ugly when thick pen is used,
136 // Interpolating line fragments would be ugly when thick pen is used,
134 // so we work around it by utilizing three separate
137 // so we work around it by utilizing three separate
135 // paths for line segments and clip those with custom regions at paint time.
138 // paths for line segments and clip those with custom regions at paint time.
136 // "Right" path contains segments that cross the axis line with visible point on the
139 // "Right" path contains segments that cross the axis line with visible point on the
137 // right side of the axis line, as well as segments that have one point within the margin
140 // right side of the axis line, as well as segments that have one point within the margin
138 // on the right side of the axis line and another point on the right side of the chart.
141 // on the right side of the axis line and another point on the right side of the chart.
139 // "Left" path contains points with similarly on the left side.
142 // "Left" path contains points with similarly on the left side.
140 // "Full" path contains rest of the points.
143 // "Full" path contains rest of the points.
141 // This doesn't yield perfect results always. E.g. when segment covers more than 90
144 // This doesn't yield perfect results always. E.g. when segment covers more than 90
142 // degrees and both of the points are within the margin, one in the top half and one in the
145 // degrees and both of the points are within the margin, one in the top half and one in the
143 // bottom half of the chart, the bottom one gets clipped incorrectly.
146 // bottom half of the chart, the bottom one gets clipped incorrectly.
144 // However, this should be rare occurrence in any sensible chart.
147 // However, this should be rare occurrence in any sensible chart.
145 currentSeriesPoint = m_series->at(qMin(seriesLastIndex, i));
148 currentSeriesPoint = m_series->at(qMin(seriesLastIndex, i));
146 currentGeometryPoint = points.at(i);
149 currentGeometryPoint = points.at(i);
147 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
150 pointOffGrid = (currentSeriesPoint.x() < minX || currentSeriesPoint.x() > maxX);
148
151
149 // Draw something unless both off-grid
152 // Draw something unless both off-grid
150 if (!pointOffGrid || !previousPointWasOffGrid) {
153 if (!pointOffGrid || !previousPointWasOffGrid) {
151 QPointF intersectionPoint;
154 QPointF intersectionPoint;
152 qreal y;
155 qreal y;
153 if (pointOffGrid != previousPointWasOffGrid) {
156 if (pointOffGrid != previousPointWasOffGrid) {
154 if (currentGeometryPoint.x() == previousGeometryPoint.x()) {
157 if (currentGeometryPoint.x() == previousGeometryPoint.x()) {
155 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) / 2.0;
158 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) / 2.0;
156 } else {
159 } else {
157 qreal ratio = (centerPoint.x() - currentGeometryPoint.x()) / (currentGeometryPoint.x() - previousGeometryPoint.x());
160 qreal ratio = (centerPoint.x() - currentGeometryPoint.x()) / (currentGeometryPoint.x() - previousGeometryPoint.x());
158 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) * ratio;
161 y = currentGeometryPoint.y() + (currentGeometryPoint.y() - previousGeometryPoint.y()) * ratio;
159 }
162 }
160 intersectionPoint = QPointF(centerPoint.x(), y);
163 intersectionPoint = QPointF(centerPoint.x(), y);
161 }
164 }
162
165
163 bool dummyOk; // We know points are ok, but this is needed
166 bool dummyOk; // We know points are ok, but this is needed
164 qreal currentAngle = 0;
167 qreal currentAngle = 0;
165 qreal previousAngle = 0;
168 qreal previousAngle = 0;
166 if (const PolarDomain *pd = qobject_cast<const PolarDomain *>(domain())) {
169 if (const PolarDomain *pd = qobject_cast<const PolarDomain *>(domain())) {
167 currentAngle = pd->toAngularCoordinate(currentSeriesPoint.x(), dummyOk);
170 currentAngle = pd->toAngularCoordinate(currentSeriesPoint.x(), dummyOk);
168 previousAngle = pd->toAngularCoordinate(m_series->at(i - 1).x(), dummyOk);
171 previousAngle = pd->toAngularCoordinate(m_series->at(i - 1).x(), dummyOk);
169 } else {
172 } else {
170 qWarning() << Q_FUNC_INFO << "Unexpected domain: " << domain();
173 qWarning() << Q_FUNC_INFO << "Unexpected domain: " << domain();
171 }
174 }
172 if ((qAbs(currentAngle - previousAngle) > 180.0)) {
175 if ((qAbs(currentAngle - previousAngle) > 180.0)) {
173 // If the angle between two points is over 180 degrees (half X range),
176 // If the angle between two points is over 180 degrees (half X range),
174 // any direct segment between them becomes meaningless.
177 // any direct segment between them becomes meaningless.
175 // In this case two line segments are drawn instead, from previous
178 // In this case two line segments are drawn instead, from previous
176 // point to the center and from center to current point.
179 // point to the center and from center to current point.
177 if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine))
180 if ((previousAngle < 0.0 || (previousAngle <= 180.0 && previousGeometryPoint.x() < rightMarginLine))
178 && previousGeometryPoint.y() < horizontal) {
181 && previousGeometryPoint.y() < horizontal) {
179 currentSegmentPath = &linePathRight;
182 currentSegmentPath = &linePathRight;
180 } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine))
183 } else if ((previousAngle > 360.0 || (previousAngle > 180.0 && previousGeometryPoint.x() > leftMarginLine))
181 && previousGeometryPoint.y() < horizontal) {
184 && previousGeometryPoint.y() < horizontal) {
182 currentSegmentPath = &linePathLeft;
185 currentSegmentPath = &linePathLeft;
183 } else if (previousAngle > 0.0 && previousAngle < 360.0) {
186 } else if (previousAngle > 0.0 && previousAngle < 360.0) {
184 currentSegmentPath = &linePath;
187 currentSegmentPath = &linePath;
185 } else {
188 } else {
186 currentSegmentPath = 0;
189 currentSegmentPath = 0;
187 }
190 }
188
191
189 if (currentSegmentPath) {
192 if (currentSegmentPath) {
190 if (previousSegmentPath != currentSegmentPath)
193 if (previousSegmentPath != currentSegmentPath)
191 currentSegmentPath->moveTo(previousGeometryPoint);
194 currentSegmentPath->moveTo(previousGeometryPoint);
192 if (previousPointWasOffGrid)
195 if (previousPointWasOffGrid)
193 fullPath.moveTo(intersectionPoint);
196 fullPath.moveTo(intersectionPoint);
194
197
195 currentSegmentPath->lineTo(centerPoint);
198 currentSegmentPath->lineTo(centerPoint);
196 fullPath.lineTo(centerPoint);
199 fullPath.lineTo(centerPoint);
197 }
200 }
198
201
199 previousSegmentPath = currentSegmentPath;
202 previousSegmentPath = currentSegmentPath;
200
203
201 if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine))
204 if ((currentAngle < 0.0 || (currentAngle <= 180.0 && currentGeometryPoint.x() < rightMarginLine))
202 && currentGeometryPoint.y() < horizontal) {
205 && currentGeometryPoint.y() < horizontal) {
203 currentSegmentPath = &linePathRight;
206 currentSegmentPath = &linePathRight;
204 } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &&currentGeometryPoint.x() > leftMarginLine))
207 } else if ((currentAngle > 360.0 || (currentAngle > 180.0 &&currentGeometryPoint.x() > leftMarginLine))
205 && currentGeometryPoint.y() < horizontal) {
208 && currentGeometryPoint.y() < horizontal) {
206 currentSegmentPath = &linePathLeft;
209 currentSegmentPath = &linePathLeft;
207 } else if (currentAngle > 0.0 && currentAngle < 360.0) {
210 } else if (currentAngle > 0.0 && currentAngle < 360.0) {
208 currentSegmentPath = &linePath;
211 currentSegmentPath = &linePath;
209 } else {
212 } else {
210 currentSegmentPath = 0;
213 currentSegmentPath = 0;
211 }
214 }
212
215
213 if (currentSegmentPath) {
216 if (currentSegmentPath) {
214 if (previousSegmentPath != currentSegmentPath)
217 if (previousSegmentPath != currentSegmentPath)
215 currentSegmentPath->moveTo(centerPoint);
218 currentSegmentPath->moveTo(centerPoint);
216 if (!previousSegmentPath)
219 if (!previousSegmentPath)
217 fullPath.moveTo(centerPoint);
220 fullPath.moveTo(centerPoint);
218
221
219 currentSegmentPath->lineTo(currentGeometryPoint);
222 currentSegmentPath->lineTo(currentGeometryPoint);
220 if (pointOffGrid)
223 if (pointOffGrid)
221 fullPath.lineTo(intersectionPoint);
224 fullPath.lineTo(intersectionPoint);
222 else
225 else
223 fullPath.lineTo(currentGeometryPoint);
226 fullPath.lineTo(currentGeometryPoint);
224 }
227 }
225 } else {
228 } else {
226 if (previousAngle < 0.0 || currentAngle < 0.0
229 if (previousAngle < 0.0 || currentAngle < 0.0
227 || ((previousAngle <= 180.0 && currentAngle <= 180.0)
230 || ((previousAngle <= 180.0 && currentAngle <= 180.0)
228 && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal)
231 && ((previousGeometryPoint.x() < rightMarginLine && previousGeometryPoint.y() < horizontal)
229 || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) {
232 || (currentGeometryPoint.x() < rightMarginLine && currentGeometryPoint.y() < horizontal)))) {
230 currentSegmentPath = &linePathRight;
233 currentSegmentPath = &linePathRight;
231 } else if (previousAngle > 360.0 || currentAngle > 360.0
234 } else if (previousAngle > 360.0 || currentAngle > 360.0
232 || ((previousAngle > 180.0 && currentAngle > 180.0)
235 || ((previousAngle > 180.0 && currentAngle > 180.0)
233 && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal)
236 && ((previousGeometryPoint.x() > leftMarginLine && previousGeometryPoint.y() < horizontal)
234 || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) {
237 || (currentGeometryPoint.x() > leftMarginLine && currentGeometryPoint.y() < horizontal)))) {
235 currentSegmentPath = &linePathLeft;
238 currentSegmentPath = &linePathLeft;
236 } else {
239 } else {
237 currentSegmentPath = &linePath;
240 currentSegmentPath = &linePath;
238 }
241 }
239
242
240 if (currentSegmentPath != previousSegmentPath)
243 if (currentSegmentPath != previousSegmentPath)
241 currentSegmentPath->moveTo(previousGeometryPoint);
244 currentSegmentPath->moveTo(previousGeometryPoint);
242 if (previousPointWasOffGrid)
245 if (previousPointWasOffGrid)
243 fullPath.moveTo(intersectionPoint);
246 fullPath.moveTo(intersectionPoint);
244
247
245 if (pointOffGrid)
248 if (pointOffGrid)
246 fullPath.lineTo(intersectionPoint);
249 fullPath.lineTo(intersectionPoint);
247 else
250 else
248 fullPath.lineTo(currentGeometryPoint);
251 fullPath.lineTo(currentGeometryPoint);
249 currentSegmentPath->lineTo(currentGeometryPoint);
252 currentSegmentPath->lineTo(currentGeometryPoint);
250 }
253 }
251 } else {
254 } else {
252 currentSegmentPath = 0;
255 currentSegmentPath = 0;
253 }
256 }
254
257
255 previousPointWasOffGrid = pointOffGrid;
258 previousPointWasOffGrid = pointOffGrid;
256 if (m_pointsVisible && !pointOffGrid && currentSeriesPoint.y() >= minY) {
259 if (m_pointsVisible && !pointOffGrid && currentSeriesPoint.y() >= minY) {
257 linePath.addEllipse(points.at(i), size, size);
260 linePath.addEllipse(points.at(i), size, size);
258 fullPath.addEllipse(points.at(i), size, size);
261 fullPath.addEllipse(points.at(i), size, size);
259 linePath.moveTo(points.at(i));
262 linePath.moveTo(points.at(i));
260 fullPath.moveTo(points.at(i));
263 fullPath.moveTo(points.at(i));
261 }
264 }
262 previousSegmentPath = currentSegmentPath;
265 previousSegmentPath = currentSegmentPath;
263 previousGeometryPoint = currentGeometryPoint;
266 previousGeometryPoint = currentGeometryPoint;
264 }
267 }
265 m_linePathPolarRight = linePathRight;
268 m_linePathPolarRight = linePathRight;
266 m_linePathPolarLeft = linePathLeft;
269 m_linePathPolarLeft = linePathLeft;
267 // Note: This construction of m_fullpath is not perfect. The partial segments that are
270 // Note: This construction of m_fullpath is not perfect. The partial segments that are
268 // outside left/right clip regions at axis boundary still generate hover/click events,
271 // outside left/right clip regions at axis boundary still generate hover/click events,
269 // because shape doesn't get clipped. It doesn't seem possible to do sensibly.
272 // because shape doesn't get clipped. It doesn't seem possible to do sensibly.
270 } else { // not polar
273 } else { // not polar
271 linePath.moveTo(points.at(0));
274 linePath.moveTo(points.at(0));
272 if (m_pointsVisible) {
275 if (m_pointsVisible) {
273 int size = m_linePen.width();
276 int size = m_linePen.width();
274 linePath.addEllipse(points.at(0), size, size);
277 linePath.addEllipse(points.at(0), size, size);
275 linePath.moveTo(points.at(0));
278 linePath.moveTo(points.at(0));
276 for (int i = 1; i < points.size(); i++) {
279 for (int i = 1; i < points.size(); i++) {
277 linePath.lineTo(points.at(i));
280 linePath.lineTo(points.at(i));
278 linePath.addEllipse(points.at(i), size, size);
281 linePath.addEllipse(points.at(i), size, size);
279 linePath.moveTo(points.at(i));
282 linePath.moveTo(points.at(i));
280 }
283 }
281 } else {
284 } else {
282 for (int i = 1; i < points.size(); i++)
285 for (int i = 1; i < points.size(); i++)
283 linePath.lineTo(points.at(i));
286 linePath.lineTo(points.at(i));
284 }
287 }
285 fullPath = linePath;
288 fullPath = linePath;
286 }
289 }
287
290
288 QPainterPathStroker stroker;
291 QPainterPathStroker stroker;
289 // QPainter::drawLine does not respect join styles, for example BevelJoin becomes MiterJoin.
292 // QPainter::drawLine does not respect join styles, for example BevelJoin becomes MiterJoin.
290 // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and
293 // This is why we are prepared for the "worst case" scenario, i.e. use always MiterJoin and
291 // multiply line width with square root of two when defining shape and bounding rectangle.
294 // multiply line width with square root of two when defining shape and bounding rectangle.
292 stroker.setWidth(margin);
295 stroker.setWidth(margin);
293 stroker.setJoinStyle(Qt::MiterJoin);
296 stroker.setJoinStyle(Qt::MiterJoin);
294 stroker.setCapStyle(Qt::SquareCap);
297 stroker.setCapStyle(Qt::SquareCap);
295 stroker.setMiterLimit(m_linePen.miterLimit());
298 stroker.setMiterLimit(m_linePen.miterLimit());
296
299
297 QPainterPath checkShapePath = stroker.createStroke(fullPath);
300 QPainterPath checkShapePath = stroker.createStroke(fullPath);
298
301
299 // Only zoom in if the bounding rects of the paths fit inside int limits. QWidget::update() uses
302 // Only zoom in if the bounding rects of the paths fit inside int limits. QWidget::update() uses
300 // a region that has to be compatible with QRect.
303 // a region that has to be compatible with QRect.
301 if (checkShapePath.boundingRect().height() <= INT_MAX
304 if (checkShapePath.boundingRect().height() <= INT_MAX
302 && checkShapePath.boundingRect().width() <= INT_MAX
305 && checkShapePath.boundingRect().width() <= INT_MAX
303 && linePath.boundingRect().height() <= INT_MAX
306 && linePath.boundingRect().height() <= INT_MAX
304 && linePath.boundingRect().width() <= INT_MAX
307 && linePath.boundingRect().width() <= INT_MAX
305 && fullPath.boundingRect().height() <= INT_MAX
308 && fullPath.boundingRect().height() <= INT_MAX
306 && fullPath.boundingRect().width() <= INT_MAX) {
309 && fullPath.boundingRect().width() <= INT_MAX) {
307 prepareGeometryChange();
310 prepareGeometryChange();
308
311
309 m_linePath = linePath;
312 m_linePath = linePath;
310 m_fullPath = fullPath;
313 m_fullPath = fullPath;
311 m_shapePath = checkShapePath;
314 m_shapePath = checkShapePath;
312
315
313 m_rect = m_shapePath.boundingRect();
316 m_rect = m_shapePath.boundingRect();
314 } else {
317 } else {
315 update();
318 update();
316 }
319 }
317 }
320 }
318
321
319 void LineChartItem::handleUpdated()
322 void LineChartItem::handleUpdated()
320 {
323 {
321 // If points visibility has changed, a geometry update is needed.
324 // If points visibility has changed, a geometry update is needed.
322 // Also, if pen changes when points are visible, geometry update is needed.
325 // Also, if pen changes when points are visible, geometry update is needed.
323 bool doGeometryUpdate =
326 bool doGeometryUpdate =
324 (m_pointsVisible != m_series->pointsVisible())
327 (m_pointsVisible != m_series->pointsVisible())
325 || (m_series->pointsVisible() && (m_linePen != m_series->pen()));
328 || (m_series->pointsVisible() && (m_linePen != m_series->pen()));
326 setVisible(m_series->isVisible());
329 setVisible(m_series->isVisible());
327 setOpacity(m_series->opacity());
330 setOpacity(m_series->opacity());
328 m_pointsVisible = m_series->pointsVisible();
331 m_pointsVisible = m_series->pointsVisible();
329 m_linePen = m_series->pen();
332 m_linePen = m_series->pen();
330 m_pointLabelsFormat = m_series->pointLabelsFormat();
333 m_pointLabelsFormat = m_series->pointLabelsFormat();
331 m_pointLabelsVisible = m_series->pointLabelsVisible();
334 m_pointLabelsVisible = m_series->pointLabelsVisible();
332 m_pointLabelsFont = m_series->pointLabelsFont();
335 m_pointLabelsFont = m_series->pointLabelsFont();
333 m_pointLabelsColor = m_series->pointLabelsColor();
336 m_pointLabelsColor = m_series->pointLabelsColor();
334 m_pointLabelsClipping = m_series->pointLabelsClipping();
337 m_pointLabelsClipping = m_series->pointLabelsClipping();
335 if (doGeometryUpdate)
338 if (doGeometryUpdate)
336 updateGeometry();
339 updateGeometry();
337 update();
340 update();
338 }
341 }
339
342
340 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
343 void LineChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
341 {
344 {
342 Q_UNUSED(widget)
345 Q_UNUSED(widget)
343 Q_UNUSED(option)
346 Q_UNUSED(option)
344
347
345 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
348 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
346
349
347 painter->save();
350 painter->save();
348 painter->setPen(m_linePen);
351 painter->setPen(m_linePen);
349 bool alwaysUsePath = false;
352 bool alwaysUsePath = false;
350
353
351 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
354 if (m_series->chart()->chartType() == QChart::ChartTypePolar) {
352 qreal halfWidth = domain()->size().width() / 2.0;
355 qreal halfWidth = domain()->size().width() / 2.0;
353 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
356 QRectF clipRectLeft = QRectF(0, 0, halfWidth, domain()->size().height());
354 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
357 QRectF clipRectRight = QRectF(halfWidth, 0, halfWidth, domain()->size().height());
355 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
358 QRegion fullPolarClipRegion(clipRect.toRect(), QRegion::Ellipse);
356 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
359 QRegion clipRegionLeft(fullPolarClipRegion.intersected(clipRectLeft.toRect()));
357 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
360 QRegion clipRegionRight(fullPolarClipRegion.intersected(clipRectRight.toRect()));
358 painter->setClipRegion(clipRegionLeft);
361 painter->setClipRegion(clipRegionLeft);
359 painter->drawPath(m_linePathPolarLeft);
362 painter->drawPath(m_linePathPolarLeft);
360 painter->setClipRegion(clipRegionRight);
363 painter->setClipRegion(clipRegionRight);
361 painter->drawPath(m_linePathPolarRight);
364 painter->drawPath(m_linePathPolarRight);
362 painter->setClipRegion(fullPolarClipRegion);
365 painter->setClipRegion(fullPolarClipRegion);
363 alwaysUsePath = true; // required for proper clipping
366 alwaysUsePath = true; // required for proper clipping
364 } else {
367 } else {
365 painter->setClipRect(clipRect);
368 painter->setClipRect(clipRect);
366 }
369 }
367
370
368 reversePainter(painter, clipRect);
371 reversePainter(painter, clipRect);
369
372
370 if (m_pointsVisible) {
373 if (m_pointsVisible) {
371 painter->setBrush(m_linePen.color());
374 painter->setBrush(m_linePen.color());
372 painter->drawPath(m_linePath);
375 painter->drawPath(m_linePath);
373 } else {
376 } else {
374 painter->setBrush(QBrush(Qt::NoBrush));
377 painter->setBrush(QBrush(Qt::NoBrush));
375 if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) {
378 if (m_linePen.style() != Qt::SolidLine || alwaysUsePath) {
376 // If pen style is not solid line, always fall back to path painting
379 // If pen style is not solid line, always fall back to path painting
377 // to ensure proper continuity of the pattern
380 // to ensure proper continuity of the pattern
378 painter->drawPath(m_linePath);
381 painter->drawPath(m_linePath);
379 } else {
382 } else {
380 for (int i(1); i < m_points.size(); i++)
383 for (int i(1); i < m_linePoints.size(); i++)
381 painter->drawLine(m_points.at(i - 1), m_points.at(i));
384 painter->drawLine(m_linePoints.at(i - 1), m_linePoints.at(i));
382 }
385 }
383 }
386 }
384
387
385 reversePainter(painter, clipRect);
388 reversePainter(painter, clipRect);
386
389
387 if (m_pointLabelsVisible) {
390 if (m_pointLabelsVisible) {
388 if (m_pointLabelsClipping)
391 if (m_pointLabelsClipping)
389 painter->setClipping(true);
392 painter->setClipping(true);
390 else
393 else
391 painter->setClipping(false);
394 painter->setClipping(false);
392 m_series->d_func()->drawSeriesPointLabels(painter, m_points, m_linePen.width() / 2);
395 m_series->d_func()->drawSeriesPointLabels(painter, m_linePoints, m_linePen.width() / 2);
393 }
396 }
394
397
395 painter->restore();
398 painter->restore();
396
399
397 }
400 }
398
401
399 void LineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
402 void LineChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
400 {
403 {
401 emit XYChart::pressed(domain()->calculateDomainPoint(event->pos()));
404 emit XYChart::pressed(domain()->calculateDomainPoint(event->pos()));
402 m_lastMousePos = event->pos();
405 m_lastMousePos = event->pos();
403 m_mousePressed = true;
406 m_mousePressed = true;
404 QGraphicsItem::mousePressEvent(event);
407 QGraphicsItem::mousePressEvent(event);
405 }
408 }
406
409
407 void LineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
410 void LineChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
408 {
411 {
409 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
412 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), true);
410 // event->accept();
413 // event->accept();
411 QGraphicsItem::hoverEnterEvent(event);
414 QGraphicsItem::hoverEnterEvent(event);
412 }
415 }
413
416
414 void LineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
417 void LineChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
415 {
418 {
416 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
419 emit XYChart::hovered(domain()->calculateDomainPoint(event->pos()), false);
417 // event->accept();
420 // event->accept();
418 QGraphicsItem::hoverEnterEvent(event);
421 QGraphicsItem::hoverEnterEvent(event);
419 }
422 }
420
423
421 void LineChartItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
424 void LineChartItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
422 {
425 {
423 emit XYChart::released(domain()->calculateDomainPoint(m_lastMousePos));
426 emit XYChart::released(domain()->calculateDomainPoint(m_lastMousePos));
424 if (m_mousePressed)
427 if (m_mousePressed)
425 emit XYChart::clicked(domain()->calculateDomainPoint(m_lastMousePos));
428 emit XYChart::clicked(domain()->calculateDomainPoint(m_lastMousePos));
426 m_mousePressed = false;
429 m_mousePressed = false;
427 QGraphicsItem::mouseReleaseEvent(event);
430 QGraphicsItem::mouseReleaseEvent(event);
428 }
431 }
429
432
430 void LineChartItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
433 void LineChartItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
431 {
434 {
432 emit XYChart::doubleClicked(domain()->calculateDomainPoint(m_lastMousePos));
435 emit XYChart::doubleClicked(domain()->calculateDomainPoint(m_lastMousePos));
433 QGraphicsItem::mouseDoubleClickEvent(event);
436 QGraphicsItem::mouseDoubleClickEvent(event);
434 }
437 }
435
438
436 #include "moc_linechartitem_p.cpp"
439 #include "moc_linechartitem_p.cpp"
437
440
438 QT_CHARTS_END_NAMESPACE
441 QT_CHARTS_END_NAMESPACE
@@ -1,94 +1,95
1 /****************************************************************************
1 /****************************************************************************
2 **
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd
3 ** Copyright (C) 2015 The Qt Company Ltd
4 ** All rights reserved.
4 ** All rights reserved.
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
5 ** For any questions to The Qt Company, please use contact form at http://qt.io
6 **
6 **
7 ** This file is part of the Qt Charts module.
7 ** This file is part of the Qt Charts module.
8 **
8 **
9 ** Licensees holding valid commercial license for Qt may use this file in
9 ** Licensees holding valid commercial license for Qt may use this file in
10 ** accordance with the Qt License Agreement provided with the Software
10 ** accordance with the Qt License Agreement provided with the Software
11 ** or, alternatively, in accordance with the terms contained in a written
11 ** or, alternatively, in accordance with the terms contained in a written
12 ** agreement between you and The Qt Company.
12 ** agreement between you and The Qt Company.
13 **
13 **
14 ** If you have questions regarding the use of this file, please use
14 ** If you have questions regarding the use of this file, please use
15 ** contact form at http://qt.io
15 ** contact form at http://qt.io
16 **
16 **
17 ****************************************************************************/
17 ****************************************************************************/
18
18
19 // W A R N I N G
19 // W A R N I N G
20 // -------------
20 // -------------
21 //
21 //
22 // This file is not part of the Qt Chart API. It exists purely as an
22 // This file is not part of the Qt Chart API. It exists purely as an
23 // implementation detail. This header file may change from version to
23 // implementation detail. This header file may change from version to
24 // version without notice, or even be removed.
24 // version without notice, or even be removed.
25 //
25 //
26 // We mean it.
26 // We mean it.
27
27
28 #ifndef LINECHARTITEM_H
28 #ifndef LINECHARTITEM_H
29 #define LINECHARTITEM_H
29 #define LINECHARTITEM_H
30
30
31 #include <QtCharts/QChartGlobal>
31 #include <QtCharts/QChartGlobal>
32 #include <private/xychart_p.h>
32 #include <private/xychart_p.h>
33 #include <QtCharts/QChart>
33 #include <QtCharts/QChart>
34 #include <QtGui/QPen>
34 #include <QtGui/QPen>
35
35
36 QT_CHARTS_BEGIN_NAMESPACE
36 QT_CHARTS_BEGIN_NAMESPACE
37
37
38 class QLineSeries;
38 class QLineSeries;
39 class ChartPresenter;
39 class ChartPresenter;
40
40
41 class LineChartItem : public XYChart
41 class LineChartItem : public XYChart
42 {
42 {
43 Q_OBJECT
43 Q_OBJECT
44 Q_INTERFACES(QGraphicsItem)
44 Q_INTERFACES(QGraphicsItem)
45 public:
45 public:
46 explicit LineChartItem(QLineSeries *series, QGraphicsItem *item = 0);
46 explicit LineChartItem(QLineSeries *series, QGraphicsItem *item = 0);
47 ~LineChartItem() {}
47 ~LineChartItem() {}
48
48
49 //from QGraphicsItem
49 //from QGraphicsItem
50 QRectF boundingRect() const;
50 QRectF boundingRect() const;
51 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
51 void paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget);
52 QPainterPath shape() const;
52 QPainterPath shape() const;
53
53
54 QPainterPath path() const { return m_fullPath; }
54 QPainterPath path() const { return m_fullPath; }
55
55
56 public Q_SLOTS:
56 public Q_SLOTS:
57 void handleUpdated();
57 void handleUpdated();
58
58
59 protected:
59 protected:
60 void updateGeometry();
60 void updateGeometry();
61 void mousePressEvent(QGraphicsSceneMouseEvent *event);
61 void mousePressEvent(QGraphicsSceneMouseEvent *event);
62 void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
62 void hoverEnterEvent(QGraphicsSceneHoverEvent *event);
63 void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
63 void hoverLeaveEvent(QGraphicsSceneHoverEvent *event);
64 void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
64 void mouseReleaseEvent(QGraphicsSceneMouseEvent *event);
65 void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
65 void mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event);
66 void suppressPoints() { m_pointsVisible = false; }
66 void suppressPoints() { m_pointsVisible = false; }
67 void forceChartType(QChart::ChartType chartType) { m_chartType = chartType; }
67 void forceChartType(QChart::ChartType chartType) { m_chartType = chartType; }
68
68
69 private:
69 private:
70 QLineSeries *m_series;
70 QLineSeries *m_series;
71 QPainterPath m_linePath;
71 QPainterPath m_linePath;
72 QPainterPath m_linePathPolarRight;
72 QPainterPath m_linePathPolarRight;
73 QPainterPath m_linePathPolarLeft;
73 QPainterPath m_linePathPolarLeft;
74 QPainterPath m_fullPath;
74 QPainterPath m_fullPath;
75 QPainterPath m_shapePath;
75 QPainterPath m_shapePath;
76
76
77 QVector<QPointF> m_linePoints;
77 QRectF m_rect;
78 QRectF m_rect;
78 QPen m_linePen;
79 QPen m_linePen;
79 bool m_pointsVisible;
80 bool m_pointsVisible;
80 QChart::ChartType m_chartType;
81 QChart::ChartType m_chartType;
81
82
82 bool m_pointLabelsVisible;
83 bool m_pointLabelsVisible;
83 QString m_pointLabelsFormat;
84 QString m_pointLabelsFormat;
84 QFont m_pointLabelsFont;
85 QFont m_pointLabelsFont;
85 QColor m_pointLabelsColor;
86 QColor m_pointLabelsColor;
86 bool m_pointLabelsClipping;
87 bool m_pointLabelsClipping;
87
88
88 QPointF m_lastMousePos;
89 QPointF m_lastMousePos;
89 bool m_mousePressed;
90 bool m_mousePressed;
90 };
91 };
91
92
92 QT_CHARTS_END_NAMESPACE
93 QT_CHARTS_END_NAMESPACE
93
94
94 #endif
95 #endif
General Comments 0
You need to be logged in to leave comments. Login now