/**************************************************************************** ** ** Copyright (C) 2016 The Qt Company Ltd. ** Contact: https://www.qt.io/licensing/ ** ** This file is part of the Qt Charts module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:GPL$ ** Commercial License Usage ** Licensees holding valid commercial Qt licenses may use this file in ** accordance with the commercial license agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and The Qt Company. For licensing terms ** and conditions see https://www.qt.io/terms-conditions. For further ** information use the contact form at https://www.qt.io/contact-us. ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License version 3 or (at your option) any later version ** approved by the KDE Free Qt Foundation. The licenses are as published by ** the Free Software Foundation and appearing in the file LICENSE.GPL3 ** included in the packaging of this file. Please review the following ** information to ensure the GNU General Public License requirements will ** be met: https://www.gnu.org/licenses/gpl-3.0.html. ** ** $QT_END_LICENSE$ ** ****************************************************************************/ #include #include #include #include #include QT_CHARTS_BEGIN_NAMESPACE Candlestick::Candlestick(QCandlestickSet *set, AbstractDomain *domain, QGraphicsObject *parent) : QGraphicsObject(parent), m_set(set), m_domain(domain), m_timePeriod(0.0), m_maximumColumnWidth(-1.0), // no maximum column width by default m_minimumColumnWidth(-1.0), // no minimum column width by default m_bodyWidth(0.5), m_bodyOutlineVisible(true), m_capsWidth(0.5), m_capsVisible(false), m_brush(QChartPrivate::defaultBrush()), m_pen(QChartPrivate::defaultPen()), m_hovering(false), m_mousePressed(false) { setAcceptHoverEvents(true); setAcceptedMouseButtons(Qt::MouseButtonMask); setFlag(QGraphicsObject::ItemIsSelectable); } Candlestick::~Candlestick() { // End hover event, if candlestick is deleted during it. if (m_hovering) emit hovered(false, m_set); } void Candlestick::setTimePeriod(qreal timePeriod) { m_timePeriod = timePeriod; } void Candlestick::setMaximumColumnWidth(qreal maximumColumnWidth) { m_maximumColumnWidth = maximumColumnWidth; } void Candlestick::setMinimumColumnWidth(qreal minimumColumnWidth) { m_minimumColumnWidth = minimumColumnWidth; } void Candlestick::setBodyWidth(qreal bodyWidth) { m_bodyWidth = bodyWidth; } void Candlestick::setBodyOutlineVisible(bool bodyOutlineVisible) { m_bodyOutlineVisible = bodyOutlineVisible; } void Candlestick::setCapsWidth(qreal capsWidth) { m_capsWidth = capsWidth; } void Candlestick::setCapsVisible(bool capsVisible) { m_capsVisible = capsVisible; } void Candlestick::setIncreasingColor(const QColor &color) { m_increasingColor = color; update(); } void Candlestick::setDecreasingColor(const QColor &color) { m_decreasingColor = color; update(); } void Candlestick::setBrush(const QBrush &brush) { m_brush = brush; update(); } void Candlestick::setPen(const QPen &pen) { qreal widthDiff = pen.widthF() - m_pen.widthF(); m_boundingRect.adjust(-widthDiff, -widthDiff, widthDiff, widthDiff); m_pen = pen; update(); } void Candlestick::setLayout(const CandlestickData &data) { m_data = data; updateGeometry(m_domain); update(); } void Candlestick::mousePressEvent(QGraphicsSceneMouseEvent *event) { m_mousePressed = true; emit pressed(m_set); QGraphicsItem::mousePressEvent(event); } void Candlestick::hoverEnterEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event) m_hovering = true; emit hovered(m_hovering, m_set); } void Candlestick::hoverLeaveEvent(QGraphicsSceneHoverEvent *event) { Q_UNUSED(event) m_hovering = false; emit hovered(m_hovering, m_set); } void Candlestick::mouseReleaseEvent(QGraphicsSceneMouseEvent *event) { emit released(m_set); if (m_mousePressed) emit clicked(m_set); m_mousePressed = false; QGraphicsItem::mouseReleaseEvent(event); } void Candlestick::mouseDoubleClickEvent(QGraphicsSceneMouseEvent *event) { // For candlestick a pressed signal needs to be explicitly fired for mouseDoubleClickEvent. emit pressed(m_set); emit doubleClicked(m_set); QGraphicsItem::mouseDoubleClickEvent(event); } QRectF Candlestick::boundingRect() const { return m_boundingRect; } void Candlestick::paint(QPainter *painter, const QStyleOptionGraphicsItem *option, QWidget *widget) { Q_UNUSED(option) Q_UNUSED(widget) bool increasingTrend = (m_data.m_open < m_data.m_close); QColor color = increasingTrend ? m_increasingColor : m_decreasingColor; QBrush brush(m_brush); brush.setColor(color); painter->save(); painter->setBrush(brush); painter->setPen(m_pen); painter->setClipRect(m_boundingRect); if (m_capsVisible) painter->drawPath(m_capsPath); painter->drawPath(m_wicksPath); if (!m_bodyOutlineVisible) painter->setPen(QColor(Qt::transparent)); painter->drawRect(m_bodyRect); painter->restore(); } void Candlestick::updateGeometry(AbstractDomain *domain) { m_domain = domain; prepareGeometryChange(); m_capsPath = QPainterPath(); m_wicksPath = QPainterPath(); m_boundingRect = QRectF(); if (!m_data.m_series->chart()) return; QList axes = m_data.m_series->chart()->axes(Qt::Horizontal, m_data.m_series); if (axes.isEmpty()) return; QAbstractAxis *axisX = axes.value(0); if (!axisX) return; qreal columnWidth = 0.0; qreal columnCenter = 0.0; switch (axisX->type()) { case QAbstractAxis::AxisTypeBarCategory: columnWidth = 1.0 / m_data.m_seriesCount; columnCenter = m_data.m_index - 0.5 + m_data.m_seriesIndex * columnWidth + columnWidth / 2.0; break; case QAbstractAxis::AxisTypeDateTime: case QAbstractAxis::AxisTypeValue: columnWidth = m_timePeriod; columnCenter = m_data.m_timestamp; break; default: qWarning() << "Unexpected axis type"; return; } const qreal bodyWidth = m_bodyWidth * columnWidth; const qreal bodyLeft = columnCenter - (bodyWidth / 2.0); const qreal bodyRight = bodyLeft + bodyWidth; const qreal upperBody = qMax(m_data.m_open, m_data.m_close); const qreal lowerBody = qMin(m_data.m_open, m_data.m_close); const bool upperWickVisible = (m_data.m_high > upperBody); const bool lowerWickVisible = (m_data.m_low < lowerBody); QPointF geometryPoint; bool validData; // upper extreme geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyLeft, m_data.m_high), validData); if (!validData) return; const qreal geometryUpperExtreme = geometryPoint.y(); // upper body geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyLeft, upperBody), validData); if (!validData) return; const qreal geometryBodyLeft = geometryPoint.x(); const qreal geometryUpperBody = geometryPoint.y(); // lower body geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyRight, lowerBody), validData); if (!validData) return; const qreal geometryBodyRight = geometryPoint.x(); const qreal geometryLowerBody = geometryPoint.y(); // lower extreme geometryPoint = m_domain->calculateGeometryPoint(QPointF(bodyRight, m_data.m_low), validData); if (!validData) return; const qreal geometryLowerExtreme = geometryPoint.y(); // Real Body m_bodyRect.setCoords(geometryBodyLeft, geometryUpperBody, geometryBodyRight, geometryLowerBody); if (m_maximumColumnWidth != -1.0) { if (m_bodyRect.width() > m_maximumColumnWidth) { qreal extra = (m_bodyRect.width() - m_maximumColumnWidth) / 2.0; m_bodyRect.adjust(extra, 0.0, 0.0, 0.0); m_bodyRect.setWidth(m_maximumColumnWidth); } } if (m_minimumColumnWidth != -1.0) { if (m_bodyRect.width() < m_minimumColumnWidth) { qreal extra = (m_minimumColumnWidth - m_bodyRect.width()) / 2.0; m_bodyRect.adjust(-extra, 0.0, 0.0, 0.0); m_bodyRect.setWidth(m_minimumColumnWidth); } } const qreal geometryCapsExtra = (m_bodyRect.width() - (m_bodyRect.width() * m_capsWidth)) /2.0; const qreal geometryCapsLeft = m_bodyRect.left() + geometryCapsExtra; const qreal geometryCapsRight = m_bodyRect.right() - geometryCapsExtra; // Upper Wick and Cap if (upperWickVisible) { m_capsPath.moveTo(geometryCapsLeft, geometryUpperExtreme); m_capsPath.lineTo(geometryCapsRight, geometryUpperExtreme); m_wicksPath.moveTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryUpperExtreme); m_wicksPath.lineTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryUpperBody); } // Lower Wick and Cap if (lowerWickVisible) { m_capsPath.moveTo(geometryCapsLeft, geometryLowerExtreme); m_capsPath.lineTo(geometryCapsRight, geometryLowerExtreme); m_wicksPath.moveTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryLowerBody); m_wicksPath.lineTo((geometryCapsLeft + geometryCapsRight) / 2.0, geometryLowerExtreme); } m_wicksPath.closeSubpath(); // bounding rectangle top qreal boundingRectTop; if (upperWickVisible) boundingRectTop = m_wicksPath.boundingRect().top(); else boundingRectTop = m_bodyRect.top(); boundingRectTop = qMax(boundingRectTop, parentItem()->boundingRect().top()); // bounding rectangle right qreal boundingRectRight = qMin(m_bodyRect.right(), parentItem()->boundingRect().right()); // bounding rectangle bottom qreal boundingRectBottom; if (lowerWickVisible) boundingRectBottom = m_wicksPath.boundingRect().bottom(); else boundingRectBottom = m_bodyRect.bottom(); boundingRectBottom = qMin(boundingRectBottom, parentItem()->boundingRect().bottom()); // bounding rectangle left qreal boundingRectLeft = qMax(m_bodyRect.left(), parentItem()->boundingRect().left()); m_boundingRect.setTop(boundingRectTop); m_boundingRect.setRight(boundingRectRight); m_boundingRect.setBottom(boundingRectBottom); m_boundingRect.setLeft(boundingRectLeft); qreal extra = m_pen.widthF(); m_boundingRect.adjust(-extra, -extra, extra, extra); } #include "moc_candlestick_p.cpp" QT_CHARTS_END_NAMESPACE