1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the Qt Charts module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:GPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU
19** General Public License version 3 or (at your option) any later version
20** approved by the KDE Free Qt Foundation. The licenses are as published by
21** the Free Software Foundation and appearing in the file LICENSE.GPL3
22** included in the packaging of this file. Please review the following
23** information to ensure the GNU General Public License requirements will
24** be met: https://www.gnu.org/licenses/gpl-3.0.html.
25**
26** $QT_END_LICENSE$
27**
28****************************************************************************/
29
30#include <QtCharts/QCandlestickSet>
31#include <QtGui/QPainter>
32#include <private/abstractdomain_p.h>
33#include <private/candlestick_p.h>
34#include <private/qchart_p.h>
35
36QT_CHARTS_BEGIN_NAMESPACE
37
38Candlestick::Candlestick(QCandlestickSet *set, AbstractDomain *domain, QGraphicsObject *parent)
39 : QGraphicsObject(parent),
40 m_set(set),
41 m_domain(domain),
42 m_timePeriod(0.0),
43 m_maximumColumnWidth(-1.0), // no maximum column width by default
44 m_minimumColumnWidth(-1.0), // no minimum column width by default
45 m_bodyWidth(0.5),
46 m_bodyOutlineVisible(true),
47 m_capsWidth(0.5),
48 m_capsVisible(false),
49 m_brush(QChartPrivate::defaultBrush()),
50 m_pen(QChartPrivate::defaultPen()),
51 m_hovering(false),
52 m_mousePressed(false)
53{
54 setAcceptHoverEvents(true);
55 setAcceptedMouseButtons(Qt::MouseButtonMask);
56 setFlag(flag: QGraphicsObject::ItemIsSelectable);
57}
58
59Candlestick::~Candlestick()
60{
61 // End hover event, if candlestick is deleted during it.
62 if (m_hovering)
63 emit hovered(status: false, set: m_set);
64}
65
66void Candlestick::setTimePeriod(qreal timePeriod)
67{
68 m_timePeriod = timePeriod;
69}
70
71void Candlestick::setMaximumColumnWidth(qreal maximumColumnWidth)
72{
73 m_maximumColumnWidth = maximumColumnWidth;
74}
75
76void Candlestick::setMinimumColumnWidth(qreal minimumColumnWidth)
77{
78 m_minimumColumnWidth = minimumColumnWidth;
79}
80
81void Candlestick::setBodyWidth(qreal bodyWidth)
82{
83 m_bodyWidth = bodyWidth;
84}
85
86void Candlestick::setBodyOutlineVisible(bool bodyOutlineVisible)
87{
88 m_bodyOutlineVisible = bodyOutlineVisible;
89}
90
91void Candlestick::setCapsWidth(qreal capsWidth)
92{
93 m_capsWidth = capsWidth;
94}
95
96void Candlestick::setCapsVisible(bool capsVisible)
97{
98 m_capsVisible = capsVisible;
99}
100
101void Candlestick::setIncreasingColor(const QColor &color)
102{
103 m_increasingColor = color;
104
105 update();
106}
107
108void Candlestick::setDecreasingColor(const QColor &color)
109{
110 m_decreasingColor = color;
111
112 update();
113}
114
115void Candlestick::setBrush(const QBrush &brush)
116{
117 m_brush = brush;
118
119 update();
120}
121
122void Candlestick::setPen(const QPen &pen)
123{
124 qreal widthDiff = pen.widthF() - m_pen.widthF();
125 m_boundingRect.adjust(xp1: -widthDiff, yp1: -widthDiff, xp2: widthDiff, yp2: widthDiff);
126
127 m_pen = pen;
128
129 update();
130}
131
132void Candlestick::setLayout(const CandlestickData &data)
133{
134 m_data = data;
135
136 updateGeometry(domain: m_domain);
137 update();
138}
139
140void Candlestick::mousePressEvent(QGraphicsSceneMouseEvent *event)
141{
142 m_mousePressed = true;
143 emit pressed(set: m_set);
144 QGraphicsItem::mousePressEvent(event);
145}
146
147void Candlestick::hoverEnterEvent(QGraphicsSceneHoverEvent *event)
148{
149 Q_UNUSED(event)
150
151 m_hovering = true;
152 emit hovered(status: m_hovering, set: m_set);
153}
154
155void Candlestick::hoverLeaveEvent(QGraphicsSceneHoverEvent *event)
156{
157 Q_UNUSED(event)
158
159 m_hovering = false;
160 emit hovered(status: m_hovering, set: m_set);
161}
162
163void Candlestick::mouseReleaseEvent(QGraphicsSceneMouseEvent *event)
164{
165 emit released(set: m_set);
166 if (m_mousePressed)
167 emit clicked(set: m_set);
168 m_mousePressed = false;
169 QGraphicsItem::mouseReleaseEvent(event);
170}
171
172void Candlestick::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event)
173{
174 // For candlestick a pressed signal needs to be explicitly fired for mouseDoubleClickEvent.
175 emit pressed(set: m_set);
176 emit doubleClicked(set: m_set);
177 QGraphicsItem::mouseDoubleClickEvent(event);
178}
179
180QRectF Candlestick::boundingRect() const
181{
182 return m_boundingRect;
183}
184
185void Candlestick::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget)
186{
187 Q_UNUSED(option)
188 Q_UNUSED(widget)
189
190 bool increasingTrend = (m_data.m_open < m_data.m_close);
191 QColor color = increasingTrend ? m_increasingColor : m_decreasingColor;
192
193 QBrush brush(m_brush);
194 brush.setColor(color);
195
196 painter->save();
197 painter->setBrush(brush);
198 painter->setPen(m_pen);
199 painter->setClipRect(m_boundingRect);
200 if (m_capsVisible)
201 painter->drawPath(path: m_capsPath);
202 painter->drawPath(path: m_wicksPath);
203 if (!m_bodyOutlineVisible)
204 painter->setPen(QColor(Qt::transparent));
205 painter->drawRect(rect: m_bodyRect);
206 painter->restore();
207}
208
209void Candlestick::updateGeometry(AbstractDomain *domain)
210{
211 m_domain = domain;
212
213 prepareGeometryChange();
214
215 m_capsPath = QPainterPath();
216 m_wicksPath = QPainterPath();
217 m_boundingRect = QRectF();
218
219 if (!m_data.m_series->chart())
220 return;
221
222 QList<QAbstractAxis *> axes = m_data.m_series->chart()->axes(orientation: Qt::Horizontal, series: m_data.m_series);
223 if (axes.isEmpty())
224 return;
225
226 QAbstractAxis *axisX = axes.value(i: 0);
227 if (!axisX)
228 return;
229
230 qreal columnWidth = 0.0;
231 qreal columnCenter = 0.0;
232 switch (axisX->type()) {
233 case QAbstractAxis::AxisTypeBarCategory:
234 columnWidth = 1.0 / m_data.m_seriesCount;
235 columnCenter = m_data.m_index - 0.5
236 + m_data.m_seriesIndex * columnWidth
237 + columnWidth / 2.0;
238 break;
239 case QAbstractAxis::AxisTypeDateTime:
240 case QAbstractAxis::AxisTypeValue:
241 columnWidth = m_timePeriod;
242 columnCenter = m_data.m_timestamp;
243 break;
244 default:
245 qWarning() << "Unexpected axis type";
246 return;
247 }
248
249 const qreal bodyWidth = m_bodyWidth * columnWidth;
250 const qreal bodyLeft = columnCenter - (bodyWidth / 2.0);
251 const qreal bodyRight = bodyLeft + bodyWidth;
252
253 const qreal upperBody = qMax(a: m_data.m_open, b: m_data.m_close);
254 const qreal lowerBody = qMin(a: m_data.m_open, b: m_data.m_close);
255 const bool upperWickVisible = (m_data.m_high > upperBody);
256 const bool lowerWickVisible = (m_data.m_low < lowerBody);
257
258 QPointF geometryPoint;
259 bool validData;
260
261 // upper extreme
262 geometryPoint = m_domain->calculateGeometryPoint(point: QPointF(bodyLeft, m_data.m_high), ok&: validData);
263 if (!validData)
264 return;
265 const qreal geometryUpperExtreme = geometryPoint.y();
266 // upper body
267 geometryPoint = m_domain->calculateGeometryPoint(point: QPointF(bodyLeft, upperBody), ok&: validData);
268 if (!validData)
269 return;
270 const qreal geometryBodyLeft = geometryPoint.x();
271 const qreal geometryUpperBody = geometryPoint.y();
272 // lower body
273 geometryPoint = m_domain->calculateGeometryPoint(point: QPointF(bodyRight, lowerBody), ok&: validData);
274 if (!validData)
275 return;
276 const qreal geometryBodyRight = geometryPoint.x();
277 const qreal geometryLowerBody = geometryPoint.y();
278 // lower extreme
279 geometryPoint = m_domain->calculateGeometryPoint(point: QPointF(bodyRight, m_data.m_low), ok&: validData);
280 if (!validData)
281 return;
282 const qreal geometryLowerExtreme = geometryPoint.y();
283
284 // Real Body
285 m_bodyRect.setCoords(xp1: geometryBodyLeft, yp1: geometryUpperBody, xp2: geometryBodyRight, yp2: geometryLowerBody);
286 if (m_maximumColumnWidth != -1.0) {
287 if (m_bodyRect.width() > m_maximumColumnWidth) {
288 qreal extra = (m_bodyRect.width() - m_maximumColumnWidth) / 2.0;
289 m_bodyRect.adjust(xp1: extra, yp1: 0.0, xp2: 0.0, yp2: 0.0);
290 m_bodyRect.setWidth(m_maximumColumnWidth);
291 }
292 }
293 if (m_minimumColumnWidth != -1.0) {
294 if (m_bodyRect.width() < m_minimumColumnWidth) {
295 qreal extra = (m_minimumColumnWidth - m_bodyRect.width()) / 2.0;
296 m_bodyRect.adjust(xp1: -extra, yp1: 0.0, xp2: 0.0, yp2: 0.0);
297 m_bodyRect.setWidth(m_minimumColumnWidth);
298 }
299 }
300
301 const qreal geometryCapsExtra = (m_bodyRect.width() - (m_bodyRect.width() * m_capsWidth)) /2.0;
302 const qreal geometryCapsLeft = m_bodyRect.left() + geometryCapsExtra;
303 const qreal geometryCapsRight = m_bodyRect.right() - geometryCapsExtra;
304
305 // Upper Wick and Cap
306 if (upperWickVisible) {
307 m_capsPath.moveTo(x: geometryCapsLeft, y: geometryUpperExtreme);
308 m_capsPath.lineTo(x: geometryCapsRight, y: geometryUpperExtreme);
309 m_wicksPath.moveTo(x: (geometryCapsLeft + geometryCapsRight) / 2.0, y: geometryUpperExtreme);
310 m_wicksPath.lineTo(x: (geometryCapsLeft + geometryCapsRight) / 2.0, y: geometryUpperBody);
311 }
312 // Lower Wick and Cap
313 if (lowerWickVisible) {
314 m_capsPath.moveTo(x: geometryCapsLeft, y: geometryLowerExtreme);
315 m_capsPath.lineTo(x: geometryCapsRight, y: geometryLowerExtreme);
316 m_wicksPath.moveTo(x: (geometryCapsLeft + geometryCapsRight) / 2.0, y: geometryLowerBody);
317 m_wicksPath.lineTo(x: (geometryCapsLeft + geometryCapsRight) / 2.0, y: geometryLowerExtreme);
318 }
319 m_wicksPath.closeSubpath();
320
321 // bounding rectangle top
322 qreal boundingRectTop;
323 if (upperWickVisible)
324 boundingRectTop = m_wicksPath.boundingRect().top();
325 else
326 boundingRectTop = m_bodyRect.top();
327 boundingRectTop = qMax(a: boundingRectTop, b: parentItem()->boundingRect().top());
328 // bounding rectangle right
329 qreal boundingRectRight = qMin(a: m_bodyRect.right(), b: parentItem()->boundingRect().right());
330 // bounding rectangle bottom
331 qreal boundingRectBottom;
332 if (lowerWickVisible)
333 boundingRectBottom = m_wicksPath.boundingRect().bottom();
334 else
335 boundingRectBottom = m_bodyRect.bottom();
336 boundingRectBottom = qMin(a: boundingRectBottom, b: parentItem()->boundingRect().bottom());
337 // bounding rectangle left
338 qreal boundingRectLeft = qMax(a: m_bodyRect.left(), b: parentItem()->boundingRect().left());
339
340 m_boundingRect.setTop(boundingRectTop);
341 m_boundingRect.setRight(boundingRectRight);
342 m_boundingRect.setBottom(boundingRectBottom);
343 m_boundingRect.setLeft(boundingRectLeft);
344
345 qreal extra = m_pen.widthF();
346 m_boundingRect.adjust(xp1: -extra, yp1: -extra, xp2: extra, yp2: extra);
347}
348
349QT_CHARTS_END_NAMESPACE
350
351#include "moc_candlestick_p.cpp"
352

source code of qtcharts/src/charts/candlestickchart/candlestick.cpp