blob: eb899d9250048cec9fcfcf65cdb9f82450d0ceff [file] [log] [blame]
/****************************************************************************
**
** 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"