|
|
/****************************************************************************
|
|
|
**
|
|
|
** 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/QBarCategoryAxis>
|
|
|
#include <QtCharts/QCandlestickSeries>
|
|
|
#include <QtCharts/QCandlestickSet>
|
|
|
#include <QtCharts/QDateTimeAxis>
|
|
|
#include <QtCharts/QHCandlestickModelMapper>
|
|
|
#include <QtCharts/QValueAxis>
|
|
|
#include <QtCore/QDateTime>
|
|
|
#include <QtCore/QDebug>
|
|
|
#include <QtWidgets/QCheckBox>
|
|
|
#include <QtWidgets/QComboBox>
|
|
|
#include <QtWidgets/QDoubleSpinBox>
|
|
|
#include <QtWidgets/QGridLayout>
|
|
|
#include <QtWidgets/QHeaderView>
|
|
|
#include <QtWidgets/QLabel>
|
|
|
#include <QtWidgets/QPushButton>
|
|
|
#include <QtWidgets/QTableView>
|
|
|
#include "customtablemodel.h"
|
|
|
#include "mainwidget.h"
|
|
|
|
|
|
QT_CHARTS_USE_NAMESPACE
|
|
|
|
|
|
MainWidget::MainWidget(QWidget *parent)
|
|
|
: QWidget(parent),
|
|
|
m_chart(new QChart()),
|
|
|
m_chartView(new QChartView(m_chart, this)),
|
|
|
m_axisX(nullptr),
|
|
|
m_axisY(nullptr),
|
|
|
m_maximumColumnWidth(-1.0),
|
|
|
m_minimumColumnWidth(5.0),
|
|
|
m_bodyOutlineVisible(true),
|
|
|
m_capsVisible(false),
|
|
|
m_bodyWidth(0.5),
|
|
|
m_capsWidth(0.5),
|
|
|
m_customIncreasingColor(false),
|
|
|
m_customDecreasingColor(false),
|
|
|
m_hModelMapper(new QHCandlestickModelMapper(this))
|
|
|
{
|
|
|
qsrand(QDateTime::currentDateTime().toTime_t());
|
|
|
|
|
|
m_chartView->setRenderHint(QPainter::Antialiasing, false);
|
|
|
|
|
|
m_hModelMapper->setModel(new CustomTableModel(this));
|
|
|
m_hModelMapper->setTimestampColumn(0);
|
|
|
m_hModelMapper->setOpenColumn(1);
|
|
|
m_hModelMapper->setHighColumn(2);
|
|
|
m_hModelMapper->setLowColumn(3);
|
|
|
m_hModelMapper->setCloseColumn(4);
|
|
|
|
|
|
QGridLayout *mainLayout = new QGridLayout();
|
|
|
mainLayout->addLayout(createSeriesControlsLayout(), 0, 0);
|
|
|
mainLayout->addLayout(createSetsControlsLayout(), 1, 0);
|
|
|
mainLayout->addLayout(createCandlestickControlsLayout(), 2, 0);
|
|
|
mainLayout->addLayout(createMiscellaneousControlsLayout(), 3, 0);
|
|
|
mainLayout->addWidget(m_chartView, 0, 1, mainLayout->rowCount() + 1, 1);
|
|
|
mainLayout->addLayout(createModelMapperControlsLayout(), 0, 2, mainLayout->rowCount(), 1);
|
|
|
setLayout(mainLayout);
|
|
|
|
|
|
addSeries();
|
|
|
}
|
|
|
|
|
|
MainWidget::~MainWidget()
|
|
|
{
|
|
|
}
|
|
|
|
|
|
QGridLayout *MainWidget::createSeriesControlsLayout()
|
|
|
{
|
|
|
QGridLayout *layout = new QGridLayout();
|
|
|
int row = 0;
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Series controls:")), row, 0, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *addSeriesButton = new QPushButton(QStringLiteral("Add a series"));
|
|
|
connect(addSeriesButton, SIGNAL(clicked(bool)), this, SLOT(addSeries()));
|
|
|
layout->addWidget(addSeriesButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *removeSeriesButton = new QPushButton(QStringLiteral("Remove a series"));
|
|
|
connect(removeSeriesButton, SIGNAL(clicked(bool)), this, SLOT(removeSeries()));
|
|
|
layout->addWidget(removeSeriesButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *removeAllSeriesButton = new QPushButton(QStringLiteral("Remove all series"));
|
|
|
connect(removeAllSeriesButton, SIGNAL(clicked(bool)), this, SLOT(removeAllSeries()));
|
|
|
layout->addWidget(removeAllSeriesButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
return layout;
|
|
|
}
|
|
|
|
|
|
QGridLayout *MainWidget::createSetsControlsLayout()
|
|
|
{
|
|
|
QGridLayout *layout = new QGridLayout();
|
|
|
int row = 0;
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Sets controls:")), row, 0, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *addSetButton = new QPushButton(QStringLiteral("Add a set"));
|
|
|
connect(addSetButton, SIGNAL(clicked(bool)), this, SLOT(addSet()));
|
|
|
layout->addWidget(addSetButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *insertSetButton = new QPushButton(QStringLiteral("Insert a set"));
|
|
|
connect(insertSetButton, SIGNAL(clicked(bool)), this, SLOT(insertSet()));
|
|
|
layout->addWidget(insertSetButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *removeSetButton = new QPushButton(QStringLiteral("Remove a set"));
|
|
|
connect(removeSetButton, SIGNAL(clicked(bool)), this, SLOT(removeSet()));
|
|
|
layout->addWidget(removeSetButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *removeAllSetsButton = new QPushButton(QStringLiteral("Remove all sets"));
|
|
|
connect(removeAllSetsButton, SIGNAL(clicked(bool)), this, SLOT(removeAllSets()));
|
|
|
layout->addWidget(removeAllSetsButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
return layout;
|
|
|
}
|
|
|
|
|
|
QGridLayout *MainWidget::createCandlestickControlsLayout()
|
|
|
{
|
|
|
QGridLayout *layout = new QGridLayout();
|
|
|
int row = 0;
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Maximum column width:")), row, 0, Qt::AlignLeft);
|
|
|
QDoubleSpinBox *maximumColumnWidthSpinBox = new QDoubleSpinBox();
|
|
|
maximumColumnWidthSpinBox->setRange(-1.0, 1024.0);
|
|
|
maximumColumnWidthSpinBox->setDecimals(0);
|
|
|
maximumColumnWidthSpinBox->setValue(m_maximumColumnWidth);
|
|
|
maximumColumnWidthSpinBox->setSingleStep(1.0);
|
|
|
connect(maximumColumnWidthSpinBox, SIGNAL(valueChanged(double)),
|
|
|
this, SLOT(changeMaximumColumnWidth(double)));
|
|
|
layout->addWidget(maximumColumnWidthSpinBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Minimum column width:")), row, 0, Qt::AlignLeft);
|
|
|
QDoubleSpinBox *minimumColumnWidthSpinBox = new QDoubleSpinBox();
|
|
|
minimumColumnWidthSpinBox->setRange(-1.0, 1024.0);
|
|
|
minimumColumnWidthSpinBox->setDecimals(0);
|
|
|
minimumColumnWidthSpinBox->setValue(m_minimumColumnWidth);
|
|
|
minimumColumnWidthSpinBox->setSingleStep(1.0);
|
|
|
connect(minimumColumnWidthSpinBox, SIGNAL(valueChanged(double)),
|
|
|
this, SLOT(changeMinimumColumnWidth(double)));
|
|
|
layout->addWidget(minimumColumnWidthSpinBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *bodyOutlineVisible = new QCheckBox(QStringLiteral("Body outline visible"));
|
|
|
connect(bodyOutlineVisible, SIGNAL(toggled(bool)), this, SLOT(bodyOutlineVisibleToggled(bool)));
|
|
|
bodyOutlineVisible->setChecked(m_bodyOutlineVisible);
|
|
|
layout->addWidget(bodyOutlineVisible, row++, 0, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *capsVisible = new QCheckBox(QStringLiteral("Caps visible"));
|
|
|
connect(capsVisible, SIGNAL(toggled(bool)), this, SLOT(capsVisibleToggled(bool)));
|
|
|
capsVisible->setChecked(m_capsVisible);
|
|
|
layout->addWidget(capsVisible, row++, 0, Qt::AlignLeft);
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Candlestick body width:")), row, 0, Qt::AlignLeft);
|
|
|
QDoubleSpinBox *bodyWidthSpinBox = new QDoubleSpinBox();
|
|
|
bodyWidthSpinBox->setRange(-1.0, 2.0);
|
|
|
bodyWidthSpinBox->setValue(m_bodyWidth);
|
|
|
bodyWidthSpinBox->setSingleStep(0.1);
|
|
|
connect(bodyWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(changeBodyWidth(double)));
|
|
|
layout->addWidget(bodyWidthSpinBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Candlestick caps width:")), row, 0, Qt::AlignLeft);
|
|
|
QDoubleSpinBox *capsWidthSpinBox = new QDoubleSpinBox();
|
|
|
capsWidthSpinBox->setRange(-1.0, 2.0);
|
|
|
capsWidthSpinBox->setValue(m_capsWidth);
|
|
|
capsWidthSpinBox->setSingleStep(0.1);
|
|
|
connect(capsWidthSpinBox, SIGNAL(valueChanged(double)), this, SLOT(changeCapsWidth(double)));
|
|
|
layout->addWidget(capsWidthSpinBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *increasingColor = new QCheckBox(QStringLiteral("Custom increasing color (only S1)"));
|
|
|
connect(increasingColor, SIGNAL(toggled(bool)), this, SLOT(customIncreasingColorToggled(bool)));
|
|
|
increasingColor->setChecked(m_customIncreasingColor);
|
|
|
layout->addWidget(increasingColor, row++, 0, 1, 2, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *decreasingColor = new QCheckBox(QStringLiteral("Custom decreasing color (only S1)"));
|
|
|
connect(decreasingColor, SIGNAL(toggled(bool)), this, SLOT(customDecreasingColorToggled(bool)));
|
|
|
decreasingColor->setChecked(m_customDecreasingColor);
|
|
|
layout->addWidget(decreasingColor, row++, 0, 1, 2, Qt::AlignLeft);
|
|
|
|
|
|
return layout;
|
|
|
}
|
|
|
|
|
|
QGridLayout *MainWidget::createMiscellaneousControlsLayout()
|
|
|
{
|
|
|
QGridLayout *layout = new QGridLayout();
|
|
|
int row = 0;
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Miscellaneous:")), row, 0, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *antialiasingCheckBox = new QCheckBox(QStringLiteral("Antialiasing"));
|
|
|
connect(antialiasingCheckBox, SIGNAL(toggled(bool)), this, SLOT(antialiasingToggled(bool)));
|
|
|
antialiasingCheckBox->setChecked(false);
|
|
|
layout->addWidget(antialiasingCheckBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *animationCheckBox = new QCheckBox(QStringLiteral("Animation"));
|
|
|
connect(animationCheckBox, SIGNAL(toggled(bool)), this, SLOT(animationToggled(bool)));
|
|
|
animationCheckBox->setChecked(false);
|
|
|
layout->addWidget(animationCheckBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *legendCheckBox = new QCheckBox(QStringLiteral("Legend"));
|
|
|
connect(legendCheckBox, SIGNAL(toggled(bool)), this, SLOT(legendToggled(bool)));
|
|
|
legendCheckBox->setChecked(true);
|
|
|
layout->addWidget(legendCheckBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QCheckBox *titleCheckBox = new QCheckBox(QStringLiteral("Title"));
|
|
|
connect(titleCheckBox, SIGNAL(toggled(bool)), this, SLOT(titleToggled(bool)));
|
|
|
titleCheckBox->setChecked(true);
|
|
|
layout->addWidget(titleCheckBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Chart theme:")), row, 0, Qt::AlignLeft);
|
|
|
QComboBox *chartThemeComboBox = new QComboBox();
|
|
|
chartThemeComboBox->addItem(QStringLiteral("Light"));
|
|
|
chartThemeComboBox->addItem(QStringLiteral("Blue Cerulean"));
|
|
|
chartThemeComboBox->addItem(QStringLiteral("Dark"));
|
|
|
chartThemeComboBox->addItem(QStringLiteral("Brown Sand"));
|
|
|
chartThemeComboBox->addItem(QStringLiteral("Blue Ncs"));
|
|
|
chartThemeComboBox->addItem(QStringLiteral("High Contrast"));
|
|
|
chartThemeComboBox->addItem(QStringLiteral("Blue Icy"));
|
|
|
chartThemeComboBox->addItem(QStringLiteral("Qt"));
|
|
|
connect(chartThemeComboBox,SIGNAL(currentIndexChanged(int)),this,SLOT(changeChartTheme(int)));
|
|
|
layout->addWidget(chartThemeComboBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("Axis X:")), row, 0, Qt::AlignLeft);
|
|
|
QComboBox *axisXComboBox = new QComboBox();
|
|
|
axisXComboBox->addItem(QStringLiteral("BarCategory"));
|
|
|
axisXComboBox->addItem(QStringLiteral("DateTime"));
|
|
|
axisXComboBox->addItem(QStringLiteral("Value"));
|
|
|
connect(axisXComboBox, SIGNAL(currentIndexChanged(int)), this, SLOT(changeAxisX(int)));
|
|
|
layout->addWidget(axisXComboBox, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
return layout;
|
|
|
}
|
|
|
|
|
|
QGridLayout *MainWidget::createModelMapperControlsLayout()
|
|
|
{
|
|
|
QGridLayout *layout = new QGridLayout();
|
|
|
int row = 0;
|
|
|
|
|
|
layout->addWidget(new QLabel(QStringLiteral("First series:")), row, 0, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *attachModelMapperButton = new QPushButton(QStringLiteral("Attach model mapper"));
|
|
|
connect(attachModelMapperButton, SIGNAL(clicked(bool)), this, SLOT(attachModelMapper()));
|
|
|
layout->addWidget(attachModelMapperButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QPushButton *detachModelMappeButton = new QPushButton(QStringLiteral("Detach model mapper"));
|
|
|
connect(detachModelMappeButton, SIGNAL(clicked(bool)), this, SLOT(detachModelMapper()));
|
|
|
layout->addWidget(detachModelMappeButton, row++, 1, Qt::AlignLeft);
|
|
|
|
|
|
QTableView *tableView = new QTableView();
|
|
|
tableView->setMinimumSize(320, 480);
|
|
|
tableView->setMaximumSize(320, 480);
|
|
|
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
|
|
tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
|
|
Q_ASSERT_X(m_hModelMapper->model(), Q_FUNC_INFO, "Model is not initialized");
|
|
|
tableView->setModel(m_hModelMapper->model());
|
|
|
layout->addWidget(tableView, row++, 0, 1, 2, Qt::AlignLeft);
|
|
|
|
|
|
layout->addItem(new QSpacerItem(1, 1, QSizePolicy::Fixed, QSizePolicy::Expanding), row++, 0);
|
|
|
|
|
|
return layout;
|
|
|
}
|
|
|
|
|
|
qreal MainWidget::randomValue(int min, int max) const
|
|
|
{
|
|
|
return (qrand() / (qreal(RAND_MAX) + 1)) * ((qMax(min, max) - qMin(min, max)) + qMin(min, max));
|
|
|
}
|
|
|
|
|
|
QCandlestickSet *MainWidget::randomSet(qreal timestamp)
|
|
|
{
|
|
|
QCandlestickSet *set = new QCandlestickSet(timestamp);
|
|
|
set->setOpen(randomValue(4, 11));
|
|
|
set->setHigh(randomValue(12, 15));
|
|
|
set->setLow(randomValue(0, 3));
|
|
|
set->setClose(randomValue(4, 11));
|
|
|
|
|
|
return set;
|
|
|
}
|
|
|
|
|
|
void MainWidget::updateAxes()
|
|
|
{
|
|
|
if (m_chart->axes().isEmpty())
|
|
|
m_chart->createDefaultAxes();
|
|
|
|
|
|
QCandlestickSeries *series;
|
|
|
if (!m_chart->series().isEmpty())
|
|
|
series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
|
|
|
else
|
|
|
series = nullptr;
|
|
|
|
|
|
m_axisX = m_chart->axes(Qt::Horizontal).first();
|
|
|
if (series && !series->candlestickSets().isEmpty()) {
|
|
|
if (m_axisX->type() == QAbstractAxis::AxisTypeBarCategory) {
|
|
|
QBarCategoryAxis *axisX = qobject_cast<QBarCategoryAxis *>(m_axisX);
|
|
|
QStringList categories;
|
|
|
for (int i = 0; i < series->candlestickSets().count(); ++i)
|
|
|
categories.append(QString::number(i));
|
|
|
axisX->setCategories(categories);
|
|
|
} else { // QAbstractAxis::AxisTypeDateTime || QAbstractAxis::AxisTypeValue
|
|
|
qreal msInMonth = 31.0 * 24.0 * 60.0 * 60.0 * 1000.0;
|
|
|
qreal min = series->candlestickSets().first()->timestamp() - msInMonth;
|
|
|
qreal max = series->candlestickSets().last()->timestamp() + msInMonth;
|
|
|
QDateTime minDateTime = QDateTime::fromMSecsSinceEpoch(min);
|
|
|
QDateTime maxDateTime = QDateTime::fromMSecsSinceEpoch(max);
|
|
|
|
|
|
if (m_axisX->type() == QAbstractAxis::AxisTypeDateTime)
|
|
|
m_axisX->setRange(minDateTime, maxDateTime);
|
|
|
else
|
|
|
m_axisX->setRange(min, max);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
m_axisY = m_chart->axes(Qt::Vertical).first();
|
|
|
m_axisY->setMax(15);
|
|
|
m_axisY->setMin(0);
|
|
|
}
|
|
|
|
|
|
void MainWidget::addSeries()
|
|
|
{
|
|
|
if (m_chart->series().count() > 9) {
|
|
|
qDebug() << "Maximum series count is 10";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
QCandlestickSeries *series = new QCandlestickSeries();
|
|
|
series->setName(QStringLiteral("S%1").arg(m_chart->series().count() + 1));
|
|
|
series->setMaximumColumnWidth(m_maximumColumnWidth);
|
|
|
series->setMinimumColumnWidth(m_minimumColumnWidth);
|
|
|
series->setBodyOutlineVisible(m_bodyOutlineVisible);
|
|
|
series->setBodyWidth(m_bodyWidth);
|
|
|
series->setCapsVisible(m_capsVisible);
|
|
|
series->setCapsWidth(m_capsWidth);
|
|
|
|
|
|
if (m_chart->series().isEmpty()) {
|
|
|
if (m_customIncreasingColor)
|
|
|
series->setIncreasingColor(QColor(Qt::green));
|
|
|
if (m_customDecreasingColor)
|
|
|
series->setDecreasingColor(QColor(Qt::red));
|
|
|
|
|
|
for (int month = 1; month <= 12; ++month) {
|
|
|
QDateTime dateTime;
|
|
|
dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), month, 1));
|
|
|
dateTime.setTime(QTime(12, 34, 56, 789));
|
|
|
|
|
|
QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch());
|
|
|
series->append(set);
|
|
|
}
|
|
|
} else {
|
|
|
QCandlestickSeries *s = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
|
|
|
for (int i = 0; i < s->candlestickSets().count(); ++i) {
|
|
|
QCandlestickSet *set = randomSet(s->candlestickSets().at(i)->timestamp());
|
|
|
series->append(set);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
m_chart->addSeries(series);
|
|
|
|
|
|
updateAxes();
|
|
|
if (!series->attachedAxes().contains(m_axisX))
|
|
|
series->attachAxis(m_axisX);
|
|
|
if (!series->attachedAxes().contains(m_axisY))
|
|
|
series->attachAxis(m_axisY);
|
|
|
}
|
|
|
|
|
|
void MainWidget::removeSeries()
|
|
|
{
|
|
|
if (m_chart->series().isEmpty()) {
|
|
|
qDebug() << "Create a series first";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (m_chart->series().count() == 1)
|
|
|
detachModelMapper();
|
|
|
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().last());
|
|
|
m_chart->removeSeries(series);
|
|
|
delete series;
|
|
|
series = nullptr;
|
|
|
}
|
|
|
|
|
|
void MainWidget::removeAllSeries()
|
|
|
{
|
|
|
if (m_chart->series().isEmpty()) {
|
|
|
qDebug() << "Create a series first";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
detachModelMapper();
|
|
|
|
|
|
m_chart->removeAllSeries();
|
|
|
}
|
|
|
|
|
|
void MainWidget::addSet()
|
|
|
{
|
|
|
if (m_chart->series().isEmpty()) {
|
|
|
qDebug() << "Create a series first";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
|
|
|
QDateTime dateTime;
|
|
|
if (series->count()) {
|
|
|
dateTime.setMSecsSinceEpoch(series->candlestickSets().last()->timestamp());
|
|
|
dateTime = dateTime.addMonths(1);
|
|
|
} else {
|
|
|
dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), 1, 1));
|
|
|
dateTime.setTime(QTime(12, 34, 56, 789));
|
|
|
}
|
|
|
|
|
|
QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch());
|
|
|
series->append(set);
|
|
|
}
|
|
|
|
|
|
updateAxes();
|
|
|
}
|
|
|
|
|
|
void MainWidget::insertSet()
|
|
|
{
|
|
|
if (m_chart->series().isEmpty()) {
|
|
|
qDebug() << "Create a series first";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
|
|
|
QDateTime dateTime;
|
|
|
if (series->count()) {
|
|
|
dateTime.setMSecsSinceEpoch(series->candlestickSets().first()->timestamp());
|
|
|
dateTime = dateTime.addMonths(-1);
|
|
|
} else {
|
|
|
dateTime.setDate(QDate(QDateTime::currentDateTime().date().year(), 1, 1));
|
|
|
dateTime.setTime(QTime(12, 34, 56, 789));
|
|
|
}
|
|
|
|
|
|
QCandlestickSet *set = randomSet(dateTime.toMSecsSinceEpoch());
|
|
|
series->insert(0, set);
|
|
|
}
|
|
|
|
|
|
updateAxes();
|
|
|
}
|
|
|
|
|
|
void MainWidget::removeSet()
|
|
|
{
|
|
|
if (m_chart->series().isEmpty()) {
|
|
|
qDebug() << "Create a series first";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
if (series->candlestickSets().isEmpty())
|
|
|
qDebug() << "Create a set first";
|
|
|
else
|
|
|
series->remove(series->candlestickSets().last());
|
|
|
}
|
|
|
|
|
|
updateAxes();
|
|
|
}
|
|
|
|
|
|
void MainWidget::removeAllSets()
|
|
|
{
|
|
|
if (m_chart->series().isEmpty()) {
|
|
|
qDebug() << "Create a series first";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
if (series->candlestickSets().isEmpty())
|
|
|
qDebug() << "Create a set first";
|
|
|
else
|
|
|
series->clear();
|
|
|
}
|
|
|
|
|
|
updateAxes();
|
|
|
}
|
|
|
|
|
|
void MainWidget::changeMaximumColumnWidth(double width)
|
|
|
{
|
|
|
m_maximumColumnWidth = width;
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
series->setMaximumColumnWidth(m_maximumColumnWidth);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::changeMinimumColumnWidth(double width)
|
|
|
{
|
|
|
m_minimumColumnWidth = width;
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
series->setMinimumColumnWidth(m_minimumColumnWidth);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::bodyOutlineVisibleToggled(bool visible)
|
|
|
{
|
|
|
m_bodyOutlineVisible = visible;
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
series->setBodyOutlineVisible(m_bodyOutlineVisible);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::capsVisibleToggled(bool visible)
|
|
|
{
|
|
|
m_capsVisible = visible;
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
series->setCapsVisible(m_capsVisible);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::changeBodyWidth(double width)
|
|
|
{
|
|
|
m_bodyWidth = width;
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
series->setBodyWidth(m_bodyWidth);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::changeCapsWidth(double width)
|
|
|
{
|
|
|
m_capsWidth = width;
|
|
|
foreach (QAbstractSeries *s, m_chart->series()) {
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(s);
|
|
|
series->setCapsWidth(m_capsWidth);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::customIncreasingColorToggled(bool custom)
|
|
|
{
|
|
|
m_customIncreasingColor = custom;
|
|
|
|
|
|
if (m_chart->series().isEmpty())
|
|
|
return;
|
|
|
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
|
|
|
if (series) {
|
|
|
QColor color = m_customIncreasingColor ? QColor(Qt::green) : QColor();
|
|
|
series->setIncreasingColor(color);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::customDecreasingColorToggled(bool custom)
|
|
|
{
|
|
|
m_customDecreasingColor = custom;
|
|
|
|
|
|
if (m_chart->series().isEmpty())
|
|
|
return;
|
|
|
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
|
|
|
if (series) {
|
|
|
QColor color = m_customDecreasingColor ? QColor(Qt::red) : QColor();
|
|
|
series->setDecreasingColor(color);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWidget::antialiasingToggled(bool enabled)
|
|
|
{
|
|
|
m_chartView->setRenderHint(QPainter::Antialiasing, enabled);
|
|
|
}
|
|
|
|
|
|
void MainWidget::animationToggled(bool enabled)
|
|
|
{
|
|
|
if (enabled)
|
|
|
m_chart->setAnimationOptions(QChart::SeriesAnimations);
|
|
|
else
|
|
|
m_chart->setAnimationOptions(QChart::NoAnimation);
|
|
|
}
|
|
|
|
|
|
void MainWidget::legendToggled(bool visible)
|
|
|
{
|
|
|
m_chart->legend()->setVisible(visible);
|
|
|
if (visible)
|
|
|
m_chart->legend()->setAlignment(Qt::AlignBottom);
|
|
|
}
|
|
|
|
|
|
void MainWidget::titleToggled(bool visible)
|
|
|
{
|
|
|
if (visible)
|
|
|
m_chart->setTitle(QStringLiteral("Candlestick Chart"));
|
|
|
else
|
|
|
m_chart->setTitle(QString());
|
|
|
}
|
|
|
|
|
|
void MainWidget::changeChartTheme(int themeIndex)
|
|
|
{
|
|
|
if (themeIndex < QChart::ChartThemeLight || themeIndex > QChart::ChartThemeQt) {
|
|
|
qDebug() << "Invalid chart theme index:" << themeIndex;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_chart->setTheme((QChart::ChartTheme)(themeIndex));
|
|
|
}
|
|
|
|
|
|
void MainWidget::changeAxisX(int axisXIndex)
|
|
|
{
|
|
|
if (m_axisX) {
|
|
|
m_chart->removeAxis(m_axisX);
|
|
|
delete m_axisX;
|
|
|
}
|
|
|
|
|
|
switch (axisXIndex) {
|
|
|
case 0:
|
|
|
m_axisX = new QBarCategoryAxis();
|
|
|
break;
|
|
|
case 1:
|
|
|
m_axisX = new QDateTimeAxis();
|
|
|
break;
|
|
|
case 2:
|
|
|
m_axisX = new QValueAxis();
|
|
|
break;
|
|
|
default:
|
|
|
qDebug() << "Invalid axis x index:" << axisXIndex;
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
m_chart->addAxis(m_axisX, Qt::AlignBottom);
|
|
|
|
|
|
updateAxes();
|
|
|
|
|
|
foreach (QAbstractSeries *series, m_chart->series())
|
|
|
series->attachAxis(m_axisX);
|
|
|
}
|
|
|
|
|
|
void MainWidget::attachModelMapper()
|
|
|
{
|
|
|
if (m_hModelMapper->series()) {
|
|
|
qDebug() << "Model mapper is already attached";
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
if (m_chart->series().isEmpty())
|
|
|
addSeries();
|
|
|
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_chart->series().at(0));
|
|
|
Q_ASSERT(series);
|
|
|
series->setName(QStringLiteral("SWMM")); // Series With Model Mapper
|
|
|
|
|
|
CustomTableModel *model = qobject_cast<CustomTableModel *>(m_hModelMapper->model());
|
|
|
foreach (QCandlestickSet *set, series->candlestickSets())
|
|
|
model->addRow(set);
|
|
|
|
|
|
m_hModelMapper->setFirstCandlestickSetRow(0);
|
|
|
m_hModelMapper->setLastCandlestickSetRow(model->rowCount() - 1);
|
|
|
m_hModelMapper->setSeries(series);
|
|
|
}
|
|
|
|
|
|
void MainWidget::detachModelMapper()
|
|
|
{
|
|
|
if (!m_hModelMapper->series())
|
|
|
return;
|
|
|
|
|
|
QCandlestickSeries *series = qobject_cast<QCandlestickSeries *>(m_hModelMapper->series());
|
|
|
Q_ASSERT(series);
|
|
|
series->setName(QStringLiteral("S1"));
|
|
|
|
|
|
m_hModelMapper->setSeries(nullptr);
|
|
|
m_hModelMapper->setFirstCandlestickSetRow(-1);
|
|
|
m_hModelMapper->setLastCandlestickSetRow(-1);
|
|
|
|
|
|
CustomTableModel *model = qobject_cast<CustomTableModel *>(m_hModelMapper->model());
|
|
|
model->clearRows();
|
|
|
}
|
|
|
|