diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -134,6 +134,10 @@ add_executable(${PROGRAM_NAME} WIN32 src/framedreader.cpp src/framedreadersettings.cpp src/plotmanager.cpp + src/plotmenu.cpp + src/barplot.cpp + src/barchart.cpp + src/barscaledraw.cpp src/numberformat.cpp src/updatechecker.cpp src/versionnumber.cpp diff --git a/src/barchart.cpp b/src/barchart.cpp new file mode 100644 --- /dev/null +++ b/src/barchart.cpp @@ -0,0 +1,97 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#include +#include + +#include "barchart.h" + +BarChart::BarChart(ChannelManager* channelMan) +{ + _channelMan = channelMan; + setSpacing(0); +} + +void BarChart::resample() +{ + setSamples(chartData()); +} + +QVector BarChart::chartData() const +{ + unsigned numChannels = _channelMan->numOfChannels(); + unsigned numOfSamples = _channelMan->numOfSamples(); + QVector data(numChannels); + for (unsigned i = 0; i < numChannels; i++) + { + data[i] = _channelMan->channelBuffer(i)->sample(numOfSamples-1); + } + return data; +} + +QwtColumnSymbol* BarChart::specialSymbol(int sampleIndex, const QPointF& sample) const +{ + unsigned numChannels = _channelMan->numOfChannels(); + if (sampleIndex < 0 || sampleIndex > (int) numChannels) + { + return NULL; + } + + auto info = _channelMan->infoModel(); + auto color = info->color(sampleIndex); + + QwtColumnSymbol* symbol = new QwtColumnSymbol(QwtColumnSymbol::Box); + symbol->setLineWidth(1); + symbol->setFrameStyle(QwtColumnSymbol::Plain); + symbol->setPalette(QPalette(color)); + + return symbol; +} + +void BarChart::drawSample( + QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, const QwtInterval &boundingInterval, + int index, const QPointF &sample ) const +{ + QwtPlotBarChart::drawSample(painter, xMap, yMap, canvasRect, boundingInterval, index, sample); + + // calculate bar size + const double barWidth = sampleWidth(xMap, canvasRect.width(), + boundingInterval.width(), sample.y()); + const double x = xMap.transform( sample.x() ); + const double x1 = x - 0.5 * barWidth; + // const double x2 = x + 0.5 * barWidth; + + const double y1 = yMap.transform(baseline()); + const double y2 = yMap.transform(sample.y()); + const double barHeight = fabs(y2 - y1); + + // create and calculate text size + const auto valueStr = QString::number(sample.y()); + auto valueText = QwtText(valueStr, QwtText::PlainText); + const auto textSize = valueText.textSize(); + auto r = QRectF(x1, y2, barWidth, textSize.height()); + if (y2 > y1) r.moveBottom(y2); + + // display text if there is enough space + if (barHeight >= textSize.height() && barWidth >= textSize.width()) + { + valueText.draw(painter, r); + } +} diff --git a/src/barchart.h b/src/barchart.h new file mode 100644 --- /dev/null +++ b/src/barchart.h @@ -0,0 +1,47 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#ifndef BARCHART_H +#define BARCHART_H + +#include +#include +#include "channelmanager.h" + +class BarChart : public QwtPlotBarChart +{ +public: + explicit BarChart(ChannelManager* channelMan); + + void resample(); + QwtColumnSymbol* specialSymbol(int sampleIndex, const QPointF&) const; + + void drawSample( + QPainter *painter, const QwtScaleMap &xMap, const QwtScaleMap &yMap, + const QRectF &canvasRect, const QwtInterval &boundingInterval, + int index, const QPointF &sample ) const; + +private: + ChannelManager* _channelMan; + + QVector chartData() const; +}; + + +#endif // BARCHART_H diff --git a/src/barplot.cpp b/src/barplot.cpp new file mode 100644 --- /dev/null +++ b/src/barplot.cpp @@ -0,0 +1,74 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#include "barplot.h" +#include "barscaledraw.h" +#include "utils.h" + +BarPlot::BarPlot(ChannelManager* channelMan, PlotMenu* menu, QWidget* parent) : + QwtPlot(parent), _menu(menu), barChart(channelMan) +{ + _channelMan = channelMan; + barChart.attach(this); + setAxisMaxMinor(QwtPlot::xBottom, 0); + setAxisScaleDraw(QwtPlot::xBottom, new BarScaleDraw(channelMan)); + + update(); + connect(_channelMan, &ChannelManager::dataAdded, this, &BarPlot::update); + connect(_channelMan, &ChannelManager::numOfChannelsChanged, this, &BarPlot::update); + + // connect to menu + connect(&menu->darkBackgroundAction, SELECT::OVERLOAD_OF(&QAction::toggled), + this, &BarPlot::darkBackground); + darkBackground(menu->darkBackgroundAction.isChecked()); +} + +void BarPlot::update() +{ + // Note: -0.99 is used instead of -1 to handle the case of `numOfChannels==1` + setAxisScale(QwtPlot::xBottom, 0, _channelMan->numOfChannels()-0.99, 1); + barChart.resample(); + replot(); +} + +void BarPlot::setYAxis(bool autoScaled, double yMin, double yMax) +{ + if (autoScaled) + { + setAxisAutoScale(QwtPlot::yLeft); + } + else + { + setAxisScale(QwtPlot::yLeft, yMin, yMax); + } +} + + +void BarPlot::darkBackground(bool enabled) +{ + if (enabled) + { + setCanvasBackground(QBrush(Qt::black)); + } + else + { + setCanvasBackground(QBrush(Qt::white)); + } + replot(); +} diff --git a/src/barplot.h b/src/barplot.h new file mode 100644 --- /dev/null +++ b/src/barplot.h @@ -0,0 +1,55 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#ifndef BARPLOT_H +#define BARPLOT_H + +#include + +#include "channelmanager.h" +#include "plotmenu.h" +#include "barchart.h" + +class BarPlot : public QwtPlot +{ + Q_OBJECT + +public: + explicit BarPlot(ChannelManager* channelMan, + PlotMenu* menu, + QWidget* parent = 0); + +public slots: + /// Set the Y axis + void setYAxis(bool autoScaled, double yMin = 0, double yMax = 1); + /// Enable/disable dark background + void darkBackground(bool enabled); + +private: + ChannelManager* _channelMan; + PlotMenu* _menu; + BarChart barChart; + + QVector chartData() const; + +private slots: + void update(); +}; + +#endif // BARPLOT_H diff --git a/src/barscaledraw.cpp b/src/barscaledraw.cpp new file mode 100644 --- /dev/null +++ b/src/barscaledraw.cpp @@ -0,0 +1,51 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#include "barscaledraw.h" + +#include + +BarScaleDraw::BarScaleDraw(ChannelManager* channelMan) +{ + _channelMan = channelMan; + enableComponent(Backbone, false); + setLabelRotation(-90); + setLabelAlignment(Qt::AlignLeft | Qt::AlignVCenter); + + QObject::connect(_channelMan, &ChannelManager::channelNameChanged, + [this]() + { + invalidateCache(); + }); +} + +QwtText BarScaleDraw::label(double value) const +{ + int index = value; + unsigned numChannels = _channelMan->numOfChannels(); + + if (index >=0 && index < (int) numChannels) + { + return _channelMan->channelName(index); + } + else + { + return QString(""); + } +} diff --git a/src/barscaledraw.h b/src/barscaledraw.h new file mode 100644 --- /dev/null +++ b/src/barscaledraw.h @@ -0,0 +1,39 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#ifndef BARSCALEDRAW_H +#define BARSCALEDRAW_H + +#include +#include +#include + +#include "channelmanager.h" + +class BarScaleDraw : public QwtScaleDraw +{ +public: + explicit BarScaleDraw(ChannelManager* channelMan); + QwtText label(double value) const; + +private: + ChannelManager* _channelMan; +}; + +#endif // BARSCALEDRAW_H diff --git a/src/mainwindow.cpp b/src/mainwindow.cpp --- a/src/mainwindow.cpp +++ b/src/mainwindow.cpp @@ -35,6 +35,7 @@ #include #include +#include #include "framebufferseries.h" #include "utils.h" @@ -60,6 +61,7 @@ MainWindow::MainWindow(QWidget *parent) aboutDialog(this), portControl(&serialPort), channelMan(1, 1, this), + secondaryPlot(NULL), snapshotMan(this, &channelMan), commandPanel(&serialPort), dataFormatPanel(&serialPort, &channelMan, &recorder), @@ -68,7 +70,7 @@ MainWindow::MainWindow(QWidget *parent) { ui->setupUi(this); - plotMan = new PlotManager(ui->plotArea, channelMan.infoModel()); + plotMan = new PlotManager(ui->plotArea, &plotMenu, channelMan.infoModel()); ui->tabWidget->insertTab(0, &portControl, "Port"); ui->tabWidget->insertTab(1, &dataFormatPanel, "Data Format"); @@ -81,8 +83,8 @@ MainWindow::MainWindow(QWidget *parent) addToolBar(recordPanel.toolbar()); ui->plotToolBar->addAction(snapshotMan.takeSnapshotAction()); - ui->menuBar->insertMenu(ui->menuHelp->menuAction(), snapshotMan.menu()); - ui->menuBar->insertMenu(ui->menuHelp->menuAction(), commandPanel.menu()); + menuBar()->insertMenu(ui->menuHelp->menuAction(), snapshotMan.menu()); + menuBar()->insertMenu(ui->menuHelp->menuAction(), commandPanel.menu()); connect(&commandPanel, &CommandPanel::focusRequested, [this]() { @@ -96,19 +98,35 @@ MainWindow::MainWindow(QWidget *parent) setupAboutDialog(); // init view menu - for (auto a : plotMan->menuActions()) - { - ui->menuView->addAction(a); - } - - ui->menuView->addSeparator(); - - QMenu* tbMenu = ui->menuView->addMenu("Toolbars"); + ui->menuBar->insertMenu(ui->menuSecondary->menuAction(), &plotMenu); + plotMenu.addSeparator(); + QMenu* tbMenu = plotMenu.addMenu("Toolbars"); tbMenu->addAction(ui->plotToolBar->toggleViewAction()); tbMenu->addAction(portControl.toolBar()->toggleViewAction()); + // init secondary plot menu + auto group = new QActionGroup(this); + group->addAction(ui->actionVertical); + group->addAction(ui->actionHorizontal); + // init UI signals + // Secondary plot menu signals + connect(ui->actionBarPlot, &QAction::triggered, + this, &MainWindow::showBarPlot); + + connect(ui->actionVertical, &QAction::triggered, + [this](bool checked) + { + if (checked) ui->splitter->setOrientation(Qt::Vertical); + }); + + connect(ui->actionHorizontal, &QAction::triggered, + [this](bool checked) + { + if (checked) ui->splitter->setOrientation(Qt::Horizontal); + }); + // Help menu signals QObject::connect(ui->actionHelpAbout, &QAction::triggered, &aboutDialog, &QWidget::show); @@ -154,6 +172,7 @@ MainWindow::MainWindow(QWidget *parent) connect(&plotControlPanel, &PlotControlPanel::plotWidthChanged, plotMan, &PlotManager::setPlotWidth); + // plot toolbar signals QObject::connect(ui->actionClear, SIGNAL(triggered(bool)), this, SLOT(clearPlot())); @@ -478,6 +497,48 @@ void MainWindow::enableDemo(bool enabled } } +void MainWindow::showSecondary(QWidget* wid) +{ + if (secondaryPlot != NULL) + { + secondaryPlot->deleteLater(); + } + + secondaryPlot = wid; + ui->splitter->addWidget(wid); + ui->splitter->setStretchFactor(0, 1); + ui->splitter->setStretchFactor(1, 0); +} + +void MainWindow::hideSecondary() +{ + if (secondaryPlot == NULL) + { + qFatal("Secondary plot doesn't exist!"); + } + + secondaryPlot->deleteLater(); + secondaryPlot = NULL; +} + +void MainWindow::showBarPlot(bool show) +{ + if (show) + { + auto plot = new BarPlot(&channelMan, &plotMenu); + plot->setYAxis(plotControlPanel.autoScale(), + plotControlPanel.yMin(), + plotControlPanel.yMax()); + connect(&plotControlPanel, &PlotControlPanel::yScaleChanged, + plot, &BarPlot::setYAxis); + showSecondary(plot); + } + else + { + hideSecondary(); + } +} + void MainWindow::onExportCsv() { bool wasPaused = ui->actionPause->isChecked(); @@ -499,7 +560,7 @@ void MainWindow::onExportCsv() PlotViewSettings MainWindow::viewSettings() const { - return plotMan->viewSettings(); + return plotMenu.viewSettings(); } void MainWindow::messageHandler(QtMsgType type, @@ -550,7 +611,7 @@ void MainWindow::saveAllSettings(QSettin dataFormatPanel.saveSettings(settings); channelMan.saveSettings(settings); plotControlPanel.saveSettings(settings); - plotMan->saveSettings(settings); + plotMenu.saveSettings(settings); commandPanel.saveSettings(settings); recordPanel.saveSettings(settings); updateCheckDialog.saveSettings(settings); @@ -563,7 +624,7 @@ void MainWindow::loadAllSettings(QSettin dataFormatPanel.loadSettings(settings); channelMan.loadSettings(settings); plotControlPanel.loadSettings(settings); - plotMan->loadSettings(settings); + plotMenu.loadSettings(settings); commandPanel.loadSettings(settings); recordPanel.loadSettings(settings); updateCheckDialog.loadSettings(settings); diff --git a/src/mainwindow.h b/src/mainwindow.h --- a/src/mainwindow.h +++ b/src/mainwindow.h @@ -44,6 +44,7 @@ #include "channelmanager.h" #include "snapshotmanager.h" #include "plotmanager.h" +#include "plotmenu.h" #include "datarecorder.h" #include "updatecheckdialog.h" @@ -78,6 +79,7 @@ private: QList curves; ChannelManager channelMan; PlotManager* plotMan; + QWidget* secondaryPlot; SnapshotManager snapshotMan; DataRecorder recorder; // operated by `recordPanel` @@ -86,9 +88,16 @@ private: DataFormatPanel dataFormatPanel; RecordPanel recordPanel; PlotControlPanel plotControlPanel; + PlotMenu plotMenu; UpdateCheckDialog updateCheckDialog; + /// Returns true if demo is running bool isDemoRunning(); + /// Display a secondary plot in the splitter, removing and + /// deleting previous one if it exists + void showSecondary(QWidget* wid); + /// Hide secondary plot + void hideSecondary(); /// Stores settings for all modules void saveAllSettings(QSettings* settings); /// Load settings for all modules @@ -111,6 +120,8 @@ private slots: void clearPlot(); void onSpsChanged(unsigned sps); void enableDemo(bool enabled); + void showBarPlot(bool show); + void onExportCsv(); void onSaveSettings(); void onLoadSettings(); diff --git a/src/mainwindow.ui b/src/mainwindow.ui --- a/src/mainwindow.ui +++ b/src/mainwindow.ui @@ -14,15 +14,26 @@ SerialPlot - + - + - + 0 0 + + Qt::Horizontal + + + + + 0 + 0 + + + @@ -90,7 +101,7 @@ 0 0 653 - 25 + 24 @@ -112,13 +123,17 @@ - + - &View + Secondary + + + + - + @@ -144,8 +159,7 @@ - - + .. Pause @@ -160,8 +174,7 @@ - - + .. Clear @@ -231,6 +244,33 @@ &Check Update + + + true + + + Bar Plot + + + + + true + + + Vertical + + + + + true + + + true + + + Horizontal + + diff --git a/src/plotmanager.cpp b/src/plotmanager.cpp --- a/src/plotmanager.cpp +++ b/src/plotmanager.cpp @@ -18,7 +18,6 @@ */ #include -#include #include #include #include "qwt_symbol.h" @@ -28,17 +27,12 @@ #include "utils.h" #include "setting_defines.h" -PlotManager::PlotManager(QWidget* plotArea, ChannelInfoModel* infoModel, QObject *parent) : - QObject(parent), - _plotArea(plotArea), - showGridAction("&Grid", this), - showMinorGridAction("&Minor Grid", this), - unzoomAction("&Unzoom", this), - darkBackgroundAction("&Dark Background", this), - showLegendAction("&Legend", this), - showMultiAction("Multi &Plot", this), - setSymbolsAction("Symbols", this) +PlotManager::PlotManager(QWidget* plotArea, PlotMenu* menu, + ChannelInfoModel* infoModel, QObject* parent) : + QObject(parent) { + _menu = menu; + _plotArea = plotArea; _autoScaled = true; _yMin = 0; _yMax = 1; @@ -56,78 +50,28 @@ PlotManager::PlotManager(QWidget* plotAr setupLayout(isMulti); addPlotWidget(); - // initialize menu actions - showGridAction.setToolTip("Show Grid"); - showMinorGridAction.setToolTip("Show Minor Grid"); - unzoomAction.setToolTip("Unzoom the Plot"); - darkBackgroundAction.setToolTip("Enable Dark Plot Background"); - showLegendAction.setToolTip("Display the Legend on Plot"); - showMultiAction.setToolTip("Display All Channels Separately"); - setSymbolsAction.setToolTip("Show/Hide symbols"); - - showGridAction.setShortcut(QKeySequence("G")); - showMinorGridAction.setShortcut(QKeySequence("M")); - - showGridAction.setCheckable(true); - showMinorGridAction.setCheckable(true); - darkBackgroundAction.setCheckable(true); - showLegendAction.setCheckable(true); - showMultiAction.setCheckable(true); - - showGridAction.setChecked(false); - showMinorGridAction.setChecked(false); - darkBackgroundAction.setChecked(false); - showLegendAction.setChecked(true); - showMultiAction.setChecked(false); - - showMinorGridAction.setEnabled(false); + // connect to menu + connect(menu, &PlotMenu::symbolShowChanged, this, &PlotManager:: setSymbols); - // setup symbols menu - setSymbolsAutoAct = setSymbolsMenu.addAction("Show When Zoomed"); - setSymbolsAutoAct->setCheckable(true); - setSymbolsAutoAct->setChecked(true); - connect(setSymbolsAutoAct, SELECT::OVERLOAD_OF(&QAction::triggered), - [this](bool checked) - { - if (checked) setSymbols(Plot::ShowSymbolsAuto); - }); - setSymbolsShowAct = setSymbolsMenu.addAction("Always Show"); - setSymbolsShowAct->setCheckable(true); - connect(setSymbolsShowAct, SELECT::OVERLOAD_OF(&QAction::triggered), - [this](bool checked) - { - if (checked) setSymbols(Plot::ShowSymbolsShow); - }); - setSymbolsHideAct = setSymbolsMenu.addAction("Always Hide"); - setSymbolsHideAct->setCheckable(true); - connect(setSymbolsHideAct, SELECT::OVERLOAD_OF(&QAction::triggered), - [this](bool checked) - { - if (checked) setSymbols(Plot::ShowSymbolsHide); - }); - setSymbolsAction.setMenu(&setSymbolsMenu); + connect(&menu->showGridAction, SELECT::OVERLOAD_OF(&QAction::toggled), + this, &PlotManager::showGrid); + connect(&menu->showMinorGridAction, SELECT::OVERLOAD_OF(&QAction::toggled), + this, &PlotManager::showMinorGrid); + connect(&menu->darkBackgroundAction, SELECT::OVERLOAD_OF(&QAction::toggled), + this, &PlotManager::darkBackground); + connect(&menu->showLegendAction, SELECT::OVERLOAD_OF(&QAction::toggled), + this, &PlotManager::showLegend); + connect(&menu->showMultiAction, SELECT::OVERLOAD_OF(&QAction::toggled), + this, &PlotManager::setMulti); + connect(&menu->unzoomAction, &QAction::triggered, + this, &PlotManager::unzoom); - // add symbol actions to same group so that they appear as radio buttons - auto group = new QActionGroup(this); - group->addAction(setSymbolsAutoAct); - group->addAction(setSymbolsShowAct); - group->addAction(setSymbolsHideAct); - - connect(&showGridAction, SELECT::OVERLOAD_OF(&QAction::triggered), - this, &PlotManager::showGrid); - connect(&showGridAction, SELECT::OVERLOAD_OF(&QAction::triggered), - &showMinorGridAction, &QAction::setEnabled); - connect(&showMinorGridAction, SELECT::OVERLOAD_OF(&QAction::triggered), - this, &PlotManager::showMinorGrid); - connect(&unzoomAction, &QAction::triggered, this, &PlotManager::unzoom); - connect(&darkBackgroundAction, SELECT::OVERLOAD_OF(&QAction::triggered), - this, &PlotManager::darkBackground); - connect(&showLegendAction, SELECT::OVERLOAD_OF(&QAction::triggered), - this, &PlotManager::showLegend); - connect(&showLegendAction, SELECT::OVERLOAD_OF(&QAction::triggered), - this, &PlotManager::showLegend); - connect(&showMultiAction, SELECT::OVERLOAD_OF(&QAction::triggered), - this, &PlotManager::setMulti); + // initial settings from menu actions + showGrid(menu->showGridAction.isChecked()); + showMinorGrid(menu->showMinorGridAction.isChecked()); + darkBackground(menu->darkBackgroundAction.isChecked()); + showLegend(menu->showLegendAction.isChecked()); + setMulti(menu->showMultiAction.isChecked()); // connect to channel info model if (_infoModel != NULL) // TODO: remove when snapshots have infomodel @@ -308,14 +252,15 @@ Plot* PlotManager::addPlotWidget() plotWidgets.append(plot); layout->addWidget(plot); - plot->darkBackground(darkBackgroundAction.isChecked()); - plot->showGrid(showGridAction.isChecked()); - plot->showMinorGrid(showMinorGridAction.isChecked()); - plot->showLegend(showLegendAction.isChecked()); + plot->darkBackground(_menu->darkBackgroundAction.isChecked()); + plot->showGrid(_menu->showGridAction.isChecked()); + plot->showMinorGrid(_menu->showMinorGridAction.isChecked()); + plot->showLegend(_menu->showLegendAction.isChecked()); + plot->setSymbols(_menu->showSymbols()); + plot->showDemoIndicator(isDemoShown); plot->setYAxis(_autoScaled, _yMin, _yMax); plot->setNumOfSamples(_numOfSamples); - plot->setSymbols(showSymbols); plot->setPlotWidth(_plotWidth); if (_xAxisAsIndex) @@ -419,19 +364,6 @@ void PlotManager::replot() } } -QList PlotManager::menuActions() -{ - QList actions; - actions << &showGridAction; - actions << &showMinorGridAction; - actions << &unzoomAction; - actions << &darkBackgroundAction; - actions << &showLegendAction; - actions << &showMultiAction; - actions << &setSymbolsAction; - return actions; -} - void PlotManager::showGrid(bool show) { for (auto plot : plotWidgets) @@ -530,7 +462,7 @@ void PlotManager::flashSnapshotOverlay() { for (auto plot : plotWidgets) { - plot->flashSnapshotOverlay(darkBackgroundAction.isChecked()); + plot->flashSnapshotOverlay(_menu->darkBackgroundAction.isChecked()); } } @@ -552,115 +484,3 @@ void PlotManager::setPlotWidth(double wi plot->setPlotWidth(width); } } - -PlotViewSettings PlotManager::viewSettings() const -{ - return PlotViewSettings( - { - showGridAction.isChecked(), - showMinorGridAction.isChecked(), - darkBackgroundAction.isChecked(), - showLegendAction.isChecked(), - showMultiAction.isChecked(), - showSymbols - }); -} - -void PlotManager::setViewSettings(const PlotViewSettings& settings) -{ - showGridAction.setChecked(settings.showGrid); - showGrid(settings.showGrid); - showMinorGridAction.setChecked(settings.showMinorGrid); - showMinorGrid(settings.showMinorGrid); - darkBackgroundAction.setChecked(settings.darkBackground); - darkBackground(settings.darkBackground); - showLegendAction.setChecked(settings.showLegend); - showLegend(settings.showLegend); - showMultiAction.setChecked(settings.showMulti); - setMulti(settings.showMulti); - - setSymbols(settings.showSymbols); - if (showSymbols == Plot::ShowSymbolsAuto) - { - setSymbolsAutoAct->setChecked(true); - } - else if (showSymbols == Plot::ShowSymbolsShow) - { - setSymbolsShowAct->setChecked(true); - } - else - { - setSymbolsHideAct->setChecked(true); - } -} - -void PlotManager::saveSettings(QSettings* settings) -{ - settings->beginGroup(SettingGroup_Plot); - settings->setValue(SG_Plot_DarkBackground, darkBackgroundAction.isChecked()); - settings->setValue(SG_Plot_Grid, showGridAction.isChecked()); - settings->setValue(SG_Plot_MinorGrid, showMinorGridAction.isChecked()); - settings->setValue(SG_Plot_Legend, showLegendAction.isChecked()); - settings->setValue(SG_Plot_MultiPlot, showMultiAction.isChecked()); - - QString showSymbolsStr; - if (showSymbols == Plot::ShowSymbolsAuto) - { - showSymbolsStr = "auto"; - } - else if (showSymbols == Plot::ShowSymbolsShow) - { - showSymbolsStr = "show"; - } - else - { - showSymbolsStr = "hide"; - } - settings->setValue(SG_Plot_Symbols, showSymbolsStr); - - settings->endGroup(); -} - -void PlotManager::loadSettings(QSettings* settings) -{ - settings->beginGroup(SettingGroup_Plot); - darkBackgroundAction.setChecked( - settings->value(SG_Plot_DarkBackground, darkBackgroundAction.isChecked()).toBool()); - darkBackground(darkBackgroundAction.isChecked()); - showGridAction.setChecked( - settings->value(SG_Plot_Grid, showGridAction.isChecked()).toBool()); - showGrid(showGridAction.isChecked()); - showMinorGridAction.setChecked( - settings->value(SG_Plot_MinorGrid, showMinorGridAction.isChecked()).toBool()); - showMinorGridAction.setEnabled(showGridAction.isChecked()); - showMinorGrid(showMinorGridAction.isChecked()); - showLegendAction.setChecked( - settings->value(SG_Plot_Legend, showLegendAction.isChecked()).toBool()); - showLegend(showLegendAction.isChecked()); - showMultiAction.setChecked( - settings->value(SG_Plot_MultiPlot, showMultiAction.isChecked()).toBool()); - setMulti(showMultiAction.isChecked()); - - QString showSymbolsStr = settings->value(SG_Plot_Symbols, QString()).toString(); - if (showSymbolsStr == "auto") - { - setSymbols(Plot::ShowSymbolsAuto); - setSymbolsAutoAct->setChecked(true); - } - else if (showSymbolsStr == "show") - { - setSymbols(Plot::ShowSymbolsShow); - setSymbolsShowAct->setChecked(true); - } - else if (showSymbolsStr == "hide") - { - setSymbols(Plot::ShowSymbolsHide); - setSymbolsHideAct->setChecked(true); - } - else - { - qCritical() << "Invalid symbol setting:" << showSymbolsStr; - } - - settings->endGroup(); -} diff --git a/src/plotmanager.h b/src/plotmanager.h --- a/src/plotmanager.h +++ b/src/plotmanager.h @@ -26,30 +26,22 @@ #include #include #include -#include #include #include #include "plot.h" #include "framebufferseries.h" #include "channelinfomodel.h" - -struct PlotViewSettings -{ - bool showGrid; - bool showMinorGrid; - bool darkBackground; - bool showLegend; - bool showMulti; - Plot::ShowSymbols showSymbols; -}; +#include "plotmenu.h" class PlotManager : public QObject { Q_OBJECT public: - explicit PlotManager(QWidget* plotArea, ChannelInfoModel* infoModel = NULL, QObject *parent = 0); + explicit PlotManager(QWidget* plotArea, PlotMenu* menu, + ChannelInfoModel* infoModel = NULL, + QObject *parent = 0); ~PlotManager(); /// Add a new curve with title and buffer. A color is /// automatically chosen for curve. @@ -62,16 +54,8 @@ public: void removeCurves(unsigned number); /// Returns current number of curves known by plot manager unsigned numOfCurves(); - /// Returns the list of actions to be inserted into the `View` menu - QList menuActions(); - /// Returns current status of menu actions - PlotViewSettings viewSettings() const; /// Set the current state of view void setViewSettings(const PlotViewSettings& settings); - /// Stores plot settings into a `QSettings`. - void saveSettings(QSettings* settings); - /// Loads plot settings from a `QSettings`. - void loadSettings(QSettings* settings); public slots: /// Enable/Disable multiple plot display @@ -94,6 +78,7 @@ public slots: private: bool isMulti; QWidget* _plotArea; + PlotMenu* _menu; QVBoxLayout* layout; ///< layout of the `plotArea` QScrollArea* scrollArea; QList curves; @@ -111,19 +96,7 @@ private: double _plotWidth; Plot::ShowSymbols showSymbols; - // menu actions - QAction showGridAction; - QAction showMinorGridAction; - QAction unzoomAction; - QAction darkBackgroundAction; - QAction showLegendAction; - QAction showMultiAction; - QAction setSymbolsAction; - QMenu setSymbolsMenu; - QAction* setSymbolsAutoAct; - QAction* setSymbolsShowAct; - QAction* setSymbolsHideAct; - + /// Setups the layout for multi or single plot void setupLayout(bool multiPlot); /// Inserts a new plot widget to the current layout. Plot* addPlotWidget(); @@ -131,7 +104,6 @@ private: Plot* plotWidget(unsigned curveIndex); /// Common part of overloaded `addCurve` functions void _addCurve(QwtPlotCurve* curve); - void setSymbols(Plot::ShowSymbols shown); /// Check and make sure "no visible channels" text is shown void checkNoVisChannels(); @@ -141,6 +113,7 @@ private slots: void showLegend(bool show = true); void unzoom(); void darkBackground(bool enabled = true); + void setSymbols(Plot::ShowSymbols shown); void onChannelInfoChanged(const QModelIndex & topLeft, const QModelIndex & bottomRight, diff --git a/src/plotmenu.cpp b/src/plotmenu.cpp new file mode 100644 --- /dev/null +++ b/src/plotmenu.cpp @@ -0,0 +1,224 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#include "plotmenu.h" +#include "setting_defines.h" +#include "utils.h" + +PlotMenu::PlotMenu(QWidget* parent) : + QMenu(tr("&View"), parent), + showGridAction("&Grid", this), + showMinorGridAction("&Minor Grid", this), + unzoomAction("&Unzoom", this), + darkBackgroundAction("&Dark Background", this), + showLegendAction("&Legend", this), + showMultiAction("Multi &Plot", this), + setSymbolsAction("&Symbols", this), + setSymbolsAutoAct("Show When &Zoomed", this), + setSymbolsShowAct("Always &Show", this), + setSymbolsHideAct("Always &Hide", this) +{ + showGridAction.setToolTip("Show Grid"); + showMinorGridAction.setToolTip("Show Minor Grid"); + unzoomAction.setToolTip("Unzoom the Plot"); + darkBackgroundAction.setToolTip("Enable Dark Plot Background"); + showLegendAction.setToolTip("Display the Legend on Plot"); + showMultiAction.setToolTip("Display All Channels Separately"); + setSymbolsAction.setToolTip("Show/Hide symbols"); + + showGridAction.setShortcut(QKeySequence("G")); + showMinorGridAction.setShortcut(QKeySequence("M")); + + showGridAction.setCheckable(true); + showMinorGridAction.setCheckable(true); + darkBackgroundAction.setCheckable(true); + showLegendAction.setCheckable(true); + showMultiAction.setCheckable(true); + + showGridAction.setChecked(false); + showMinorGridAction.setChecked(false); + darkBackgroundAction.setChecked(false); + showLegendAction.setChecked(true); + showMultiAction.setChecked(false); + + // minor grid is only enabled when _major_ grid is enabled + showMinorGridAction.setEnabled(false); + connect(&showGridAction, SELECT::OVERLOAD_OF(&QAction::triggered), + &showMinorGridAction, &QAction::setEnabled); + + // setup set symbols menu + setSymbolsMenu.addAction(&setSymbolsAutoAct); + setSymbolsAutoAct.setCheckable(true); + setSymbolsAutoAct.setChecked(true); + connect(&setSymbolsAutoAct, SELECT::OVERLOAD_OF(&QAction::triggered), + [this](bool checked) + { + if (checked) emit symbolShowChanged(Plot::ShowSymbolsAuto); + }); + + setSymbolsMenu.addAction(&setSymbolsShowAct); + setSymbolsShowAct.setCheckable(true); + connect(&setSymbolsShowAct, SELECT::OVERLOAD_OF(&QAction::triggered), + [this](bool checked) + { + if (checked) symbolShowChanged(Plot::ShowSymbolsShow); + }); + + setSymbolsMenu.addAction(&setSymbolsHideAct); + setSymbolsHideAct.setCheckable(true); + connect(&setSymbolsHideAct, SELECT::OVERLOAD_OF(&QAction::triggered), + [this](bool checked) + { + if (checked) symbolShowChanged(Plot::ShowSymbolsHide); + }); + + // add symbol actions to same group so that they appear as radio buttons + auto group = new QActionGroup(this); + group->addAction(&setSymbolsAutoAct); + group->addAction(&setSymbolsShowAct); + group->addAction(&setSymbolsHideAct); + + setSymbolsAction.setMenu(&setSymbolsMenu); + + // add all actions to create this menu + addAction(&showGridAction); + addAction(&showMinorGridAction); + addAction(&unzoomAction); + addAction(&darkBackgroundAction); + addAction(&showLegendAction); + addAction(&showMultiAction); + addAction(&setSymbolsAction); +} + +PlotMenu::PlotMenu(PlotViewSettings s, QWidget* parent) : + PlotMenu(parent) +{ + showGridAction.setChecked(s.showGrid); + showMinorGridAction.setChecked(s.showMinorGrid); + darkBackgroundAction.setChecked(s.darkBackground); + showLegendAction.setChecked(s.showLegend); + showMultiAction.setChecked(s.showMulti); + switch (s.showSymbols) + { + case Plot::ShowSymbolsAuto: + setSymbolsAutoAct.setChecked(true); + break; + case Plot::ShowSymbolsShow: + setSymbolsShowAct.setChecked(true); + break; + case Plot::ShowSymbolsHide: + setSymbolsHideAct.setChecked(true); + break; + } +} + +PlotViewSettings PlotMenu::viewSettings() const +{ + return PlotViewSettings( + { + showGridAction.isChecked(), + showMinorGridAction.isChecked(), + darkBackgroundAction.isChecked(), + showLegendAction.isChecked(), + showMultiAction.isChecked(), + showSymbols() + }); +} + +Plot::ShowSymbols PlotMenu::showSymbols() const +{ + if (setSymbolsAutoAct.isChecked()) + { + return Plot::ShowSymbolsAuto; + } + else if (setSymbolsShowAct.isChecked()) + { + return Plot::ShowSymbolsShow; + } + else // setSymbolsHideAct.isChecked() + { + return Plot::ShowSymbolsHide; + } +} + +void PlotMenu::saveSettings(QSettings* settings) +{ + settings->beginGroup(SettingGroup_Plot); + settings->setValue(SG_Plot_DarkBackground, darkBackgroundAction.isChecked()); + settings->setValue(SG_Plot_Grid, showGridAction.isChecked()); + settings->setValue(SG_Plot_MinorGrid, showMinorGridAction.isChecked()); + settings->setValue(SG_Plot_Legend, showLegendAction.isChecked()); + settings->setValue(SG_Plot_MultiPlot, showMultiAction.isChecked()); + + QString showSymbolsStr; + if (showSymbols() == Plot::ShowSymbolsAuto) + { + showSymbolsStr = "auto"; + } + else if (showSymbols() == Plot::ShowSymbolsShow) + { + showSymbolsStr = "show"; + } + else + { + showSymbolsStr = "hide"; + } + settings->setValue(SG_Plot_Symbols, showSymbolsStr); + + settings->endGroup(); +} + +void PlotMenu::loadSettings(QSettings* settings) +{ + settings->beginGroup(SettingGroup_Plot); + darkBackgroundAction.setChecked( + settings->value(SG_Plot_DarkBackground, darkBackgroundAction.isChecked()).toBool()); + showGridAction.setChecked( + settings->value(SG_Plot_Grid, showGridAction.isChecked()).toBool()); + showMinorGridAction.setChecked( + settings->value(SG_Plot_MinorGrid, showMinorGridAction.isChecked()).toBool()); + showMinorGridAction.setEnabled(showGridAction.isChecked()); + showLegendAction.setChecked( + settings->value(SG_Plot_Legend, showLegendAction.isChecked()).toBool()); + showMultiAction.setChecked( + settings->value(SG_Plot_MultiPlot, showMultiAction.isChecked()).toBool()); + + QString showSymbolsStr = settings->value(SG_Plot_Symbols, QString()).toString(); + if (showSymbolsStr == "auto") + { + // setSymbols(Plot::ShowSymbolsAuto); + setSymbolsAutoAct.setChecked(true); + } + else if (showSymbolsStr == "show") + { + // setSymbols(Plot::ShowSymbolsShow); + setSymbolsShowAct.setChecked(true); + } + else if (showSymbolsStr == "hide") + { + // setSymbols(Plot::ShowSymbolsHide); + setSymbolsHideAct.setChecked(true); + } + else + { + qCritical() << "Invalid symbol setting:" << showSymbolsStr; + } + + settings->endGroup(); +} diff --git a/src/plotmenu.h b/src/plotmenu.h new file mode 100644 --- /dev/null +++ b/src/plotmenu.h @@ -0,0 +1,73 @@ +/* + Copyright © 2017 Hasan Yavuz Özderya + + This file is part of serialplot. + + serialplot is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + serialplot is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with serialplot. If not, see . +*/ + +#ifndef PLOTMENU_H +#define PLOTMENU_H + +#include +#include +#include + +#include "plot.h" + +/// Used to quickly transfer view options between different menus +struct PlotViewSettings +{ + bool showGrid; + bool showMinorGrid; + bool darkBackground; + bool showLegend; + bool showMulti; + Plot::ShowSymbols showSymbols; +}; + +class PlotMenu : public QMenu +{ + Q_OBJECT + +public: + PlotMenu(QWidget* parent = 0); + PlotMenu(PlotViewSettings s, QWidget* parent = 0); + + QAction showGridAction; + QAction showMinorGridAction; + QAction unzoomAction; + QAction darkBackgroundAction; + QAction showLegendAction; + QAction showMultiAction; + QAction setSymbolsAction; + QMenu setSymbolsMenu; + QAction setSymbolsAutoAct; + QAction setSymbolsShowAct; + QAction setSymbolsHideAct; + + /// Returns a bundle of current view settings (menu selections) + PlotViewSettings viewSettings() const; + /// Selected "show symbol" option + Plot::ShowSymbols showSymbols() const; + /// Stores plot settings into a `QSettings`. + void saveSettings(QSettings* settings); + /// Loads plot settings from a `QSettings`. + void loadSettings(QSettings* settings); + +signals: + void symbolShowChanged(Plot::ShowSymbols shown); +}; + +#endif // PLOTMENU_H diff --git a/src/snapshotview.cpp b/src/snapshotview.cpp --- a/src/snapshotview.cpp +++ b/src/snapshotview.cpp @@ -23,14 +23,14 @@ SnapshotView::SnapshotView(MainWindow* parent, Snapshot* snapshot) : QMainWindow(parent), ui(new Ui::SnapshotView), - renameDialog(this) + renameDialog(this), + plotMenu(parent->viewSettings()) { _snapshot = snapshot; ui->setupUi(this); - plotMan = new PlotManager(ui->plotArea, snapshot->infoModel(), this); - plotMan->setViewSettings(parent->viewSettings()); + plotMan = new PlotManager(ui->plotArea, &plotMenu, snapshot->infoModel(), this); ui->menuSnapshot->insertAction(ui->actionClose, snapshot->deleteAction()); this->setWindowTitle(snapshot->displayName()); @@ -53,11 +53,8 @@ SnapshotView::SnapshotView(MainWindow* p connect(ui->actionSave, &QAction::triggered, this, &SnapshotView::save); - // add 'View' menu items - for (auto a : plotMan->menuActions()) - { - ui->menuView->addAction(a); - } + // add "View" menu + menuBar()->insertMenu(NULL, &plotMenu); } SnapshotView::~SnapshotView() diff --git a/src/snapshotview.h b/src/snapshotview.h --- a/src/snapshotview.h +++ b/src/snapshotview.h @@ -31,6 +31,7 @@ #include "mainwindow.h" #include "plotmanager.h" +#include "plotmenu.h" #include "snapshot.h" namespace Ui { @@ -54,6 +55,7 @@ private: Snapshot* _snapshot; QInputDialog renameDialog; PlotManager* plotMan; + PlotMenu plotMenu; void closeEvent(QCloseEvent *event); diff --git a/src/snapshotview.ui b/src/snapshotview.ui --- a/src/snapshotview.ui +++ b/src/snapshotview.ui @@ -26,7 +26,7 @@ 0 0 544 - 25 + 24 @@ -37,13 +37,7 @@ - - - &View - - -