|
|
/****************************************************************************
|
|
|
**
|
|
|
** Copyright (C) 2015 The Qt Company Ltd
|
|
|
** All rights reserved.
|
|
|
** For any questions to The Qt Company, please use contact form at http://qt.io
|
|
|
**
|
|
|
** This file is part of the Qt Charts module.
|
|
|
**
|
|
|
** Licensees holding valid commercial license for Qt may use this file in
|
|
|
** accordance with the Qt License Agreement provided with the Software
|
|
|
** or, alternatively, in accordance with the terms contained in a written
|
|
|
** agreement between you and The Qt Company.
|
|
|
**
|
|
|
** If you have questions regarding the use of this file, please use
|
|
|
** contact form at http://qt.io
|
|
|
**
|
|
|
****************************************************************************/
|
|
|
|
|
|
#include "mainwindow.h"
|
|
|
#include "objectinspectorwidget.h"
|
|
|
#include "penwidget.h"
|
|
|
#include "brushwidget.h"
|
|
|
#include "engine.h"
|
|
|
#include <QtCore/QSettings>
|
|
|
#include <QtCharts/QChartView>
|
|
|
#include <QtWidgets/QDockWidget>
|
|
|
#include <QtWidgets/QMenuBar>
|
|
|
#include <QtWidgets/QTableWidget>
|
|
|
#include <QtWidgets/QSplitter>
|
|
|
#include <QtCore/QMetaEnum>
|
|
|
#include <QtGui/QStandardItemModel>
|
|
|
#include <QtWidgets/QHeaderView>
|
|
|
#include <QtCore/QDebug>
|
|
|
#include <QtWidgets/QMessageBox>
|
|
|
|
|
|
static const QString FILENAME_SETTING("filename");
|
|
|
static const QString GEOMETRY_SETTING("geometry");
|
|
|
|
|
|
MainWindow::MainWindow() :
|
|
|
m_engine(new Engine(this)),
|
|
|
m_view(new QChartView(m_engine->chart())),
|
|
|
m_scene(m_view->scene()),
|
|
|
m_table(new QTableView()),
|
|
|
m_addSeriesMenu(0),
|
|
|
m_seriesMenu(0),
|
|
|
m_chartMenu(0),
|
|
|
m_themeMenu(0),
|
|
|
m_animationMenu(0),
|
|
|
m_zoomMenu(0),
|
|
|
m_removeAllAction(0),
|
|
|
m_legendAction(0),
|
|
|
m_antialiasingAction(0)
|
|
|
{
|
|
|
createMenus();
|
|
|
createDockWidgets();
|
|
|
createTable();
|
|
|
createLayout();
|
|
|
|
|
|
QSettings settings;
|
|
|
restoreGeometry(settings.value(GEOMETRY_SETTING).toByteArray());
|
|
|
m_filename = settings.value(FILENAME_SETTING).toString();
|
|
|
if (m_filename.isEmpty())
|
|
|
m_filename = "untitled";
|
|
|
|
|
|
setWindowTitle(m_filename);
|
|
|
updateUI();
|
|
|
}
|
|
|
|
|
|
MainWindow::~MainWindow()
|
|
|
{
|
|
|
delete m_engine;
|
|
|
}
|
|
|
|
|
|
void MainWindow::createMenus()
|
|
|
{
|
|
|
QMenu *file = menuBar()->addMenu(tr("File"));
|
|
|
QMenu *edit = menuBar()->addMenu(tr("Edit"));
|
|
|
m_seriesMenu = menuBar()->addMenu(tr("Series"));
|
|
|
m_chartMenu = menuBar()->addMenu(tr("Chart"));
|
|
|
|
|
|
m_addSeriesMenu = new QMenu(tr("Add series"));
|
|
|
m_themeMenu = new QMenu(tr("Apply theme"));
|
|
|
m_animationMenu = new QMenu(tr("Animations"));
|
|
|
m_zoomMenu = new QMenu(tr("Zoom"));
|
|
|
|
|
|
file->addAction(tr("New"), this, SLOT(handleNewAction()));
|
|
|
file->addAction(tr("Load"), this, SLOT(handleLoadAction()));
|
|
|
file->addAction(tr("Save"), this, SLOT(handleSaveAction()));
|
|
|
file->addAction(tr("Save As"), this, SLOT(handleSaveAsAction()));
|
|
|
|
|
|
//seriesMenu
|
|
|
m_seriesMenu->addMenu(m_addSeriesMenu);
|
|
|
m_removeAllAction = new QAction(tr("Remove all series"), this);
|
|
|
QObject::connect(m_removeAllAction, SIGNAL(triggered()), this, SLOT(handleRemoveAllSeriesAction()));
|
|
|
m_seriesMenu->addAction(m_removeAllAction);
|
|
|
m_seriesMenu->addSeparator();
|
|
|
|
|
|
//seriesMenu /addSeriesMenu
|
|
|
{
|
|
|
int index = QAbstractSeries::staticMetaObject.indexOfEnumerator("SeriesType");
|
|
|
QMetaEnum metaEnum = QAbstractSeries::staticMetaObject.enumerator(index);
|
|
|
|
|
|
int count = metaEnum.keyCount();
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
QAction* action = new QAction(metaEnum.key(i), this);
|
|
|
action->setData(metaEnum.value(i));
|
|
|
m_addSeriesMenu->addAction(action);
|
|
|
QObject::connect(action, SIGNAL(triggered()), this, SLOT(handleAddSeriesMenu()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//chartMenu / themeMenu
|
|
|
{
|
|
|
m_chartMenu->addMenu(m_themeMenu);
|
|
|
int index = QChart::staticMetaObject.indexOfEnumerator("ChartTheme");
|
|
|
QMetaEnum metaEnum = QChart::staticMetaObject.enumerator(index);
|
|
|
|
|
|
int count = metaEnum.keyCount();
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
QAction* action = new QAction(metaEnum.key(i), this);
|
|
|
action->setData(metaEnum.value(i));
|
|
|
action->setCheckable(true);
|
|
|
m_themeMenu->addAction(action);
|
|
|
QObject::connect(action, SIGNAL(triggered()), this, SLOT(handleThemeMenu()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//chartMenu / animationMenu
|
|
|
{
|
|
|
m_chartMenu->addMenu(m_animationMenu);
|
|
|
int index = QChart::staticMetaObject.indexOfEnumerator("AnimationOption");
|
|
|
QMetaEnum metaEnum = QChart::staticMetaObject.enumerator(index);
|
|
|
|
|
|
int count = metaEnum.keyCount();
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
QAction* action = new QAction(metaEnum.key(i), this);
|
|
|
action->setData(metaEnum.value(i));
|
|
|
action->setCheckable(true);
|
|
|
m_animationMenu->addAction(action);
|
|
|
QObject::connect(action, SIGNAL(triggered()), this, SLOT(handleAnimationMenu()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//chartMenu / zoomMenu
|
|
|
{
|
|
|
m_chartMenu->addMenu(m_zoomMenu);
|
|
|
int index = QChartView::staticMetaObject.indexOfEnumerator("RubberBand");
|
|
|
QMetaEnum metaEnum = QChartView::staticMetaObject.enumerator(index);
|
|
|
|
|
|
int count = metaEnum.keyCount();
|
|
|
|
|
|
for (int i = 0; i < count; ++i) {
|
|
|
QAction* action = new QAction(metaEnum.key(i), this);
|
|
|
action->setData(metaEnum.value(i));
|
|
|
action->setCheckable(true);
|
|
|
m_zoomMenu->addAction(action);
|
|
|
QObject::connect(action, SIGNAL(triggered()), this, SLOT(handleZoomMenu()));
|
|
|
}
|
|
|
}
|
|
|
|
|
|
//chartMenu / legend
|
|
|
m_legendAction = new QAction(tr("Legend"), this);
|
|
|
m_legendAction->setCheckable(true);
|
|
|
m_chartMenu->addAction(m_legendAction);
|
|
|
QObject::connect(m_legendAction, SIGNAL(triggered()), this, SLOT(handleLegendAction()));
|
|
|
|
|
|
//chartMenu / Anti-aliasing
|
|
|
m_antialiasingAction = new QAction(tr("Anti-aliasing"), this);
|
|
|
m_antialiasingAction->setCheckable(true);
|
|
|
m_chartMenu->addAction(m_antialiasingAction);
|
|
|
QObject::connect(m_antialiasingAction, SIGNAL(triggered()), this, SLOT(handleAntialiasingAction()));
|
|
|
|
|
|
}
|
|
|
|
|
|
void MainWindow::createDockWidgets()
|
|
|
{
|
|
|
m_brushWidget = new BrushWidget();
|
|
|
QDockWidget *brushDockWidget = new QDockWidget(tr("Brush"), this);
|
|
|
brushDockWidget->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
|
|
brushDockWidget->setWidget(m_brushWidget);
|
|
|
addDockWidget(Qt::RightDockWidgetArea, brushDockWidget);
|
|
|
|
|
|
m_penWidget = new PenWidget();
|
|
|
QDockWidget *penDockWidget = new QDockWidget(tr("Pen"), this);
|
|
|
penDockWidget->setFeatures(QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
|
|
penDockWidget->setWidget(m_penWidget);
|
|
|
addDockWidget(Qt::RightDockWidgetArea, penDockWidget);
|
|
|
|
|
|
m_inspectorWidget = new InspectorWidget();
|
|
|
QDockWidget *inspectorDockWidget = new QDockWidget(tr("Object Inspector"), this);
|
|
|
inspectorDockWidget->setFeatures(
|
|
|
QDockWidget::DockWidgetMovable | QDockWidget::DockWidgetFloatable);
|
|
|
inspectorDockWidget->setWidget(m_inspectorWidget);
|
|
|
addDockWidget(Qt::RightDockWidgetArea, inspectorDockWidget);
|
|
|
|
|
|
setDockOptions(QMainWindow::AnimatedDocks);
|
|
|
}
|
|
|
|
|
|
void MainWindow::createLayout()
|
|
|
{
|
|
|
m_table->horizontalHeader()->setSectionResizeMode(QHeaderView::Stretch);
|
|
|
|
|
|
int rowHeight = m_table->rowHeight(0);
|
|
|
int tableHeight = (m_engine->modelCount() * rowHeight) + m_table->horizontalHeader()->height() + 2 * m_table->frameWidth();
|
|
|
|
|
|
m_table->setMinimumHeight(tableHeight);
|
|
|
m_table->setMaximumHeight(tableHeight);
|
|
|
|
|
|
QSplitter *splitter = new QSplitter(this);
|
|
|
splitter->setOrientation(Qt::Vertical);
|
|
|
splitter->addWidget(m_table);
|
|
|
splitter->addWidget(m_view);
|
|
|
setCentralWidget(splitter);
|
|
|
m_view->hide();
|
|
|
}
|
|
|
|
|
|
void MainWindow::createTable()
|
|
|
{
|
|
|
m_table->setModel(m_engine->model());
|
|
|
m_table->setSelectionModel(m_engine->selectionModel());
|
|
|
QObject::connect(m_table->selectionModel(),SIGNAL(selectionChanged(QItemSelection,QItemSelection)), this,SLOT(updateUI()));
|
|
|
}
|
|
|
|
|
|
void MainWindow::updateUI()
|
|
|
{
|
|
|
|
|
|
QItemSelectionModel* selection = m_table->selectionModel();
|
|
|
const QModelIndexList& list = selection->selectedIndexes();
|
|
|
|
|
|
QMap<int, QModelIndex> columns;
|
|
|
|
|
|
foreach (const QModelIndex& index, list) {
|
|
|
columns.insertMulti(index.column(), index);
|
|
|
}
|
|
|
|
|
|
QList<int> keys = columns.uniqueKeys();
|
|
|
|
|
|
bool seriesEnabled = false;
|
|
|
|
|
|
foreach (QAction* action, m_addSeriesMenu->actions()) {
|
|
|
switch (action->data().toInt()) {
|
|
|
case QAbstractSeries::SeriesTypeLine:
|
|
|
case QAbstractSeries::SeriesTypeSpline:
|
|
|
case QAbstractSeries::SeriesTypeScatter:
|
|
|
action->setEnabled(list.count() > 0 && keys.count() >= 2);
|
|
|
seriesEnabled |= action->isEnabled();
|
|
|
break;
|
|
|
case QAbstractSeries::SeriesTypeBar:
|
|
|
case QAbstractSeries::SeriesTypePercentBar:
|
|
|
case QAbstractSeries::SeriesTypeStackedBar:
|
|
|
action->setEnabled(list.count() > 0 && keys.count() >= 2);
|
|
|
seriesEnabled |= action->isEnabled();
|
|
|
break;
|
|
|
case QAbstractSeries::SeriesTypePie:
|
|
|
action->setEnabled(list.count() > 0 && keys.count() == 2);
|
|
|
seriesEnabled |= action->isEnabled();
|
|
|
break;
|
|
|
case QAbstractSeries::SeriesTypeArea:
|
|
|
action->setEnabled(list.count() > 0 && keys.count() == 3);
|
|
|
seriesEnabled |= action->isEnabled();
|
|
|
break;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
m_chartMenu->setEnabled(m_engine->chart()->series().count() > 0);
|
|
|
m_seriesMenu->setEnabled(seriesEnabled || m_engine->chart()->series().count() > 0);
|
|
|
m_removeAllAction->setEnabled(m_engine->chart()->series().count() > 0);
|
|
|
|
|
|
int theme = m_engine->chart()->theme();
|
|
|
foreach (QAction* action, m_themeMenu->actions()) {
|
|
|
action->setChecked(action->data().toInt() == theme);
|
|
|
}
|
|
|
|
|
|
int animation = m_engine->chart()->animationOptions();
|
|
|
foreach (QAction* action, m_animationMenu->actions()) {
|
|
|
action->setChecked(action->data().toInt() == animation);
|
|
|
}
|
|
|
|
|
|
int zoom = m_view->rubberBand();
|
|
|
foreach (QAction* action, m_zoomMenu->actions()) {
|
|
|
action->setChecked(action->data().toInt() == zoom);
|
|
|
}
|
|
|
|
|
|
m_legendAction->setChecked(m_engine->chart()->legend()->isVisible());
|
|
|
m_antialiasingAction->setChecked(m_view->renderHints().testFlag(QPainter::Antialiasing));
|
|
|
|
|
|
foreach (QAction *action, m_seriesMenu->actions()) {
|
|
|
//TODO: visibility handling
|
|
|
//if (m_series.value(action->text()))
|
|
|
// ;
|
|
|
//action->setChecked(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWindow::closeEvent(QCloseEvent *event)
|
|
|
{
|
|
|
QSettings settings;
|
|
|
settings.setValue(GEOMETRY_SETTING, saveGeometry());
|
|
|
settings.setValue(FILENAME_SETTING, m_filename);
|
|
|
QMainWindow::closeEvent(event);
|
|
|
}
|
|
|
|
|
|
//handlers
|
|
|
|
|
|
void MainWindow::handleNewAction()
|
|
|
{
|
|
|
m_engine->chart()->removeAllSeries();
|
|
|
m_view->hide();
|
|
|
m_engine->clearModels();
|
|
|
createTable();
|
|
|
m_filename = "untitled";
|
|
|
setWindowTitle(m_filename);
|
|
|
updateUI();
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleAddSeriesMenu()
|
|
|
{
|
|
|
m_view->show();
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
QList<QAbstractSeries*> series = m_engine->addSeries(QAbstractSeries::SeriesType(action->data().toInt()));
|
|
|
|
|
|
foreach (QAbstractSeries* s , series)
|
|
|
{
|
|
|
QAction *newAction = new QAction(s->name(),this);
|
|
|
//newAction->setCheckable(true);
|
|
|
m_series.insert(s->name(),s);
|
|
|
m_seriesMenu->addAction(newAction);
|
|
|
}
|
|
|
|
|
|
updateUI();
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleRemoveAllSeriesAction()
|
|
|
{
|
|
|
|
|
|
foreach (QAction* action, m_seriesMenu->actions()){
|
|
|
if(m_series.contains(action->text())){
|
|
|
m_seriesMenu->removeAction(action);
|
|
|
m_engine->removeSeries(m_series.value(action->text()));
|
|
|
delete action;
|
|
|
}
|
|
|
}
|
|
|
|
|
|
m_series.clear();
|
|
|
|
|
|
m_view->hide();
|
|
|
updateUI();
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleThemeMenu()
|
|
|
{
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
m_engine->chart()->setTheme(QChart::ChartTheme(action->data().toInt()));
|
|
|
updateUI();
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleAnimationMenu()
|
|
|
{
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
m_engine->chart()->setAnimationOptions(QChart::AnimationOption(action->data().toInt()));
|
|
|
updateUI();
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleZoomMenu()
|
|
|
{
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
m_view->setRubberBand(QChartView::RubberBand(action->data().toInt()));
|
|
|
updateUI();
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleAntialiasingAction()
|
|
|
{
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
if (action->isChecked()) {
|
|
|
m_view->setRenderHint(QPainter::Antialiasing, true);
|
|
|
}
|
|
|
else {
|
|
|
m_view->setRenderHint(QPainter::Antialiasing, false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleLegendAction()
|
|
|
{
|
|
|
QAction* action = qobject_cast<QAction*>(sender());
|
|
|
if (action->isChecked()) {
|
|
|
m_engine->chart()->legend()->setVisible(true);
|
|
|
}
|
|
|
else {
|
|
|
m_engine->chart()->legend()->setVisible(false);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleSaveAction()
|
|
|
{
|
|
|
if(!m_engine->save(m_filename)) {
|
|
|
|
|
|
QScopedPointer<QMessageBox> messageBox(new QMessageBox(this));
|
|
|
messageBox->setIcon(QMessageBox::Warning);
|
|
|
messageBox->setWindowModality(Qt::WindowModal);
|
|
|
messageBox->setWindowTitle(QString(tr("Error")));
|
|
|
messageBox->setText(tr("Could not write to ") + m_filename);
|
|
|
messageBox->exec();
|
|
|
}
|
|
|
}
|
|
|
|
|
|
void MainWindow::handleLoadAction()
|
|
|
{
|
|
|
if(!m_engine->load(m_filename)) {
|
|
|
|
|
|
QScopedPointer<QMessageBox> messageBox(new QMessageBox(this));
|
|
|
messageBox->setIcon(QMessageBox::Warning);
|
|
|
messageBox->setWindowModality(Qt::WindowModal);
|
|
|
messageBox->setWindowTitle(QString(tr("Error")));
|
|
|
messageBox->setText(tr("Could not open ") + m_filename);
|
|
|
messageBox->exec();
|
|
|
|
|
|
}else createTable();
|
|
|
}
|
|
|
|