mainwidget.cpp
528 lines
| 17.1 KiB
| text/x-c
|
CppLexer
|
r0 | /**************************************************************************** | ||
** | ||||
** 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 "mainwidget.h" | ||||
#include "customtablemodel.h" | ||||
#include "pentool.h" | ||||
#include <QtCharts/QVBoxPlotModelMapper> | ||||
#include <QtWidgets/QTableView> | ||||
#include <QtWidgets/QHeaderView> | ||||
#include <QtCharts/QChartView> | ||||
#include <QtCharts/QBoxPlotSeries> | ||||
#include <QtCharts/QBoxSet> | ||||
#include <QtCharts/QLegend> | ||||
#include <QtCharts/QBarCategoryAxis> | ||||
#include <QtGui/QBrush> | ||||
#include <QtGui/QColor> | ||||
#include <QtWidgets/QPushButton> | ||||
#include <QtWidgets/QComboBox> | ||||
#include <QtWidgets/QSpinBox> | ||||
#include <QtWidgets/QCheckBox> | ||||
#include <QtWidgets/QGridLayout> | ||||
#include <QtWidgets/QHBoxLayout> | ||||
#include <QtWidgets/QLabel> | ||||
#include <QtWidgets/QSpacerItem> | ||||
#include <QtWidgets/QMessageBox> | ||||
#include <cmath> | ||||
#include <QtCore/QDebug> | ||||
#include <QtGui/QStandardItemModel> | ||||
#include <QtCharts/QBarCategoryAxis> | ||||
#include <QtCharts/QLogValueAxis> | ||||
QT_CHARTS_USE_NAMESPACE | ||||
static const QString allCategories[] = {"Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"}; | ||||
static const int maxCategories = 12; | ||||
MainWidget::MainWidget(QWidget *parent) : | ||||
QWidget(parent), | ||||
m_chart(0), | ||||
m_axis(0), | ||||
m_rowPos(0), | ||||
m_seriesCount(0) | ||||
{ | ||||
m_chart = new QChart(); | ||||
m_penTool = new PenTool("Whiskers pen", this); | ||||
// Grid layout for the controls for configuring the chart widget | ||||
QGridLayout *grid = new QGridLayout(); | ||||
// Create add a series button | ||||
QPushButton *addSeriesButton = new QPushButton("Add a series"); | ||||
connect(addSeriesButton, SIGNAL(clicked()), this, SLOT(addSeries())); | ||||
grid->addWidget(addSeriesButton, m_rowPos++, 1); | ||||
// Create remove a series button | ||||
QPushButton *removeSeriesButton = new QPushButton("Remove a series"); | ||||
connect(removeSeriesButton, SIGNAL(clicked()), this, SLOT(removeSeries())); | ||||
grid->addWidget(removeSeriesButton, m_rowPos++, 1); | ||||
// Create add a single box button | ||||
QPushButton *addBoxButton = new QPushButton("Add a box"); | ||||
connect(addBoxButton, SIGNAL(clicked()), this, SLOT(addBox())); | ||||
grid->addWidget(addBoxButton, m_rowPos++, 1); | ||||
// Create insert a box button | ||||
QPushButton *insertBoxButton = new QPushButton("Insert a box"); | ||||
connect(insertBoxButton, SIGNAL(clicked()), this, SLOT(insertBox())); | ||||
grid->addWidget(insertBoxButton, m_rowPos++, 1); | ||||
// Create add a single box button | ||||
QPushButton *removeBoxButton = new QPushButton("Remove a box"); | ||||
connect(removeBoxButton, SIGNAL(clicked()), this, SLOT(removeBox())); | ||||
grid->addWidget(removeBoxButton, m_rowPos++, 1); | ||||
// Create clear button | ||||
QPushButton *clearButton = new QPushButton("Clear"); | ||||
connect(clearButton, SIGNAL(clicked()), this, SLOT(clear())); | ||||
grid->addWidget(clearButton, m_rowPos++, 1); | ||||
// Create clear button | ||||
QPushButton *clearBoxButton = new QPushButton("ClearBox"); | ||||
connect(clearBoxButton, SIGNAL(clicked()), this, SLOT(clearBox())); | ||||
grid->addWidget(clearBoxButton, m_rowPos++, 1); | ||||
// Create set brush button | ||||
QPushButton *setBrushButton = new QPushButton("Set brush"); | ||||
connect(setBrushButton, SIGNAL(clicked()), this, SLOT(setBrush())); | ||||
grid->addWidget(setBrushButton, m_rowPos++, 1); | ||||
// Create set whiskers pen button | ||||
QPushButton *setWhiskersButton = new QPushButton("Whiskers pen"); | ||||
connect(setWhiskersButton, SIGNAL(clicked()), m_penTool, SLOT(show())); | ||||
connect(m_penTool, SIGNAL(changed()), this, SLOT(changePen())); | ||||
grid->addWidget(setWhiskersButton, m_rowPos++, 1); | ||||
// Box width setting | ||||
m_boxWidthSB = new QDoubleSpinBox(); | ||||
m_boxWidthSB->setMinimum(-1.0); | ||||
m_boxWidthSB->setMaximum(2.0); | ||||
m_boxWidthSB->setSingleStep(0.1); | ||||
m_boxWidthSB->setValue(0.5); | ||||
grid->addWidget(new QLabel("Box width:"), m_rowPos, 0); | ||||
grid->addWidget(m_boxWidthSB, m_rowPos++, 1); | ||||
connect(m_boxWidthSB, SIGNAL(valueChanged(double)), this, SLOT(setBoxWidth(double))); | ||||
initThemeCombo(grid); | ||||
initCheckboxes(grid); | ||||
QTableView *tableView = new QTableView; | ||||
m_model = new CustomTableModel(tableView); | ||||
tableView->setModel(m_model); | ||||
tableView->setMaximumWidth(200); | ||||
grid->addWidget(tableView, m_rowPos++, 0, 3, 2, Qt::AlignLeft); | ||||
tableView->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch); | ||||
tableView->verticalHeader()->setSectionResizeMode(QHeaderView::Stretch); | ||||
// add row with empty label to make all the other rows static | ||||
grid->addWidget(new QLabel(""), grid->rowCount(), 0); | ||||
grid->setRowStretch(grid->rowCount() - 1, 1); | ||||
// Create chart view with the chart | ||||
m_chartView = new QChartView(m_chart, this); | ||||
// As a default antialiasing is off | ||||
m_chartView->setRenderHint(QPainter::Antialiasing, false); | ||||
// Another grid layout as a main layout | ||||
QGridLayout *mainLayout = new QGridLayout(); | ||||
mainLayout->addLayout(grid, 0, 0); | ||||
mainLayout->addWidget(m_chartView, 0, 1, 3, 1); | ||||
setLayout(mainLayout); | ||||
legendToggled(false); | ||||
animationToggled(false); | ||||
} | ||||
// Combo box for selecting theme | ||||
void MainWidget::initThemeCombo(QGridLayout *grid) | ||||
{ | ||||
QComboBox *chartTheme = new QComboBox(); | ||||
chartTheme->addItem("Default"); | ||||
chartTheme->addItem("Light"); | ||||
chartTheme->addItem("Blue Cerulean"); | ||||
chartTheme->addItem("Dark"); | ||||
chartTheme->addItem("Brown Sand"); | ||||
chartTheme->addItem("Blue NCS"); | ||||
chartTheme->addItem("High Contrast"); | ||||
chartTheme->addItem("Blue Icy"); | ||||
chartTheme->addItem("Qt"); | ||||
connect(chartTheme, SIGNAL(currentIndexChanged(int)), | ||||
this, SLOT(changeChartTheme(int))); | ||||
grid->addWidget(new QLabel("Chart theme:"), m_rowPos, 0); | ||||
grid->addWidget(chartTheme, m_rowPos++, 1); | ||||
} | ||||
// Different check boxes for customizing chart | ||||
void MainWidget::initCheckboxes(QGridLayout *grid) | ||||
{ | ||||
QCheckBox *animationCheckBox = new QCheckBox("Animation"); | ||||
connect(animationCheckBox, SIGNAL(toggled(bool)), this, SLOT(animationToggled(bool))); | ||||
animationCheckBox->setChecked(false); | ||||
grid->addWidget(animationCheckBox, m_rowPos++, 0); | ||||
QCheckBox *legendCheckBox = new QCheckBox("Legend"); | ||||
connect(legendCheckBox, SIGNAL(toggled(bool)), this, SLOT(legendToggled(bool))); | ||||
legendCheckBox->setChecked(false); | ||||
grid->addWidget(legendCheckBox, m_rowPos++, 0); | ||||
QCheckBox *titleCheckBox = new QCheckBox("Title"); | ||||
connect(titleCheckBox, SIGNAL(toggled(bool)), this, SLOT(titleToggled(bool))); | ||||
titleCheckBox->setChecked(false); | ||||
grid->addWidget(titleCheckBox, m_rowPos++, 0); | ||||
QCheckBox *antialiasingCheckBox = new QCheckBox("Antialiasing"); | ||||
connect(antialiasingCheckBox, SIGNAL(toggled(bool)), this, SLOT(antialiasingToggled(bool))); | ||||
antialiasingCheckBox->setChecked(false); | ||||
grid->addWidget(antialiasingCheckBox, m_rowPos++, 0); | ||||
QCheckBox *modelMapperCheckBox = new QCheckBox("Use model mapper"); | ||||
connect(modelMapperCheckBox, SIGNAL(toggled(bool)), this, SLOT(modelMapperToggled(bool))); | ||||
modelMapperCheckBox->setChecked(false); | ||||
grid->addWidget(modelMapperCheckBox, m_rowPos++, 0); | ||||
m_boxOutlined = new QCheckBox("Box outlined"); | ||||
connect(m_boxOutlined, SIGNAL(toggled(bool)), this, SLOT(boxOutlineToggled(bool))); | ||||
m_boxOutlined->setChecked(true); | ||||
grid->addWidget(m_boxOutlined, m_rowPos++, 0); | ||||
} | ||||
void MainWidget::updateAxis(int categoryCount) | ||||
{ | ||||
if (!m_axis) { | ||||
m_chart->createDefaultAxes(); | ||||
m_axis = new QBarCategoryAxis(); | ||||
} | ||||
QStringList categories; | ||||
for (int i = 0; i < categoryCount; i++) | ||||
categories << allCategories[i]; | ||||
m_axis->setCategories(categories); | ||||
} | ||||
void MainWidget::addSeries() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::addSeries()"; | ||||
if (m_seriesCount > 9) | ||||
return; | ||||
// Initial data | ||||
QBoxSet *set0 = new QBoxSet(); | ||||
QBoxSet *set1 = new QBoxSet(); | ||||
QBoxSet *set2 = new QBoxSet(); | ||||
QBoxSet *set3 = new QBoxSet(); | ||||
QBoxSet *set4 = new QBoxSet(); | ||||
QBoxSet *set5 = new QBoxSet(); | ||||
// low bot med top upp | ||||
*set0 << -1 << 2 << 4 << 13 << 15; | ||||
*set1 << 5 << 6 << 7.5 << 8 << 12; | ||||
*set2 << 3 << 5 << 5.7 << 8 << 9; | ||||
*set3 << 5 << 6 << 6.8 << 7 << 8; | ||||
*set4 << 4 << 5 << 5.2 << 6 << 7; | ||||
*set5 << 4 << 7 << 8.2 << 9 << 10; | ||||
m_series[m_seriesCount] = new QBoxPlotSeries(); | ||||
m_series[m_seriesCount]->append(set0); | ||||
m_series[m_seriesCount]->append(set1); | ||||
m_series[m_seriesCount]->append(set2); | ||||
m_series[m_seriesCount]->append(set3); | ||||
m_series[m_seriesCount]->append(set4); | ||||
m_series[m_seriesCount]->append(set5); | ||||
m_series[m_seriesCount]->setName("Box & Whiskers"); | ||||
connect(m_series[m_seriesCount], SIGNAL(clicked(QBoxSet*)), this, SLOT(boxClicked(QBoxSet*))); | ||||
connect(m_series[m_seriesCount], SIGNAL(pressed(QBoxSet*)), this, SLOT(boxPressed(QBoxSet*))); | ||||
connect(m_series[m_seriesCount], SIGNAL(released(QBoxSet*)), this, SLOT(boxReleased(QBoxSet*))); | ||||
connect(m_series[m_seriesCount], SIGNAL(doubleClicked(QBoxSet*)), | ||||
this, SLOT(boxDoubleClicked(QBoxSet*))); | ||||
connect(m_series[m_seriesCount], SIGNAL(hovered(bool, QBoxSet*)), this, SLOT(boxHovered(bool, QBoxSet*))); | ||||
connect(set1, SIGNAL(clicked()), this, SLOT(singleBoxClicked())); | ||||
connect(set1, SIGNAL(pressed()), this, SLOT(singleBoxPressed())); | ||||
connect(set1, SIGNAL(released()), this, SLOT(singleBoxReleased())); | ||||
connect(set1, SIGNAL(doubleClicked()), this, SLOT(singleBoxDoubleClicked())); | ||||
connect(set2, SIGNAL(hovered(bool)), this, SLOT(singleBoxHovered(bool))); | ||||
m_series[m_seriesCount]->setBoxOutlineVisible(m_boxOutlined->checkState()); | ||||
m_series[m_seriesCount]->setBoxWidth(m_boxWidthSB->value()); | ||||
m_chart->addSeries(m_series[m_seriesCount]); | ||||
updateAxis(m_series[0]->count()); | ||||
m_chart->setAxisX(m_axis, m_series[m_seriesCount]); | ||||
m_seriesCount++; | ||||
} | ||||
void MainWidget::removeSeries() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::removeSeries()"; | ||||
if (m_seriesCount > 0) { | ||||
m_seriesCount--; | ||||
m_chart->removeSeries(m_series[m_seriesCount]); | ||||
delete m_series[m_seriesCount]; | ||||
m_series[m_seriesCount] = 0; | ||||
} else { | ||||
qDebug() << "Create a series first"; | ||||
} | ||||
} | ||||
void MainWidget::addBox() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::addBox()"; | ||||
if (m_seriesCount > 0 && m_series[0]->count() < maxCategories) { | ||||
QBoxSet *newSet = new QBoxSet(); | ||||
newSet->setValue(QBoxSet::LowerExtreme, 5.0); | ||||
newSet->setValue(QBoxSet::LowerQuartile, 6.0); | ||||
newSet->setValue(QBoxSet::Median, 6.8); | ||||
newSet->setValue(QBoxSet::UpperQuartile, 7.0); | ||||
newSet->setValue(QBoxSet::UpperExtreme, 8.0); | ||||
updateAxis(m_series[0]->count() + 1); | ||||
m_series[0]->append(newSet); | ||||
} | ||||
} | ||||
void MainWidget::insertBox() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::insertBox()"; | ||||
if (m_seriesCount > 0 && m_series[0]->count() < maxCategories) { | ||||
updateAxis(m_series[0]->count() + 1); | ||||
for (int i = 0; i < m_seriesCount; i++) { | ||||
QBoxSet *newSet = new QBoxSet(); | ||||
*newSet << 2 << 6 << 6.8 << 7 << 10; | ||||
m_series[i]->insert(1, newSet); | ||||
} | ||||
} | ||||
} | ||||
void MainWidget::removeBox() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::removeBox"; | ||||
if (m_seriesCount > 0) { | ||||
for (int i = 0; i < m_seriesCount; i++) { | ||||
qDebug() << "m_series[i]->count() = " << m_series[i]->count(); | ||||
if (m_series[i]->count()) { | ||||
QList<QBoxSet *> sets = m_series[i]->boxSets(); | ||||
m_series[i]->remove(sets.at(m_series[i]->count() - 1)); | ||||
} | ||||
} | ||||
updateAxis(m_series[0]->count()); | ||||
} else { | ||||
qDebug() << "Create a series first"; | ||||
} | ||||
} | ||||
void MainWidget::clear() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::clear"; | ||||
if (m_seriesCount > 0) | ||||
m_series[0]->clear(); | ||||
else | ||||
qDebug() << "Create a series first"; | ||||
} | ||||
void MainWidget::clearBox() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::clearBox"; | ||||
if (m_seriesCount > 0) { | ||||
QList<QBoxSet *> sets = m_series[0]->boxSets(); | ||||
if (sets.count() > 1) | ||||
sets.at(1)->clear(); | ||||
else | ||||
qDebug() << "Create a series with at least two items first"; | ||||
} else { | ||||
qDebug() << "Create a series first"; | ||||
} | ||||
} | ||||
void MainWidget::setBrush() | ||||
{ | ||||
qDebug() << "BoxPlotTester::MainWidget::setBrush"; | ||||
if (m_seriesCount > 0) { | ||||
QList<QBoxSet *> sets = m_series[0]->boxSets(); | ||||
if (sets.count() > 1) | ||||
sets.at(1)->setBrush(QBrush(QColor(Qt::yellow))); | ||||
else | ||||
qDebug() << "Create a series with at least two items first"; | ||||
} else { | ||||
qDebug() << "Create a series first"; | ||||
} | ||||
} | ||||
void MainWidget::animationToggled(bool enabled) | ||||
{ | ||||
qDebug() << "BoxPlotTester::Animation toggled to " << enabled; | ||||
if (enabled) | ||||
m_chart->setAnimationOptions(QChart::SeriesAnimations); | ||||
else | ||||
m_chart->setAnimationOptions(QChart::NoAnimation); | ||||
} | ||||
void MainWidget::legendToggled(bool enabled) | ||||
{ | ||||
qDebug() << "BoxPlotTester::Legend toggled to " << enabled; | ||||
m_chart->legend()->setVisible(enabled); | ||||
if (enabled) | ||||
m_chart->legend()->setAlignment(Qt::AlignBottom); | ||||
} | ||||
void MainWidget::titleToggled(bool enabled) | ||||
{ | ||||
qDebug() << "BoxPlotTester::Title toggled to " << enabled; | ||||
if (enabled) | ||||
m_chart->setTitle("Simple boxplotchart example"); | ||||
else | ||||
m_chart->setTitle(""); | ||||
} | ||||
void MainWidget::antialiasingToggled(bool enabled) | ||||
{ | ||||
qDebug() << "BoxPlotTester::antialiasingToggled toggled to " << enabled; | ||||
m_chartView->setRenderHint(QPainter::Antialiasing, enabled); | ||||
} | ||||
void MainWidget::boxOutlineToggled(bool visible) | ||||
{ | ||||
qDebug() << "BoxPlotTester::boxOutlineToggled toggled to " << visible; | ||||
for (int i = 0; i < m_seriesCount; i++) | ||||
m_series[i]->setBoxOutlineVisible(visible); | ||||
} | ||||
void MainWidget::modelMapperToggled(bool enabled) | ||||
{ | ||||
if (enabled) { | ||||
m_series[m_seriesCount] = new QBoxPlotSeries(); | ||||
int first = 0; | ||||
int count = 5; | ||||
QVBoxPlotModelMapper *mapper = new QVBoxPlotModelMapper(this); | ||||
mapper->setFirstBoxSetColumn(0); | ||||
mapper->setLastBoxSetColumn(5); | ||||
mapper->setFirstRow(first); | ||||
mapper->setRowCount(count); | ||||
mapper->setSeries(m_series[m_seriesCount]); | ||||
mapper->setModel(m_model); | ||||
m_chart->addSeries(m_series[m_seriesCount]); | ||||
m_seriesCount++; | ||||
} else { | ||||
removeSeries(); | ||||
} | ||||
} | ||||
void MainWidget::changeChartTheme(int themeIndex) | ||||
{ | ||||
qDebug() << "BoxPlotTester::changeChartTheme: " << themeIndex; | ||||
if (themeIndex == 0) | ||||
m_chart->setTheme(QChart::ChartThemeLight); | ||||
else | ||||
m_chart->setTheme((QChart::ChartTheme) (themeIndex - 1)); | ||||
} | ||||
void MainWidget::boxClicked(QBoxSet *set) | ||||
{ | ||||
qDebug() << "boxClicked, median = " << set->at(QBoxSet::Median); | ||||
} | ||||
void MainWidget::boxHovered(bool state, QBoxSet *set) | ||||
{ | ||||
if (state) | ||||
qDebug() << "box median " << set->at(QBoxSet::Median) << " hover started"; | ||||
else | ||||
qDebug() << "box median " << set->at(QBoxSet::Median) << " hover ended"; | ||||
} | ||||
void MainWidget::boxPressed(QBoxSet *set) | ||||
{ | ||||
qDebug() << "boxPressed, median = " << set->at(QBoxSet::Median); | ||||
} | ||||
void MainWidget::boxReleased(QBoxSet *set) | ||||
{ | ||||
qDebug() << "boxReleased, median = " << set->at(QBoxSet::Median); | ||||
} | ||||
void MainWidget::boxDoubleClicked(QBoxSet *set) | ||||
{ | ||||
qDebug() << "boxDoubleClicked, median = " << set->at(QBoxSet::Median); | ||||
} | ||||
void MainWidget::singleBoxClicked() | ||||
{ | ||||
qDebug() << "singleBoxClicked"; | ||||
} | ||||
void MainWidget::singleBoxPressed() | ||||
{ | ||||
qDebug() << "singleBoxPressed"; | ||||
} | ||||
void MainWidget::singleBoxReleased() | ||||
{ | ||||
qDebug() << "singleBoxReleased"; | ||||
} | ||||
void MainWidget::singleBoxDoubleClicked() | ||||
{ | ||||
qDebug() << "singleBoxDoubleClicked"; | ||||
} | ||||
void MainWidget::singleBoxHovered(bool state) | ||||
{ | ||||
if (state) | ||||
qDebug() << "single box hover started"; | ||||
else | ||||
qDebug() << "single box hover ended"; | ||||
} | ||||
void MainWidget::changePen() | ||||
{ | ||||
qDebug() << "changePen() = " << m_penTool->pen(); | ||||
for (int i = 0; i < m_seriesCount; i++) | ||||
m_series[i]->setPen(m_penTool->pen()); | ||||
} | ||||
void MainWidget::setBoxWidth(double width) | ||||
{ | ||||
qDebug() << "setBoxWidth to " << width; | ||||
for (int i = 0; i < m_seriesCount; i++) | ||||
m_series[i]->setBoxWidth(qreal(width)); | ||||
} | ||||