| /**************************************************************************** |
| ** |
| ** 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 <QtCharts/QCandlestickSet> |
| #include <QtGui/QPainter> |
| #include <private/abstractdomain_p.h> |
| #include <private/candlestick_p.h> |
| #include <private/qchart_p.h> |
| |
| 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<QAbstractAxis *> 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); |
| } |
| |
| QT_CHARTS_END_NAMESPACE |
| |
| #include "moc_candlestick_p.cpp" |