##// END OF EJS Templates
pieslice: fix slice labels turning the wrong way...
Jani Honkonen -
r1327:39a357025ee6
parent child
Show More
@@ -1,224 +1,230
1 /****************************************************************************
1 /****************************************************************************
2 **
2 **
3 ** Copyright (C) 2012 Digia Plc
3 ** Copyright (C) 2012 Digia Plc
4 ** All rights reserved.
4 ** All rights reserved.
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
5 ** For any questions to Digia, please use contact form at http://qt.digia.com
6 **
6 **
7 ** This file is part of the Qt Commercial Charts Add-on.
7 ** This file is part of the Qt Commercial Charts Add-on.
8 **
8 **
9 ** $QT_BEGIN_LICENSE$
9 ** $QT_BEGIN_LICENSE$
10 ** Licensees holding valid Qt Commercial licenses may use this file in
10 ** Licensees holding valid Qt Commercial licenses may use this file in
11 ** accordance with the Qt Commercial License Agreement provided with the
11 ** accordance with the Qt Commercial License Agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia.
13 ** a written agreement between you and Digia.
14 **
14 **
15 ** If you have questions regarding the use of this file, please use
15 ** If you have questions regarding the use of this file, please use
16 ** contact form at http://qt.digia.com
16 ** contact form at http://qt.digia.com
17 ** $QT_END_LICENSE$
17 ** $QT_END_LICENSE$
18 **
18 **
19 ****************************************************************************/
19 ****************************************************************************/
20
20
21 #include "piesliceitem_p.h"
21 #include "piesliceitem_p.h"
22 #include "piechartitem_p.h"
22 #include "piechartitem_p.h"
23 #include "qpieseries.h"
23 #include "qpieseries.h"
24 #include "qpieslice.h"
24 #include "qpieslice.h"
25 #include "chartpresenter_p.h"
25 #include "chartpresenter_p.h"
26 #include <QPainter>
26 #include <QPainter>
27 #include <qmath.h>
27 #include <qmath.h>
28 #include <QGraphicsSceneEvent>
28 #include <QGraphicsSceneEvent>
29 #include <QTime>
29 #include <QTime>
30 #include <QDebug>
30 #include <QDebug>
31
31
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
32 QTCOMMERCIALCHART_BEGIN_NAMESPACE
33
33
34 #define PI 3.14159265 // TODO: is this defined in some header?
35
36 QPointF offset(qreal angle, qreal length)
34 QPointF offset(qreal angle, qreal length)
37 {
35 {
38 qreal dx = qSin(angle*(PI/180)) * length;
36 qreal dx = qSin(angle*(M_PI/180)) * length;
39 qreal dy = qCos(angle*(PI/180)) * length;
37 qreal dy = qCos(angle*(M_PI/180)) * length;
40 return QPointF(dx, -dy);
38 return QPointF(dx, -dy);
41 }
39 }
42
40
43 PieSliceItem::PieSliceItem(QGraphicsItem* parent)
41 PieSliceItem::PieSliceItem(QGraphicsItem* parent)
44 :QGraphicsObject(parent),
42 :QGraphicsObject(parent),
45 m_hovered(false)
43 m_hovered(false)
46 {
44 {
47 setAcceptHoverEvents(true);
45 setAcceptHoverEvents(true);
48 setAcceptedMouseButtons(Qt::MouseButtonMask);
46 setAcceptedMouseButtons(Qt::MouseButtonMask);
49 setZValue(ChartPresenter::PieSeriesZValue);
47 setZValue(ChartPresenter::PieSeriesZValue);
50 }
48 }
51
49
52 PieSliceItem::~PieSliceItem()
50 PieSliceItem::~PieSliceItem()
53 {
51 {
54 // If user is hovering over the slice and it gets destroyed we do
52 // If user is hovering over the slice and it gets destroyed we do
55 // not get a hover leave event. So we must emit the signal here.
53 // not get a hover leave event. So we must emit the signal here.
56 if (m_hovered)
54 if (m_hovered)
57 emit hovered(false);
55 emit hovered(false);
58 }
56 }
59
57
60 QRectF PieSliceItem::boundingRect() const
58 QRectF PieSliceItem::boundingRect() const
61 {
59 {
62 return m_boundingRect;
60 return m_boundingRect;
63 }
61 }
64
62
65 QPainterPath PieSliceItem::shape() const
63 QPainterPath PieSliceItem::shape() const
66 {
64 {
67 // Don't include the label and label arm.
65 // Don't include the label and label arm.
68 // This is used to detect a mouse clicks. We do not want clicks from label.
66 // This is used to detect a mouse clicks. We do not want clicks from label.
69 return m_slicePath;
67 return m_slicePath;
70 }
68 }
71
69
72 void PieSliceItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
70 void PieSliceItem::paint(QPainter* painter, const QStyleOptionGraphicsItem* /*option*/, QWidget* /*widget*/)
73 {
71 {
74 painter->save();
72 painter->save();
75 painter->setClipRect(parentItem()->boundingRect());
73 painter->setClipRect(parentItem()->boundingRect());
76 painter->setPen(m_data.m_slicePen);
74 painter->setPen(m_data.m_slicePen);
77 painter->setBrush(m_data.m_sliceBrush);
75 painter->setBrush(m_data.m_sliceBrush);
78 painter->drawPath(m_slicePath);
76 painter->drawPath(m_slicePath);
79 painter->restore();
77 painter->restore();
80
78
81 if (m_data.m_isLabelVisible) {
79 if (m_data.m_isLabelVisible) {
82 painter->save();
80 painter->save();
83 painter->setClipRect(parentItem()->boundingRect());
81 painter->setClipRect(parentItem()->boundingRect());
84 // Pen for label arm not defined in the QPieSeries api, let's use brush's color instead
82 // Pen for label arm not defined in the QPieSeries api, let's use brush's color instead
85 // Also, the drawText actually uses the pen color for the text color (unlike QGraphicsSimpleTextItem)
83 // Also, the drawText actually uses the pen color for the text color (unlike QGraphicsSimpleTextItem)
86 painter->setPen(m_data.m_labelBrush.color());
84 painter->setPen(m_data.m_labelBrush.color());
87 painter->drawPath(m_labelArmPath);
85 painter->drawPath(m_labelArmPath);
88 painter->setFont(m_data.m_labelFont);
86 painter->setFont(m_data.m_labelFont);
89 painter->drawText(m_labelTextRect.bottomLeft(), m_data.m_labelText);
87 painter->drawText(m_labelTextRect.bottomLeft(), m_data.m_labelText);
90 painter->restore();
88 painter->restore();
91 }
89 }
92 }
90 }
93
91
94 void PieSliceItem::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
92 void PieSliceItem::hoverEnterEvent(QGraphicsSceneHoverEvent* /*event*/)
95 {
93 {
96 m_hovered = true;
94 m_hovered = true;
97 emit hovered(true);
95 emit hovered(true);
98 }
96 }
99
97
100 void PieSliceItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
98 void PieSliceItem::hoverLeaveEvent(QGraphicsSceneHoverEvent* /*event*/)
101 {
99 {
102 m_hovered = false;
100 m_hovered = false;
103 emit hovered(false);
101 emit hovered(false);
104 }
102 }
105
103
106 void PieSliceItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
104 void PieSliceItem::mousePressEvent(QGraphicsSceneMouseEvent *event)
107 {
105 {
108 emit clicked(event->buttons());
106 emit clicked(event->buttons());
109 }
107 }
110
108
111 void PieSliceItem::setLayout(const PieSliceData &sliceData)
109 void PieSliceItem::setLayout(const PieSliceData &sliceData)
112 {
110 {
113 m_data = sliceData;
111 m_data = sliceData;
114 updateGeometry();
112 updateGeometry();
115 update();
113 update();
116 }
114 }
117
115
118 void PieSliceItem::updateGeometry()
116 void PieSliceItem::updateGeometry()
119 {
117 {
120 if (m_data.m_radius <= 0)
118 if (m_data.m_radius <= 0)
121 return;
119 return;
122
120
123 prepareGeometryChange();
121 prepareGeometryChange();
124
122
125 // update slice path
123 // update slice path
126 qreal centerAngle;
124 qreal centerAngle;
127 QPointF armStart;
125 QPointF armStart;
128 m_slicePath = slicePath(m_data.m_center, m_data.m_radius, m_data.m_startAngle, m_data.m_angleSpan, &centerAngle, &armStart);
126 m_slicePath = slicePath(m_data.m_center, m_data.m_radius, m_data.m_startAngle, m_data.m_angleSpan, &centerAngle, &armStart);
129
127
130 // update text rect
128 // update text rect
131 m_labelTextRect = labelTextRect(m_data.m_labelFont, m_data.m_labelText);
129 m_labelTextRect = labelTextRect(m_data.m_labelFont, m_data.m_labelText);
132
130
133 // update label arm path
131 // update label arm path
134 QPointF labelTextStart;
132 QPointF labelTextStart;
135 m_labelArmPath = labelArmPath(armStart, centerAngle, m_data.m_radius * m_data.m_labelArmLengthFactor, m_labelTextRect.width(), &labelTextStart);
133 m_labelArmPath = labelArmPath(armStart, centerAngle, m_data.m_radius * m_data.m_labelArmLengthFactor, m_labelTextRect.width(), &labelTextStart);
136
134
137 // update text position
135 // update text position
138 m_labelTextRect.moveBottomLeft(labelTextStart);
136 m_labelTextRect.moveBottomLeft(labelTextStart);
139
137
140 // update bounding rect
138 // update bounding rect
141 if (m_data.m_isLabelVisible)
139 if (m_data.m_isLabelVisible)
142 m_boundingRect = m_slicePath.boundingRect().united(m_labelArmPath.boundingRect()).united(m_labelTextRect);
140 m_boundingRect = m_slicePath.boundingRect().united(m_labelArmPath.boundingRect()).united(m_labelTextRect);
143 else
141 else
144 m_boundingRect = m_slicePath.boundingRect();
142 m_boundingRect = m_slicePath.boundingRect();
145 }
143 }
146
144
147 QPointF PieSliceItem::sliceCenter(QPointF point, qreal radius, QPieSlice *slice)
145 QPointF PieSliceItem::sliceCenter(QPointF point, qreal radius, QPieSlice *slice)
148 {
146 {
149 if (slice->isExploded()) {
147 if (slice->isExploded()) {
150 qreal centerAngle = slice->startAngle() + (slice->angleSpan()/2);
148 qreal centerAngle = slice->startAngle() + (slice->angleSpan()/2);
151 qreal len = radius * slice->explodeDistanceFactor();
149 qreal len = radius * slice->explodeDistanceFactor();
152 qreal dx = qSin(centerAngle*(PI/180)) * len;
150 point += offset(centerAngle, len);
153 qreal dy = -qCos(centerAngle*(PI/180)) * len;
154 point += QPointF(dx, dy);
155 }
151 }
156 return point;
152 return point;
157 }
153 }
158
154
159 QPainterPath PieSliceItem::slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, qreal *centerAngle, QPointF* armStart)
155 QPainterPath PieSliceItem::slicePath(QPointF center, qreal radius, qreal startAngle, qreal angleSpan, qreal *centerAngle, QPointF* armStart)
160 {
156 {
161 // calculate center angle
157 // calculate center angle
162 *centerAngle = startAngle + (angleSpan/2);
158 *centerAngle = startAngle + (angleSpan/2);
163
159
164 // calculate slice rectangle
160 // calculate slice rectangle
165 QRectF rect(center.x()-radius, center.y()-radius, radius*2, radius*2);
161 QRectF rect(center.x()-radius, center.y()-radius, radius*2, radius*2);
166
162
167 // slice path
163 // slice path
168 // TODO: draw the shape so that it might have a hole in the center
164 // TODO: draw the shape so that it might have a hole in the center
169 QPainterPath path;
165 QPainterPath path;
170 path.moveTo(rect.center());
166 path.moveTo(rect.center());
171 path.arcTo(rect, -startAngle + 90, -angleSpan);
167 path.arcTo(rect, -startAngle + 90, -angleSpan);
172 path.closeSubpath();
168 path.closeSubpath();
173
169
174 // calculate label arm start point
170 // calculate label arm start point
175 *armStart = center;
171 *armStart = center;
176 *armStart += offset(*centerAngle, radius + PIESLICE_LABEL_GAP);
172 *armStart += offset(*centerAngle, radius + PIESLICE_LABEL_GAP);
177
173
178 return path;
174 return path;
179 }
175 }
180
176
181 QPainterPath PieSliceItem::labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF *textStart)
177 QPainterPath PieSliceItem::labelArmPath(QPointF start, qreal angle, qreal length, qreal textWidth, QPointF *textStart)
182 {
178 {
179 // Normalize the angle to 0-360 range
180 // NOTE: We are using int here on purpose. Depenging on platform and hardware
181 // qreal can be a double, float or something the user gives to the Qt configure
182 // (QT_COORD_TYPE). Compilers do not seem to support modulo for double or float
183 // but there are fmod() and fmodf() functions for that. So instead of some #ifdef
184 // that might break we just use int. Precision for this is just fine for our needs.
185 int normalized = angle * 10.0;
186 normalized = normalized % 3600;
187 if (normalized < 0)
188 normalized += 3600;
189 angle = (qreal) normalized / 10.0;
190
183 // prevent label arm pointing straight down because it will look bad
191 // prevent label arm pointing straight down because it will look bad
184 if (angle < 180 && angle > 170)
192 if (angle < 180 && angle > 170)
185 angle = 170;
193 angle = 170;
186 if (angle > 180 && angle < 190)
194 if (angle > 180 && angle < 190)
187 angle = 190;
195 angle = 190;
188
196
189 // line from slice to label
197 // line from slice to label
190 qreal dx = qSin(angle*(PI/180)) * length;
198 QPointF parm1 = start + offset(angle, length);
191 qreal dy = -qCos(angle*(PI/180)) * length;
192 QPointF parm1 = start + QPointF(dx, dy);
193
199
194 // line to underline the label
200 // line to underline the label
195 QPointF parm2 = parm1;
201 QPointF parm2 = parm1;
196 if (angle < 180) { // arm swings the other way on the left side
202 if (angle < 180) { // arm swings the other way on the left side
197 parm2 += QPointF(textWidth, 0);
203 parm2 += QPointF(textWidth, 0);
198 *textStart = parm1;
204 *textStart = parm1;
199 }
205 }
200 else {
206 else {
201 parm2 += QPointF(-textWidth,0);
207 parm2 += QPointF(-textWidth,0);
202 *textStart = parm2;
208 *textStart = parm2;
203 }
209 }
204
210
205 // elevate the text position a bit so that it does not hit the line
211 // elevate the text position a bit so that it does not hit the line
206 *textStart += QPointF(0, -3);
212 *textStart += QPointF(0, -3);
207
213
208 QPainterPath path;
214 QPainterPath path;
209 path.moveTo(start);
215 path.moveTo(start);
210 path.lineTo(parm1);
216 path.lineTo(parm1);
211 path.lineTo(parm2);
217 path.lineTo(parm2);
212
218
213 return path;
219 return path;
214 }
220 }
215
221
216 QRectF PieSliceItem::labelTextRect(QFont font, QString text)
222 QRectF PieSliceItem::labelTextRect(QFont font, QString text)
217 {
223 {
218 QFontMetricsF fm(font);
224 QFontMetricsF fm(font);
219 return fm.boundingRect(text);
225 return fm.boundingRect(text);
220 }
226 }
221
227
222 #include "moc_piesliceitem_p.cpp"
228 #include "moc_piesliceitem_p.cpp"
223
229
224 QTCOMMERCIALCHART_END_NAMESPACE
230 QTCOMMERCIALCHART_END_NAMESPACE
General Comments 0
You need to be logged in to leave comments. Login now