# HG changeset patch # User Hasan Yavuz ÖZDERYA # Date 2015-09-30 11:20:39 # Node ID b7695437c109e5e7fe9e7fa307c97a4a3bb34a11 # Parent 6abb4bd782b3d26ebe317a7ab8015298f98047cf # Parent c51c9195eb8ff01509a5fece01acd7a504fd6bec Merge with snapshots diff --git a/CMakeLists.txt b/CMakeLists.txt --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -52,7 +52,12 @@ endif (QWT_USE_STATIC) include_directories(${QWT_INCLUDE_DIR}) # wrap UI and resource files -qt5_wrap_ui(UI_FILES mainwindow.ui portcontrol.ui about_dialog.ui) +qt5_wrap_ui(UI_FILES + mainwindow.ui + portcontrol.ui + about_dialog.ui + snapshotview.ui + ) qt5_add_resources(RES_FILES misc/icons.qrc) add_executable(${PROGRAM_NAME} WIN32 @@ -66,6 +71,10 @@ add_executable(${PROGRAM_NAME} WIN32 scalepicker.cpp scalezoomer.cpp portlist.cpp + snapshot.cpp + snapshotview.cpp + snapshotmanager.cpp + plotsnapshotoverlay.cpp ${UI_FILES} ${RES_FILES} misc/windows_icon.rc diff --git a/mainwindow.cpp b/mainwindow.cpp --- a/mainwindow.cpp +++ b/mainwindow.cpp @@ -31,6 +31,8 @@ #include #include +#include + #include "utils.h" #include "version.h" #include "floatswap.h" @@ -51,15 +53,25 @@ Q_DECLARE_METATYPE(Range); MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow), - portControl(&serialPort) + portControl(&serialPort), + snapshotMan(this, &channelBuffers) { ui->setupUi(this); ui->tabWidget->insertTab(0, &portControl, "Port"); ui->tabWidget->setCurrentIndex(0); addToolBar(portControl.toolBar()); + ui->mainToolBar->addAction(snapshotMan.takeSnapshotAction()); + ui->menuBar->insertMenu(ui->menuHelp->menuAction(), snapshotMan.menu()); + setupAboutDialog(); + // init view menu + for (auto a : ui->plot->menuActions()) + { + ui->menuView->addAction(a); + } + // init UI signals // menu signals @@ -69,27 +81,11 @@ MainWindow::MainWindow(QWidget *parent) QObject::connect(ui->actionExportCsv, &QAction::triggered, this, &MainWindow::onExportCsv); + ui->actionQuit->setShortcutContext(Qt::ApplicationShortcut); + QObject::connect(ui->actionQuit, &QAction::triggered, this, &MainWindow::close); - QObject::connect(ui->actionGrid, &QAction::toggled, [this](bool show) - { - ui->plot->showGrid(show); - ui->actionMinorGrid->setEnabled(show); - }); - - ui->actionMinorGrid->setEnabled(ui->actionGrid->isChecked()); - QObject::connect(ui->actionMinorGrid, &QAction::toggled, - ui->plot, &Plot::showMinorGrid); - - QObject::connect(ui->actionUnzoom, &QAction::triggered, - ui->plot, &Plot::unzoom); - - QObject::connect(ui->actionDarkBackground, &QAction::toggled, - ui->plot, &Plot::darkBackground); - - ui->plot->darkBackground(ui->actionDarkBackground->isChecked()); - // port control signals QObject::connect(&portControl, &PortControl::portToggled, this, &MainWindow::onPortToggled); @@ -112,6 +108,9 @@ MainWindow::MainWindow(QWidget *parent) QObject::connect(ui->actionClear, SIGNAL(triggered(bool)), this, SLOT(clearPlot())); + QObject::connect(snapshotMan.takeSnapshotAction(), &QAction::triggered, + ui->plot, &Plot::flashSnapshotOverlay); + // setup number of channels spinbox QObject::connect(ui->spNumOfChannels, SELECT::OVERLOAD_OF(&QSpinBox::valueChanged), @@ -152,7 +151,7 @@ MainWindow::MainWindow(QWidget *parent) channelBuffers.append(new FrameBuffer(numOfSamples)); curves.append(new QwtPlotCurve()); curves[i]->setSamples(channelBuffers[i]); - curves[i]->setPen(makeColor(i)); + curves[i]->setPen(Plot::makeColor(i)); curves[i]->attach(ui->plot); } @@ -160,10 +159,6 @@ MainWindow::MainWindow(QWidget *parent) ui->plot->setAxis(ui->cbAutoScale->isChecked(), ui->spYmin->value(), ui->spYmax->value()); - // init grid - ui->plot->showGrid(ui->actionGrid->isChecked()); - ui->plot->showMinorGrid(ui->actionMinorGrid->isChecked()); - // init scale range preset list for (int nbits = 8; nbits <= 24; nbits++) // signed binary formats { @@ -241,6 +236,7 @@ MainWindow::~MainWindow() { serialPort.close(); } + delete ui; ui = NULL; // we check if ui is deleted in messageHandler } @@ -460,7 +456,7 @@ void MainWindow::onNumOfChannelsChanged( channelBuffers.append(new FrameBuffer(numOfSamples)); curves.append(new QwtPlotCurve()); curves.last()->setSamples(channelBuffers.last()); - curves.last()->setPen(makeColor(curves.length()-1)); + curves.last()->setPen(Plot::makeColor(curves.length()-1)); curves.last()->attach(ui->plot); } } @@ -642,37 +638,6 @@ void MainWindow::enableDemo(bool enabled } } -/* - Below crude drawing demostrates how color selection occurs for - given channel index - - 0° <--Hue Value--> 360° - |* . o . + . o . * . o . + . o . * . o . + . o . * . o . + . o . | - - * -> 0-3 - + -> 4-7 - o -> 8-15 - . -> 16-31 - - */ - -QColor MainWindow::makeColor(unsigned int channelIndex) -{ - auto i = channelIndex; - - if (i < 4) - { - return QColor::fromHsv(360*i/4, 255, 230); - } - else - { - double p = floor(log2(i)); - double n = pow(2, p); - i = i - n; - return QColor::fromHsv(360*i/n + 360/pow(2,p+1), 255, 230); - } -} - void MainWindow::onExportCsv() { bool wasPaused = ui->actionPause->isChecked(); diff --git a/mainwindow.h b/mainwindow.h --- a/mainwindow.h +++ b/mainwindow.h @@ -37,6 +37,7 @@ #include "portcontrol.h" #include "ui_about_dialog.h" #include "framebuffer.h" +#include "snapshotmanager.h" namespace Ui { class MainWindow; @@ -99,14 +100,14 @@ private: unsigned int sampleCount; QTimer spsTimer; + SnapshotManager snapshotMan; + // demo QTimer demoTimer; int demoCount; bool isDemoRunning(); QwtPlotTextLabel demoIndicator; - QColor makeColor(unsigned int channelIndex); - private slots: void onPortToggled(bool open); void onDataReady(); // used with binary number formats diff --git a/mainwindow.ui b/mainwindow.ui --- a/mainwindow.ui +++ b/mainwindow.ui @@ -16,7 +16,7 @@ - + 0 @@ -483,10 +483,6 @@ View - - - - @@ -560,56 +556,12 @@ Ctrl+Q - - - true - - - Grid - - - Show/hide grid - - - G - - - - - UnZoom - - - - - true - - - Minor Grid - - - Show/hide minor grid - - - M - - - - - true - - - Dark Background - - - Toggle Dark Background - - Plot - QFrame + QWidget
plot.h
1
diff --git a/plot.cpp b/plot.cpp --- a/plot.cpp +++ b/plot.cpp @@ -18,13 +18,21 @@ */ #include +#include + #include "plot.h" +#include "utils.h" + Plot::Plot(QWidget* parent) : QwtPlot(parent), zoomer(this->canvas(), false), - sZoomer(this, &zoomer) + sZoomer(this, &zoomer), + _showGridAction("Grid", this), + _showMinorGridAction("Minor Grid", this), + _unzoomAction("Unzoom", this), + _darkBackgroundAction("Dark Background", this) { - isAutoScaled = false; + isAutoScaled = true; QObject::connect(&zoomer, &Zoomer::unzoomed, this, &Plot::unzoomed); @@ -34,7 +42,43 @@ Plot::Plot(QWidget* parent) : rectItem.setRect(QRectF(0,0,100,1)); // rectItem.attach(this); + showGrid(false); darkBackground(false); + + _showGridAction.setToolTip("Show Grid"); + _showMinorGridAction.setToolTip("Show Minor Grid"); + _unzoomAction.setToolTip("Unzoom the Plot"); + _darkBackgroundAction.setToolTip("Enable Dark Plot Background"); + + _showGridAction.setShortcut(QKeySequence("G")); + _showMinorGridAction.setShortcut(QKeySequence("M")); + + _showGridAction.setCheckable(true); + _showMinorGridAction.setCheckable(true); + _darkBackgroundAction.setCheckable(true); + + _showGridAction.setChecked(false); + _showMinorGridAction.setChecked(false); + _darkBackgroundAction.setChecked(false); + + _showMinorGridAction.setEnabled(false); + + connect(&_showGridAction, SELECT::OVERLOAD_OF(&QAction::triggered), + this, &Plot::showGrid); + connect(&_showGridAction, SELECT::OVERLOAD_OF(&QAction::triggered), + &_showMinorGridAction, &QAction::setEnabled); + connect(&_showMinorGridAction, SELECT::OVERLOAD_OF(&QAction::triggered), + this, &Plot::showMinorGrid); + connect(&_unzoomAction, &QAction::triggered, this, &Plot::unzoom); + connect(&_darkBackgroundAction, SELECT::OVERLOAD_OF(&QAction::triggered), + this, &Plot::darkBackground); + + snapshotOverlay = NULL; +} + +Plot::~Plot() +{ + if (snapshotOverlay != NULL) delete snapshotOverlay; } void Plot::setAxis(bool autoScaled, double yAxisMin, double yAxisMax) @@ -51,6 +95,16 @@ void Plot::setAxis(bool autoScaled, doub resetAxes(); } +QList Plot::menuActions() +{ + QList actions; + actions << &_showGridAction; + actions << &_showMinorGridAction; + actions << &_unzoomAction; + actions << &_darkBackgroundAction; + return actions; +} + void Plot::resetAxes() { if (isAutoScaled) @@ -110,3 +164,56 @@ void Plot::darkBackground(bool enabled) } replot(); } + +/* + Below crude drawing demostrates how color selection occurs for + given channel index + + 0° <--Hue Value--> 360° + |* . o . + . o . * . o . + . o . * . o . + . o . * . o . + . o . | + + * -> 0-3 + + -> 4-7 + o -> 8-15 + . -> 16-31 + + */ +QColor Plot::makeColor(unsigned int channelIndex) +{ + auto i = channelIndex; + + if (i < 4) + { + return QColor::fromHsv(360*i/4, 255, 230); + } + else + { + double p = floor(log2(i)); + double n = pow(2, p); + i = i - n; + return QColor::fromHsv(360*i/n + 360/pow(2,p+1), 255, 230); + } +} + +void Plot::flashSnapshotOverlay() +{ + if (snapshotOverlay != NULL) delete snapshotOverlay; + + QColor color; + if (_darkBackgroundAction.isChecked()) + { + color = QColor(Qt::white); + } + else + { + color = QColor(Qt::black); + } + + snapshotOverlay = new PlotSnapshotOverlay(this->canvas(), color); + connect(snapshotOverlay, &PlotSnapshotOverlay::done, + [this]() + { + delete snapshotOverlay; + snapshotOverlay = NULL; + }); +} diff --git a/plot.h b/plot.h --- a/plot.h +++ b/plot.h @@ -20,11 +20,16 @@ #ifndef PLOT_H #define PLOT_H +#include +#include +#include #include #include #include + #include "zoomer.h" #include "scalezoomer.h" +#include "plotsnapshotoverlay.h" class Plot : public QwtPlot { @@ -32,8 +37,13 @@ class Plot : public QwtPlot public: Plot(QWidget* parent = 0); + ~Plot(); void setAxis(bool autoScaled, double yMin = 0, double yMax = 1); + QList menuActions(); + + static QColor makeColor(unsigned int channelIndex); + private: bool isAutoScaled; double yMin, yMax; @@ -41,6 +51,12 @@ private: ScaleZoomer sZoomer; QwtPlotGrid grid; QwtPlotShapeItem rectItem; + PlotSnapshotOverlay* snapshotOverlay; + + QAction _showGridAction; + QAction _showMinorGridAction; + QAction _unzoomAction; + QAction _darkBackgroundAction; void resetAxes(); @@ -50,6 +66,8 @@ public slots: void unzoom(); void darkBackground(bool enabled = true); + void flashSnapshotOverlay(); + private slots: void unzoomed(); }; diff --git a/plotsnapshotoverlay.cpp b/plotsnapshotoverlay.cpp new file mode 100644 --- /dev/null +++ b/plotsnapshotoverlay.cpp @@ -0,0 +1,65 @@ +/* + Copyright © 2015 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 "plotsnapshotoverlay.h" + +#define LINE_WIDTH (10) +#define ANIM_LENGTH (500) // milliseconds +#define UPDATE_PERIOD (20) // milliseconds + +PlotSnapshotOverlay::PlotSnapshotOverlay(QWidget* widget, QColor color) : + QwtWidgetOverlay(widget) +{ + _color = color; + animTimer.setSingleShot(true); + animTimer.setInterval(ANIM_LENGTH); + updateTimer.setSingleShot(false); + updateTimer.setInterval(UPDATE_PERIOD); + + connect(&updateTimer, &QTimer::timeout, [this](){this->updateOverlay();}); + connect(&animTimer, &QTimer::timeout, &updateTimer, &QTimer::stop); + connect(&animTimer, &QTimer::timeout, this, &PlotSnapshotOverlay::done); + + animTimer.start(); + updateTimer.start(); +} + +void PlotSnapshotOverlay::drawOverlay(QPainter* painter) const +{ + if (!animTimer.isActive()) return; + QColor lineColor = _color; + + double fadingRatio = ((double) animTimer.remainingTime()) / ANIM_LENGTH; + lineColor.setAlpha(std::min(255, (int) (255*fadingRatio))); + + QPen pen(lineColor); + pen.setWidth(LINE_WIDTH); + pen.setJoinStyle(Qt::MiterJoin); + + int width = painter->device()->width(); + int height = painter->device()->height(); + + painter->save(); + painter->setPen(pen); + painter->drawRect(LINE_WIDTH/2, LINE_WIDTH/2, width-LINE_WIDTH, height-LINE_WIDTH); + painter->restore(); +} diff --git a/plotsnapshotoverlay.h b/plotsnapshotoverlay.h new file mode 100644 --- /dev/null +++ b/plotsnapshotoverlay.h @@ -0,0 +1,48 @@ +/* + Copyright © 2015 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 PLOTSNAPSHOTOVERLAY_H +#define PLOTSNAPSHOTOVERLAY_H + +#include +#include +#include +#include + +// Draws a sort of flashing effect on plot widget when snapshot taken +class PlotSnapshotOverlay : public QwtWidgetOverlay +{ + Q_OBJECT + +public: + PlotSnapshotOverlay(QWidget* widget, QColor color); + +protected: + void drawOverlay(QPainter*) const; + +signals: + void done(); // indicates that animation completed + +private: + QColor _color; + QTimer animTimer; // controls fading + QTimer updateTimer; // need to force repaint the canvas +}; + +#endif // PLOTSNAPSHOTOVERLAY_H diff --git a/serialplot.pro b/serialplot.pro --- a/serialplot.pro +++ b/serialplot.pro @@ -42,7 +42,10 @@ SOURCES += main.cpp\ framebuffer.cpp \ scalepicker.cpp \ scalezoomer.cpp \ - portlist.cpp + portlist.cpp \ + snapshotview.cpp \ + snapshotmanager.cpp \ + snapshot.cpp HEADERS += mainwindow.h \ utils.h \ @@ -54,11 +57,15 @@ HEADERS += mainwindow.h \ framebuffer.h \ scalepicker.h \ scalezoomer.h \ - portlist.h + portlist.h \ + snapshotview.h \ + snapshotmanager.h \ + snapshot.h FORMS += mainwindow.ui \ about_dialog.ui \ - portcontrol.ui + portcontrol.ui \ + snapshotview.ui INCLUDEPATH += qmake/ diff --git a/snapshot.cpp b/snapshot.cpp new file mode 100644 --- /dev/null +++ b/snapshot.cpp @@ -0,0 +1,91 @@ +/* + Copyright © 2015 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 "snapshot.h" +#include "snapshotview.h" + +Snapshot::Snapshot(QMainWindow* parent, QString name) : + QObject(parent), + _showAction(name, this), + _deleteAction("Delete", this) +{ + _name = name; + + view = NULL; + mainWindow = parent; + connect(&_showAction, &QAction::triggered, this, &Snapshot::show); + + _deleteAction.setToolTip(QString("Delete ") + _name); + connect(&_deleteAction, &QAction::triggered, this, &Snapshot::onDeleteTriggered); +} + +Snapshot::~Snapshot() +{ + if (view != NULL) + { + delete view; + } +} + +QAction* Snapshot::showAction() +{ + return &_showAction; +} + +QAction* Snapshot::deleteAction() +{ + return &_deleteAction; +} + +void Snapshot::show() +{ + if (view == NULL) + { + view = new SnapshotView(mainWindow, this); + connect(view, &SnapshotView::closed, this, &Snapshot::viewClosed); + } + view->show(); + view->activateWindow(); + view->raise(); +} + +void Snapshot::viewClosed() +{ + view->deleteLater(); + view = NULL; +} + +void Snapshot::onDeleteTriggered() +{ + emit deleteRequested(this); +} + +QString Snapshot::name() +{ + return _name; +} + +void Snapshot::setName(QString name) +{ + _name = name; + _showAction.setText(_name); + emit nameChanged(this); +} diff --git a/snapshot.h b/snapshot.h new file mode 100644 --- /dev/null +++ b/snapshot.h @@ -0,0 +1,64 @@ +/* + Copyright © 2015 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 SNAPSHOT_H +#define SNAPSHOT_H + +#include +#include +#include +#include +#include + +class SnapshotView; + +class Snapshot : public QObject +{ + Q_OBJECT + +public: + Snapshot(QMainWindow* parent, QString name); + ~Snapshot(); + + QVector> data; + QAction* showAction(); + QAction* deleteAction(); + + QString name(); + void setName(QString name); + +signals: + void deleteRequested(Snapshot*); + void nameChanged(Snapshot*); + +private: + QString _name; + QAction _showAction; + QAction _deleteAction; + QMainWindow* mainWindow; + SnapshotView* view; + +private slots: + void show(); + void viewClosed(); + + void onDeleteTriggered(); +}; + +#endif /* SNAPSHOT_H */ diff --git a/snapshotmanager.cpp b/snapshotmanager.cpp new file mode 100644 --- /dev/null +++ b/snapshotmanager.cpp @@ -0,0 +1,198 @@ +/* + Copyright © 2015 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 +#include +#include +#include +#include + +#include "snapshotmanager.h" + +SnapshotManager::SnapshotManager(QMainWindow* mainWindow, + QList* channelBuffers) : + _menu("Snapshots"), + _takeSnapshotAction("Take Snapshot", this), + loadSnapshotAction("Load Snapshots", this), + clearAction("Clear Snapshots", this) +{ + _mainWindow = mainWindow; + _channelBuffers = channelBuffers; + + _takeSnapshotAction.setToolTip("Take a snapshot of current plot (F5)"); + _takeSnapshotAction.setShortcut(QKeySequence("F5")); + loadSnapshotAction.setToolTip("Load snapshots from CSV files"); + clearAction.setToolTip("Delete all snapshots"); + connect(&_takeSnapshotAction, SIGNAL(triggered(bool)), + this, SLOT(takeSnapshot())); + connect(&clearAction, SIGNAL(triggered(bool)), + this, SLOT(clearSnapshots())); + connect(&loadSnapshotAction, SIGNAL(triggered(bool)), + this, SLOT(loadSnapshots())); + + updateMenu(); +} + +SnapshotManager::~SnapshotManager() +{ + for (auto snapshot : snapshots) + { + delete snapshot; + } +} + +void SnapshotManager::takeSnapshot() +{ + QString name = QTime::currentTime().toString("'Snapshot ['HH:mm:ss']'"); + auto snapshot = new Snapshot(_mainWindow, name); + + unsigned numOfChannels = _channelBuffers->size(); + unsigned numOfSamples = _channelBuffers->at(0)->size(); + + for (unsigned ci = 0; ci < numOfChannels; ci++) + { + snapshot->data.append(QVector(numOfSamples)); + for (unsigned i = 0; i < numOfSamples; i++) + { + snapshot->data[ci][i] = _channelBuffers->at(ci)->sample(i); + } + } + + addSnapshot(snapshot); +} + +void SnapshotManager::addSnapshot(Snapshot* snapshot, bool update_menu) +{ + snapshots.append(snapshot); + QObject::connect(snapshot, &Snapshot::deleteRequested, + this, &SnapshotManager::deleteSnapshot); + if (update_menu) updateMenu(); +} + +void SnapshotManager::updateMenu() +{ + _menu.clear(); + _menu.addAction(&_takeSnapshotAction); + _menu.addAction(&loadSnapshotAction); + if (snapshots.size()) + { + _menu.addSeparator(); + for (auto ss : snapshots) + { + _menu.addAction(ss->showAction()); + } + _menu.addSeparator(); + _menu.addAction(&clearAction); + } +} + +void SnapshotManager::clearSnapshots() +{ + for (auto snapshot : snapshots) + { + delete snapshot; + } + snapshots.clear(); + updateMenu(); +} + +void SnapshotManager::deleteSnapshot(Snapshot* snapshot) +{ + snapshots.removeOne(snapshot); + snapshot->deleteLater(); // regular delete causes a crash when triggered from menu + updateMenu(); +} + +void SnapshotManager::loadSnapshots() +{ + auto files = QFileDialog::getOpenFileNames(_mainWindow, tr("Load CSV File")); + + for (auto f : files) + { + if (!f.isNull()) loadSnapshotFromFile(f); + } + + updateMenu(); +} + +void SnapshotManager::loadSnapshotFromFile(QString fileName) +{ + QFile file(fileName); + if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) + { + qCritical() << "Couldn't open file: " << fileName; + qCritical() << file.errorString(); + return; + } + + // read first row as headlines and determine number of channels + auto headLine = QString(file.readLine()); + unsigned numOfChannels = headLine.split(',').size(); + + // read data + QVector> data(numOfChannels); + unsigned lineNum = 1; + while (file.canReadLine()) + { + // parse line + auto line = QString(file.readLine()); + auto split = line.split(','); + + if (split.size() != (int) numOfChannels) + { + qCritical() << "Parsing error at line " << lineNum + << ": number of columns is not consistent."; + return; + } + + for (unsigned ci = 0; ci < numOfChannels; ci++) + { + // parse column + bool ok; + double y = split[ci].toDouble(&ok); + if (!ok) + { + qCritical() << "Parsing error at line " << lineNum + << ", column " << ci + << ": can't convert \"" << split[ci] + << "\" to double."; + return; + } + data[ci].append(QPointF(lineNum-1, y)); + } + lineNum++; + } + + auto snapshot = new Snapshot(_mainWindow, QFileInfo(fileName).baseName()); + snapshot->data = data; + + addSnapshot(snapshot, false); +} + +QMenu* SnapshotManager::menu() +{ + return &_menu; +} + +QAction* SnapshotManager::takeSnapshotAction() +{ + return &_takeSnapshotAction; +} diff --git a/snapshotmanager.h b/snapshotmanager.h new file mode 100644 --- /dev/null +++ b/snapshotmanager.h @@ -0,0 +1,63 @@ +/* + Copyright © 2015 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 SNAPSHOTMANAGER_H +#define SNAPSHOTMANAGER_H + +#include +#include +#include + +#include "framebuffer.h" +#include "snapshot.h" + +class SnapshotManager : public QObject +{ + Q_OBJECT + +public: + SnapshotManager(QMainWindow* mainWindow, QList* channelBuffers); + ~SnapshotManager(); + + QMenu* menu(); + QAction* takeSnapshotAction(); + +private: + QMainWindow* _mainWindow; + QList* _channelBuffers; + + QList snapshots; + + QMenu _menu; + QAction _takeSnapshotAction; + QAction loadSnapshotAction; + QAction clearAction; + + void addSnapshot(Snapshot* snapshot, bool update_menu=true); + void updateMenu(); + +private slots: + void takeSnapshot(); + void clearSnapshots(); + void deleteSnapshot(Snapshot* snapshot); + void loadSnapshots(); + void loadSnapshotFromFile(QString fileName); +}; + +#endif /* SNAPSHOTMANAGER_H */ diff --git a/snapshotview.cpp b/snapshotview.cpp new file mode 100644 --- /dev/null +++ b/snapshotview.cpp @@ -0,0 +1,132 @@ +/* + Copyright © 2015 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 "snapshotview.h" +#include "ui_snapshotview.h" + +SnapshotView::SnapshotView(QWidget *parent, Snapshot* snapshot) : + QMainWindow(parent), + ui(new Ui::SnapshotView), + renameDialog(this) +{ + _snapshot = snapshot; + + ui->setupUi(this); + ui->menuSnapshot->insertAction(ui->actionClose, snapshot->deleteAction()); + this->setWindowTitle(snapshot->name()); + + unsigned numOfChannels = snapshot->data.size(); + + for (unsigned ci = 0; ci < numOfChannels; ci++) + { + QwtPlotCurve* curve = new QwtPlotCurve(); + curves.append(curve); + curve->setSamples(snapshot->data[ci]); + curve->setPen(Plot::makeColor(ci)); + curve->attach(ui->plot); + } + + renameDialog.setWindowTitle("Rename Snapshot"); + renameDialog.setLabelText("Enter new name:"); + connect(ui->actionRename, &QAction::triggered, + this, &SnapshotView::showRenameDialog); + + connect(ui->actionExport, &QAction::triggered, + this, &SnapshotView::save); + + for (auto a : ui->plot->menuActions()) + { + ui->menuView->addAction(a); + } +} + +SnapshotView::~SnapshotView() +{ + for (auto curve : curves) + { + delete curve; + } + delete ui; +} + +void SnapshotView::closeEvent(QCloseEvent *event) +{ + QMainWindow::closeEvent(event); + emit closed(); +} + +void SnapshotView::showRenameDialog() +{ + renameDialog.setTextValue(_snapshot->name()); + renameDialog.open(this, SLOT(renameSnapshot(QString))); +} + +void SnapshotView::renameSnapshot(QString name) +{ + _snapshot->setName(name); + setWindowTitle(name); +} + +void SnapshotView::save() +{ + QString fileName = QFileDialog::getSaveFileName(this, tr("Export CSV File")); + + if (fileName.isNull()) return; // user canceled + + // TODO: remove code duplication (MainWindow::onExportCsv) + QSaveFile file(fileName); + + if (file.open(QIODevice::WriteOnly | QIODevice::Text)) + { + QTextStream fileStream(&file); + + unsigned numOfChannels = _snapshot->data.size(); + unsigned numOfSamples = _snapshot->data[0].size(); + + // print header + for (unsigned int ci = 0; ci < numOfChannels; ci++) + { + fileStream << "Channel " << ci; + if (ci != numOfChannels-1) fileStream << ","; + } + fileStream << '\n'; + + // print rows + for (unsigned int i = 0; i < numOfSamples; i++) + { + for (unsigned int ci = 0; ci < numOfChannels; ci++) + { + fileStream << _snapshot->data[ci][i].y(); + if (ci != numOfChannels-1) fileStream << ","; + } + fileStream << '\n'; + } + + if (!file.commit()) + { + qCritical() << "File save error during snapshot save: " << file.error(); + } + } + else + { + qCritical() << "File open error during snapshot save: " << file.error(); + } +} diff --git a/snapshotview.h b/snapshotview.h new file mode 100644 --- /dev/null +++ b/snapshotview.h @@ -0,0 +1,64 @@ +/* + Copyright © 2015 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 SNAPSHOTVIEW_H +#define SNAPSHOTVIEW_H + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "plot.h" +#include "snapshot.h" + +namespace Ui { +class SnapshotView; +} + +class SnapshotView : public QMainWindow +{ + Q_OBJECT + +public: + explicit SnapshotView(QWidget *parent, Snapshot* snapshot); + ~SnapshotView(); + +signals: + void closed(); + +private: + Ui::SnapshotView *ui; + QList curves; + Snapshot* _snapshot; + QInputDialog renameDialog; + + void closeEvent(QCloseEvent *event); + +private slots: + void showRenameDialog(); + void renameSnapshot(QString name); + void save(); +}; + +#endif // SNAPSHOTVIEW_H diff --git a/snapshotview.ui b/snapshotview.ui new file mode 100644 --- /dev/null +++ b/snapshotview.ui @@ -0,0 +1,103 @@ + + + SnapshotView + + + + 0 + 0 + 544 + 449 + + + + MainWindow + + + + + + + + + + + + 0 + 0 + 544 + 27 + + + + + Snapshot + + + + + + + + View + + + + + + + + Export CSV + + + Save snapshot as CSV file + + + + + Rename + + + Rename this snapshot + + + + + Close + + + Close Window + + + Ctrl+W + + + + + + Plot + QWidget +
plot.h
+ 1 +
+
+ + + + actionClose + triggered() + SnapshotView + close() + + + -1 + -1 + + + 271 + 224 + + + + +