##// END OF EJS Templates
Fix area series point labels...
Titta Heikkala -
r2778:6eb2f806ec7f
parent child
Show More
@@ -1,283 +1,284
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/areachartitem_p.h>
19 #include <private/areachartitem_p.h>
20 #include <QtCharts/QAreaSeries>
20 #include <QtCharts/QAreaSeries>
21 #include <private/qareaseries_p.h>
21 #include <private/qareaseries_p.h>
22 #include <QtCharts/QLineSeries>
22 #include <QtCharts/QLineSeries>
23 #include <private/chartpresenter_p.h>
23 #include <private/chartpresenter_p.h>
24 #include <private/abstractdomain_p.h>
24 #include <private/abstractdomain_p.h>
25 #include <QtGui/QPainter>
25 #include <QtGui/QPainter>
26 #include <QtWidgets/QGraphicsSceneMouseEvent>
26 #include <QtWidgets/QGraphicsSceneMouseEvent>
27 #include <QtCore/QDebug>
27 #include <QtCore/QDebug>
28
28
29
29
30 QT_CHARTS_BEGIN_NAMESPACE
30 QT_CHARTS_BEGIN_NAMESPACE
31
31
32 AreaChartItem::AreaChartItem(QAreaSeries *areaSeries, QGraphicsItem* item)
32 AreaChartItem::AreaChartItem(QAreaSeries *areaSeries, QGraphicsItem* item)
33 : ChartItem(areaSeries->d_func(),item),
33 : ChartItem(areaSeries->d_func(),item),
34 m_series(areaSeries),
34 m_series(areaSeries),
35 m_upper(0),
35 m_upper(0),
36 m_lower(0),
36 m_lower(0),
37 m_pointsVisible(false),
37 m_pointsVisible(false),
38 m_pointLabelsVisible(false),
38 m_pointLabelsVisible(false),
39 m_pointLabelsFormat(areaSeries->pointLabelsFormat()),
39 m_pointLabelsFormat(areaSeries->pointLabelsFormat()),
40 m_pointLabelsFont(areaSeries->pointLabelsFont()),
40 m_pointLabelsFont(areaSeries->pointLabelsFont()),
41 m_pointLabelsColor(areaSeries->pointLabelsColor()),
41 m_pointLabelsColor(areaSeries->pointLabelsColor()),
42 m_mousePressed(false)
42 m_mousePressed(false)
43 {
43 {
44 setAcceptHoverEvents(true);
44 setAcceptHoverEvents(true);
45 setFlag(QGraphicsItem::ItemIsSelectable, true);
45 setFlag(QGraphicsItem::ItemIsSelectable, true);
46 setZValue(ChartPresenter::LineChartZValue);
46 setZValue(ChartPresenter::LineChartZValue);
47 if (m_series->upperSeries())
47 if (m_series->upperSeries())
48 m_upper = new AreaBoundItem(this, m_series->upperSeries());
48 m_upper = new AreaBoundItem(this, m_series->upperSeries());
49 if (m_series->lowerSeries())
49 if (m_series->lowerSeries())
50 m_lower = new AreaBoundItem(this, m_series->lowerSeries());
50 m_lower = new AreaBoundItem(this, m_series->lowerSeries());
51
51
52 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
52 QObject::connect(m_series->d_func(), SIGNAL(updated()), this, SLOT(handleUpdated()));
53 QObject::connect(m_series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
53 QObject::connect(m_series, SIGNAL(visibleChanged()), this, SLOT(handleUpdated()));
54 QObject::connect(m_series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
54 QObject::connect(m_series, SIGNAL(opacityChanged()), this, SLOT(handleUpdated()));
55 QObject::connect(this, SIGNAL(clicked(QPointF)), areaSeries, SIGNAL(clicked(QPointF)));
55 QObject::connect(this, SIGNAL(clicked(QPointF)), areaSeries, SIGNAL(clicked(QPointF)));
56 QObject::connect(this, SIGNAL(hovered(QPointF,bool)), areaSeries, SIGNAL(hovered(QPointF,bool)));
56 QObject::connect(this, SIGNAL(hovered(QPointF,bool)), areaSeries, SIGNAL(hovered(QPointF,bool)));
57 QObject::connect(this, SIGNAL(pressed(QPointF)), areaSeries, SIGNAL(pressed(QPointF)));
57 QObject::connect(this, SIGNAL(pressed(QPointF)), areaSeries, SIGNAL(pressed(QPointF)));
58 QObject::connect(this, SIGNAL(released(QPointF)), areaSeries, SIGNAL(released(QPointF)));
58 QObject::connect(this, SIGNAL(released(QPointF)), areaSeries, SIGNAL(released(QPointF)));
59 QObject::connect(this, SIGNAL(doubleClicked(QPointF)),
59 QObject::connect(this, SIGNAL(doubleClicked(QPointF)),
60 areaSeries, SIGNAL(doubleClicked(QPointF)));
60 areaSeries, SIGNAL(doubleClicked(QPointF)));
61 QObject::connect(areaSeries, SIGNAL(pointLabelsFormatChanged(QString)),
61 QObject::connect(areaSeries, SIGNAL(pointLabelsFormatChanged(QString)),
62 this, SLOT(handleUpdated()));
62 this, SLOT(handleUpdated()));
63 QObject::connect(areaSeries, SIGNAL(pointLabelsVisibilityChanged(bool)),
63 QObject::connect(areaSeries, SIGNAL(pointLabelsVisibilityChanged(bool)),
64 this, SLOT(handleUpdated()));
64 this, SLOT(handleUpdated()));
65 QObject::connect(areaSeries, SIGNAL(pointLabelsFontChanged(QFont)),
65 QObject::connect(areaSeries, SIGNAL(pointLabelsFontChanged(QFont)),
66 this, SLOT(handleUpdated()));
66 this, SLOT(handleUpdated()));
67 QObject::connect(areaSeries, SIGNAL(pointLabelsColorChanged(QColor)),
67 QObject::connect(areaSeries, SIGNAL(pointLabelsColorChanged(QColor)),
68 this, SLOT(handleUpdated()));
68 this, SLOT(handleUpdated()));
69
69
70 handleUpdated();
70 handleUpdated();
71 }
71 }
72
72
73 AreaChartItem::~AreaChartItem()
73 AreaChartItem::~AreaChartItem()
74 {
74 {
75 delete m_upper;
75 delete m_upper;
76 delete m_lower;
76 delete m_lower;
77 }
77 }
78
78
79 void AreaChartItem::setPresenter(ChartPresenter *presenter)
79 void AreaChartItem::setPresenter(ChartPresenter *presenter)
80 {
80 {
81 if (m_upper)
81 if (m_upper)
82 m_upper->setPresenter(presenter);
82 m_upper->setPresenter(presenter);
83 if (m_lower) {
83 if (m_lower) {
84 m_lower->setPresenter(presenter);
84 m_lower->setPresenter(presenter);
85 }
85 }
86 ChartItem::setPresenter(presenter);
86 ChartItem::setPresenter(presenter);
87 }
87 }
88
88
89 QRectF AreaChartItem::boundingRect() const
89 QRectF AreaChartItem::boundingRect() const
90 {
90 {
91 return m_rect;
91 return m_rect;
92 }
92 }
93
93
94 QPainterPath AreaChartItem::shape() const
94 QPainterPath AreaChartItem::shape() const
95 {
95 {
96 return m_path;
96 return m_path;
97 }
97 }
98
98
99 void AreaChartItem::updatePath()
99 void AreaChartItem::updatePath()
100 {
100 {
101 QPainterPath path;
101 QPainterPath path;
102 QRectF rect(QPointF(0,0),domain()->size());
102 QRectF rect(QPointF(0,0),domain()->size());
103
103
104 path = m_upper->path();
104 path = m_upper->path();
105
105
106 if (m_lower) {
106 if (m_lower) {
107 // Note: Polarcharts always draw area correctly only when both series have equal width or are
107 // Note: Polarcharts always draw area correctly only when both series have equal width or are
108 // fully displayed. If one series is partally off-chart, the connecting line between
108 // fully displayed. If one series is partally off-chart, the connecting line between
109 // the series does not attach to the end of the partially hidden series but to the point
109 // the series does not attach to the end of the partially hidden series but to the point
110 // where it intersects the axis line. The problem is especially noticeable when one of the series
110 // where it intersects the axis line. The problem is especially noticeable when one of the series
111 // is entirely off-chart, in which case the connecting line connects two ends of the
111 // is entirely off-chart, in which case the connecting line connects two ends of the
112 // visible series.
112 // visible series.
113 // This happens because we get the paths from linechart, which omits off-chart segments.
113 // This happens because we get the paths from linechart, which omits off-chart segments.
114 // To properly fix, linechart would need to provide true full path, in right, left, and the rest
114 // To properly fix, linechart would need to provide true full path, in right, left, and the rest
115 // portions to enable proper clipping. However, combining those to single visually unified area
115 // portions to enable proper clipping. However, combining those to single visually unified area
116 // would be a nightmare, since they would have to be painted separately.
116 // would be a nightmare, since they would have to be painted separately.
117 path.connectPath(m_lower->path().toReversed());
117 path.connectPath(m_lower->path().toReversed());
118 } else {
118 } else {
119 QPointF first = path.pointAtPercent(0);
119 QPointF first = path.pointAtPercent(0);
120 QPointF last = path.pointAtPercent(1);
120 QPointF last = path.pointAtPercent(1);
121 if (presenter()->chartType() == QChart::ChartTypeCartesian) {
121 if (presenter()->chartType() == QChart::ChartTypeCartesian) {
122 path.lineTo(last.x(), rect.bottom());
122 path.lineTo(last.x(), rect.bottom());
123 path.lineTo(first.x(), rect.bottom());
123 path.lineTo(first.x(), rect.bottom());
124 } else { // polar
124 } else { // polar
125 path.lineTo(rect.center());
125 path.lineTo(rect.center());
126 }
126 }
127 }
127 }
128 path.closeSubpath();
128 path.closeSubpath();
129
129
130 // Only zoom in if the bounding rect of the path fits inside int limits. QWidget::update() uses
130 // Only zoom in if the bounding rect of the path fits inside int limits. QWidget::update() uses
131 // a region that has to be compatible with QRect.
131 // a region that has to be compatible with QRect.
132 if (path.boundingRect().height() <= INT_MAX
132 if (path.boundingRect().height() <= INT_MAX
133 && path.boundingRect().width() <= INT_MAX) {
133 && path.boundingRect().width() <= INT_MAX) {
134 prepareGeometryChange();
134 prepareGeometryChange();
135 m_path = path;
135 m_path = path;
136 m_rect = path.boundingRect();
136 m_rect = path.boundingRect();
137 update();
137 update();
138 }
138 }
139 }
139 }
140
140
141 void AreaChartItem::handleUpdated()
141 void AreaChartItem::handleUpdated()
142 {
142 {
143 setVisible(m_series->isVisible());
143 setVisible(m_series->isVisible());
144 m_pointsVisible = m_series->pointsVisible();
144 m_pointsVisible = m_series->pointsVisible();
145 m_linePen = m_series->pen();
145 m_linePen = m_series->pen();
146 m_brush = m_series->brush();
146 m_brush = m_series->brush();
147 m_pointPen = m_series->pen();
147 m_pointPen = m_series->pen();
148 m_pointPen.setWidthF(2 * m_pointPen.width());
148 m_pointPen.setWidthF(2 * m_pointPen.width());
149 setOpacity(m_series->opacity());
149 setOpacity(m_series->opacity());
150 m_pointLabelsFormat = m_series->pointLabelsFormat();
150 m_pointLabelsFormat = m_series->pointLabelsFormat();
151 m_pointLabelsVisible = m_series->pointLabelsVisible();
151 m_pointLabelsVisible = m_series->pointLabelsVisible();
152 m_pointLabelsFont = m_series->pointLabelsFont();
152 m_pointLabelsFont = m_series->pointLabelsFont();
153 m_pointLabelsColor = m_series->pointLabelsColor();
153 m_pointLabelsColor = m_series->pointLabelsColor();
154 update();
154 update();
155 }
155 }
156
156
157 void AreaChartItem::handleDomainUpdated()
157 void AreaChartItem::handleDomainUpdated()
158 {
158 {
159 if (m_upper) {
159 if (m_upper) {
160 AbstractDomain* d = m_upper->domain();
160 AbstractDomain* d = m_upper->domain();
161 d->setSize(domain()->size());
161 d->setSize(domain()->size());
162 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
162 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
163 m_upper->handleDomainUpdated();
163 m_upper->handleDomainUpdated();
164 }
164 }
165
165
166 if (m_lower) {
166 if (m_lower) {
167 AbstractDomain* d = m_lower->domain();
167 AbstractDomain* d = m_lower->domain();
168 d->setSize(domain()->size());
168 d->setSize(domain()->size());
169 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
169 d->setRange(domain()->minX(),domain()->maxX(),domain()->minY(),domain()->maxY());
170 m_lower->handleDomainUpdated();
170 m_lower->handleDomainUpdated();
171 }
171 }
172 }
172 }
173
173
174 void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
174 void AreaChartItem::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
175 {
175 {
176 Q_UNUSED(widget)
176 Q_UNUSED(widget)
177 Q_UNUSED(option)
177 Q_UNUSED(option)
178 painter->save();
178 painter->save();
179 painter->setPen(m_linePen);
179 painter->setPen(m_linePen);
180 painter->setBrush(m_brush);
180 painter->setBrush(m_brush);
181 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
181 QRectF clipRect = QRectF(QPointF(0, 0), domain()->size());
182 if (presenter()->chartType() == QChart::ChartTypePolar)
182 if (presenter()->chartType() == QChart::ChartTypePolar)
183 painter->setClipRegion(QRegion(clipRect.toRect(), QRegion::Ellipse));
183 painter->setClipRegion(QRegion(clipRect.toRect(), QRegion::Ellipse));
184 else
184 else
185 painter->setClipRect(clipRect);
185 painter->setClipRect(clipRect);
186 painter->drawPath(m_path);
186 painter->drawPath(m_path);
187 if (m_pointsVisible) {
187 if (m_pointsVisible) {
188 painter->setPen(m_pointPen);
188 painter->setPen(m_pointPen);
189 painter->drawPoints(m_upper->geometryPoints());
189 painter->drawPoints(m_upper->geometryPoints());
190 if (m_lower)
190 if (m_lower)
191 painter->drawPoints(m_lower->geometryPoints());
191 painter->drawPoints(m_lower->geometryPoints());
192 }
192 }
193
193
194 // Draw series point label
194 // Draw series point label
195 if (m_pointLabelsVisible) {
195 if (m_pointLabelsVisible) {
196 static const QString xPointTag(QLatin1String("@xPoint"));
196 static const QString xPointTag(QLatin1String("@xPoint"));
197 static const QString yPointTag(QLatin1String("@yPoint"));
197 static const QString yPointTag(QLatin1String("@yPoint"));
198 const int labelOffset = 2;
198 const int labelOffset = 2;
199
199
200 painter->setFont(m_pointLabelsFont);
200 painter->setFont(m_pointLabelsFont);
201 painter->setPen(QPen(m_pointLabelsColor));
201 painter->setPen(QPen(m_pointLabelsColor));
202 QFontMetrics fm(painter->font());
202 QFontMetrics fm(painter->font());
203
203
204 QString pointLabel = m_pointLabelsFormat;
204 QString pointLabel;
205
205
206 if (m_series->upperSeries()) {
206 if (m_series->upperSeries()) {
207 for (int i(0); i < m_series->upperSeries()->count(); i++) {
207 for (int i(0); i < m_series->upperSeries()->count(); i++) {
208 pointLabel = m_pointLabelsFormat;
208 pointLabel.replace(xPointTag,
209 pointLabel.replace(xPointTag,
209 presenter()->numberToString(m_series->upperSeries()->at(i).x()));
210 presenter()->numberToString(m_series->upperSeries()->at(i).x()));
210 pointLabel.replace(yPointTag,
211 pointLabel.replace(yPointTag,
211 presenter()->numberToString(m_series->upperSeries()->at(i).y()));
212 presenter()->numberToString(m_series->upperSeries()->at(i).y()));
212
213
213 // Position text in relation to the point
214 // Position text in relation to the point
214 int pointLabelWidth = fm.width(pointLabel);
215 int pointLabelWidth = fm.width(pointLabel);
215 QPointF position(m_upper->geometryPoints().at(i));
216 QPointF position(m_upper->geometryPoints().at(i));
216 position.setX(position.x() - pointLabelWidth / 2);
217 position.setX(position.x() - pointLabelWidth / 2);
217 position.setY(position.y() - m_series->upperSeries()->pen().width() / 2 - labelOffset);
218 position.setY(position.y() - m_series->upperSeries()->pen().width() / 2 - labelOffset);
218
219
219 painter->drawText(position, pointLabel);
220 painter->drawText(position, pointLabel);
220 }
221 }
221 }
222 }
222
223
223 if (m_series->lowerSeries()) {
224 if (m_series->lowerSeries()) {
224 for (int i(0); i < m_series->lowerSeries()->count(); i++) {
225 for (int i(0); i < m_series->lowerSeries()->count(); i++) {
226 pointLabel = m_pointLabelsFormat;
225 pointLabel.replace(xPointTag,
227 pointLabel.replace(xPointTag,
226 presenter()->numberToString(m_series->lowerSeries()->at(i).x()));
228 presenter()->numberToString(m_series->lowerSeries()->at(i).x()));
227 pointLabel.replace(yPointTag,
229 pointLabel.replace(yPointTag,
228 presenter()->numberToString(m_series->lowerSeries()->at(i).y()));
230 presenter()->numberToString(m_series->lowerSeries()->at(i).y()));
229
231
230 // Position text in relation to the point
232 // Position text in relation to the point
231 int pointLabelWidth = fm.width(pointLabel);
233 int pointLabelWidth = fm.width(pointLabel);
232 QPointF position(m_lower->geometryPoints().at(i));
234 QPointF position(m_lower->geometryPoints().at(i));
233 position.setX(position.x() - pointLabelWidth / 2);
235 position.setX(position.x() - pointLabelWidth / 2);
234 position.setY(position.y() - m_series->lowerSeries()->pen().width() / 2 - labelOffset);
236 position.setY(position.y() - m_series->lowerSeries()->pen().width() / 2 - labelOffset);
235
236 painter->drawText(position, pointLabel);
237 painter->drawText(position, pointLabel);
237 }
238 }
238 }
239 }
239 }
240 }
240
241
241 painter->restore();
242 painter->restore();
242 }
243 }
243
244
244 void AreaChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
245 void AreaChartItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
245 {
246 {
246 emit pressed(m_upper->domain()->calculateDomainPoint(event->pos()));
247 emit pressed(m_upper->domain()->calculateDomainPoint(event->pos()));
247 m_lastMousePos = event->pos();
248 m_lastMousePos = event->pos();
248 m_mousePressed = true;
249 m_mousePressed = true;
249 ChartItem::mousePressEvent(event);
250 ChartItem::mousePressEvent(event);
250 }
251 }
251
252
252 void AreaChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
253 void AreaChartItem::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
253 {
254 {
254 emit hovered(domain()->calculateDomainPoint(event->pos()), true);
255 emit hovered(domain()->calculateDomainPoint(event->pos()), true);
255 event->accept();
256 event->accept();
256 // QGraphicsItem::hoverEnterEvent(event);
257 // QGraphicsItem::hoverEnterEvent(event);
257 }
258 }
258
259
259 void AreaChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
260 void AreaChartItem::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
260 {
261 {
261 emit hovered(domain()->calculateDomainPoint(event->pos()), false);
262 emit hovered(domain()->calculateDomainPoint(event->pos()), false);
262 event->accept();
263 event->accept();
263 // QGraphicsItem::hoverEnterEvent(event);
264 // QGraphicsItem::hoverEnterEvent(event);
264 }
265 }
265
266
266 void AreaChartItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
267 void AreaChartItem::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
267 {
268 {
268 emit released(m_upper->domain()->calculateDomainPoint(m_lastMousePos));
269 emit released(m_upper->domain()->calculateDomainPoint(m_lastMousePos));
269 if (m_mousePressed)
270 if (m_mousePressed)
270 emit clicked(m_upper->domain()->calculateDomainPoint(m_lastMousePos));
271 emit clicked(m_upper->domain()->calculateDomainPoint(m_lastMousePos));
271 m_mousePressed = false;
272 m_mousePressed = false;
272 ChartItem::mouseReleaseEvent(event);
273 ChartItem::mouseReleaseEvent(event);
273 }
274 }
274
275
275 void AreaChartItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
276 void AreaChartItem::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
276 {
277 {
277 emit doubleClicked(m_upper->domain()->calculateDomainPoint(m_lastMousePos));
278 emit doubleClicked(m_upper->domain()->calculateDomainPoint(m_lastMousePos));
278 ChartItem::mouseDoubleClickEvent(event);
279 ChartItem::mouseDoubleClickEvent(event);
279 }
280 }
280
281
281 #include "moc_areachartitem_p.cpp"
282 #include "moc_areachartitem_p.cpp"
282
283
283 QT_CHARTS_END_NAMESPACE
284 QT_CHARTS_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now