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