Changeset - 6585bc4c1c6c
[Not reviewed]
recording
0 4 0
Hasan Yavuz ÖZDERYA - 9 years ago 2017-02-16 08:06:13
hy@ozderya.net
save/load record settings
4 files changed with 50 insertions and 5 deletions:
0 comments (0 inline, 0 general)
src/mainwindow.cpp
Show inline comments
 
/*
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include "mainwindow.h"
 
#include "ui_mainwindow.h"
 
#include <QByteArray>
 
#include <QApplication>
 
#include <QFileDialog>
 
#include <QMessageBox>
 
#include <QFile>
 
#include <QTextStream>
 
#include <QMenu>
 
#include <QDesktopServices>
 
#include <QMap>
 
#include <QtDebug>
 
#include <qwt_plot.h>
 
#include <limits.h>
 
#include <cmath>
 
#include <iostream>
 

	
 
#include <plot.h>
 

	
 
#include "framebufferseries.h"
 
#include "utils.h"
 
#include "defines.h"
 
#include "version.h"
 
#include "setting_defines.h"
 

	
 
#if defined(Q_OS_WIN) && defined(QT_STATIC)
 
#include <QtPlugin>
 
Q_IMPORT_PLUGIN(QWindowsIntegrationPlugin)
 
#endif
 

	
 
const QMap<int, QString> panelSettingMap({
 
        {0, "Port"},
 
        {1, "DataFormat"},
 
        {2, "Plot"},
 
        {3, "Commands"}
 
    });
 

	
 
MainWindow::MainWindow(QWidget *parent) :
 
    QMainWindow(parent),
 
    ui(new Ui::MainWindow),
 
    aboutDialog(this),
 
    portControl(&serialPort),
 
    channelMan(1, 1, this),
 
    snapshotMan(this, &channelMan),
 
    commandPanel(&serialPort),
 
    dataFormatPanel(&serialPort, &channelMan, &recorder),
 
    recordPanel(&recorder, &channelMan)
 
{
 
    ui->setupUi(this);
 

	
 
    plotMan = new PlotManager(ui->plotArea, channelMan.infoModel());
 

	
 
    ui->tabWidget->insertTab(0, &portControl, "Port");
 
    ui->tabWidget->insertTab(1, &dataFormatPanel, "Data Format");
 
    ui->tabWidget->insertTab(2, &plotControlPanel, "Plot");
 
    ui->tabWidget->insertTab(3, &commandPanel, "Commands");
 
    ui->tabWidget->insertTab(4, &recordPanel, "Record");
 
    ui->tabWidget->setCurrentIndex(0);
 
    auto tbPortControl = portControl.toolBar();
 
    addToolBar(tbPortControl);
 
    addToolBar(recordPanel.toolbar());
 

	
 
    ui->plotToolBar->addAction(snapshotMan.takeSnapshotAction());
 
    ui->menuBar->insertMenu(ui->menuHelp->menuAction(), snapshotMan.menu());
 
    ui->menuBar->insertMenu(ui->menuHelp->menuAction(), commandPanel.menu());
 

	
 
    connect(&commandPanel, &CommandPanel::focusRequested, [this]()
 
            {
 
                this->ui->tabWidget->setCurrentWidget(&commandPanel);
 
            });
 

	
 
    tbPortControl->setObjectName("tbPortControl");
 
    ui->plotToolBar->setObjectName("tbPlot");
 

	
 
    setupAboutDialog();
 

	
 
    // init view menu
 
    for (auto a : plotMan->menuActions())
 
    {
 
        ui->menuView->addAction(a);
 
    }
 

	
 
    ui->menuView->addSeparator();
 

	
 
    QMenu* tbMenu = ui->menuView->addMenu("Toolbars");
 
    tbMenu->addAction(ui->plotToolBar->toggleViewAction());
 
    tbMenu->addAction(portControl.toolBar()->toggleViewAction());
 

	
 
    // init UI signals
 

	
 
    // Help menu signals
 
    QObject::connect(ui->actionHelpAbout, &QAction::triggered,
 
              &aboutDialog, &QWidget::show);
 

	
 
    QObject::connect(ui->actionReportBug, &QAction::triggered,
 
                     [](){QDesktopServices::openUrl(QUrl(BUG_REPORT_URL));});
 

	
 
    // File menu signals
 
    QObject::connect(ui->actionExportCsv, &QAction::triggered,
 
                     this, &MainWindow::onExportCsv);
 

	
 
    QObject::connect(ui->actionSaveSettings, &QAction::triggered,
 
                     this, &MainWindow::onSaveSettings);
 

	
 
    QObject::connect(ui->actionLoadSettings, &QAction::triggered,
 
                     this, &MainWindow::onLoadSettings);
 

	
 
    ui->actionQuit->setShortcutContext(Qt::ApplicationShortcut);
 

	
 
    QObject::connect(ui->actionQuit, &QAction::triggered,
 
                     this, &MainWindow::close);
 

	
 
    // port control signals
 
    QObject::connect(&portControl, &PortControl::portToggled,
 
                     this, &MainWindow::onPortToggled);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged,
 
            this, &MainWindow::onNumOfSamplesChanged);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::numOfSamplesChanged,
 
            plotMan, &PlotManager::onNumOfSamplesChanged);
 

	
 
    connect(&plotControlPanel, &PlotControlPanel::scaleChanged,
 
            plotMan, &PlotManager::setAxis);
 

	
 
    QObject::connect(ui->actionClear, SIGNAL(triggered(bool)),
 
                     this, SLOT(clearPlot()));
 

	
 
    QObject::connect(snapshotMan.takeSnapshotAction(), &QAction::triggered,
 
                     plotMan, &PlotManager::flashSnapshotOverlay);
 

	
 
    // init port signals
 
    QObject::connect(&(this->serialPort), SIGNAL(error(QSerialPort::SerialPortError)),
 
                     this, SLOT(onPortError(QSerialPort::SerialPortError)));
 

	
 
    // init data format and reader
 
    QObject::connect(&channelMan, &ChannelManager::dataAdded,
 
                     plotMan, &PlotManager::replot);
 

	
 
    QObject::connect(ui->actionPause, &QAction::triggered,
 
                     &channelMan, &ChannelManager::pause);
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordStarted,
 
                     &dataFormatPanel, &DataFormatPanel::startRecording);
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordStopped,
 
                     &dataFormatPanel, &DataFormatPanel::stopRecording);
 

	
 
    QObject::connect(ui->actionPause, &QAction::triggered,
 
                     [this](bool enabled)
 
                     {
 
                         if (enabled && !recordPanel.recordPaused())
 
                         {
 
                             dataFormatPanel.pause(true);
 
                         }
 
                         else
 
                         {
 
                             dataFormatPanel.pause(false);
 
                         }
 
                     });
 

	
 
    QObject::connect(&recordPanel, &RecordPanel::recordPausedChanged,
 
                     [this](bool enabled)
 
                     {
 
                         if (ui->actionPause->isChecked() && enabled)
 
                         {
 
                             dataFormatPanel.pause(false);
 
                         }
 
                     });
 

	
 
    connect(&serialPort, &QIODevice::aboutToClose,
 
            &recordPanel, &RecordPanel::onPortClose);
 

	
 
    // init data arrays and plot
 
    numOfSamples = plotControlPanel.numOfSamples();
 
    unsigned numOfChannels = dataFormatPanel.numOfChannels();
 

	
 
    channelMan.setNumOfSamples(numOfSamples);
 
    channelMan.setNumOfChannels(dataFormatPanel.numOfChannels());
 

	
 
    connect(&dataFormatPanel, &DataFormatPanel::numOfChannelsChanged,
 
            &channelMan, &ChannelManager::setNumOfChannels);
 

	
 
    connect(&channelMan, &ChannelManager::numOfChannelsChanged,
 
            this, &MainWindow::onNumOfChannelsChanged);
 

	
 
    plotControlPanel.setChannelInfoModel(channelMan.infoModel());
 

	
 
    // init curve list
 
    for (unsigned int i = 0; i < numOfChannels; i++)
 
    {
 
        plotMan->addCurve(channelMan.channelName(i), channelMan.channelBuffer(i));
 
    }
 

	
 
    // init auto scale
 
    plotMan->setAxis(plotControlPanel.autoScale(),
 
                     plotControlPanel.yMin(), plotControlPanel.yMax());
 

	
 
    // Init sps (sample per second) counter
 
    spsLabel.setText("0sps");
 
    spsLabel.setToolTip("samples per second (per channel)");
 
    ui->statusBar->addPermanentWidget(&spsLabel);
 
    QObject::connect(&dataFormatPanel,
 
                     &DataFormatPanel::samplesPerSecondChanged,
 
                     this, &MainWindow::onSpsChanged);
 

	
 
    // init demo
 
    QObject::connect(ui->actionDemoMode, &QAction::toggled,
 
                     this, &MainWindow::enableDemo);
 

	
 
    QObject::connect(ui->actionDemoMode, &QAction::toggled,
 
                     plotMan, &PlotManager::showDemoIndicator);
 

	
 
    // load default settings
 
    QSettings settings("serialplot", "serialplot");
 
    loadAllSettings(&settings);
 

	
 
    // ensure command panel has 1 command if none loaded
 
    if (!commandPanel.numOfCommands())
 
    {
 
        commandPanel.newCommandAction()->trigger();
 
    }
 

	
 
    // Important: This should be after newCommandAction is triggered
 
    // (above) we don't want user to be greeted with command panel on
 
    // the very first run.
 
    connect(commandPanel.newCommandAction(), &QAction::triggered, [this]()
 
            {
 
                this->ui->tabWidget->setCurrentWidget(&commandPanel);
 
            });
 
}
 

	
 
MainWindow::~MainWindow()
 
{
 
    // save settings
 
    QSettings settings("serialplot", "serialplot");
 
    saveAllSettings(&settings);
 

	
 
    if (serialPort.isOpen())
 
    {
 
        serialPort.close();
 
    }
 

	
 
    delete plotMan;
 

	
 
    delete ui;
 
    ui = NULL; // we check if ui is deleted in messageHandler
 
}
 

	
 
void MainWindow::closeEvent(QCloseEvent * event)
 
{
 
    if (!snapshotMan.isAllSaved())
 
    {
 
        auto clickedButton = QMessageBox::warning(
 
            this, "Closing SerialPlot",
 
            "There are un-saved snapshots. If you close you will loose the data.",
 
            QMessageBox::Discard | QMessageBox::Discard,
 
            QMessageBox::Cancel);
 
        if (clickedButton == QMessageBox::Cancel)
 
        {
 
            event->ignore();
 
            return;
 
        }
 
    }
 
    QMainWindow::closeEvent(event);
 
}
 

	
 
void MainWindow::setupAboutDialog()
 
{
 
    Ui_AboutDialog uiAboutDialog;
 
    uiAboutDialog.setupUi(&aboutDialog);
 

	
 
    QObject::connect(uiAboutDialog.pbAboutQt, &QPushButton::clicked,
 
                     [](){ QApplication::aboutQt();});
 

	
 
    QString aboutText = uiAboutDialog.lbAbout->text();
 
    aboutText.replace("$VERSION_STRING$", VERSION_STRING);
 
    aboutText.replace("$VERSION_REVISION$", VERSION_REVISION);
 
    uiAboutDialog.lbAbout->setText(aboutText);
 
}
 

	
 
void MainWindow::onPortToggled(bool open)
 
{
 
    // make sure demo mode is disabled
 
    if (open && isDemoRunning()) enableDemo(false);
 
    ui->actionDemoMode->setEnabled(!open);
 
}
 

	
 
void MainWindow::onPortError(QSerialPort::SerialPortError error)
 
{
 
    switch(error)
 
    {
 
        case QSerialPort::NoError :
 
            break;
 
        case QSerialPort::ResourceError :
 
            qWarning() << "Port error: resource unavaliable; most likely device removed.";
 
            if (serialPort.isOpen())
 
            {
 
                qWarning() << "Closing port on resource error: " << serialPort.portName();
 
                portControl.togglePort();
 
            }
 
            portControl.loadPortList();
 
            break;
 
        case QSerialPort::DeviceNotFoundError:
 
            qCritical() << "Device doesn't exists: " << serialPort.portName();
 
            break;
 
        case QSerialPort::PermissionError:
 
            qCritical() << "Permission denied. Either you don't have \
 
required privileges or device is already opened by another process.";
 
            break;
 
        case QSerialPort::OpenError:
 
            qWarning() << "Device is already opened!";
 
            break;
 
        case QSerialPort::NotOpenError:
 
            qCritical() << "Device is not open!";
 
            break;
 
        case QSerialPort::ParityError:
 
            qCritical() << "Parity error detected.";
 
            break;
 
        case QSerialPort::FramingError:
 
            qCritical() << "Framing error detected.";
 
            break;
 
        case QSerialPort::BreakConditionError:
 
            qCritical() << "Break condition is detected.";
 
            break;
 
        case QSerialPort::WriteError:
 
            qCritical() << "An error occurred while writing data.";
 
            break;
 
        case QSerialPort::ReadError:
 
            qCritical() << "An error occurred while reading data.";
 
            break;
 
        case QSerialPort::UnsupportedOperationError:
 
            qCritical() << "Operation is not supported.";
 
            break;
 
        case QSerialPort::TimeoutError:
 
            qCritical() << "A timeout error occurred.";
 
            break;
 
        case QSerialPort::UnknownError:
 
            qCritical() << "Unknown error! Error: " << serialPort.errorString();
 
            break;
 
        default:
 
            qCritical() << "Unhandled port error: " << error;
 
            break;
 
    }
 
}
 

	
 
void MainWindow::clearPlot()
 
{
 
    for (unsigned ci = 0; ci < channelMan.numOfChannels(); ci++)
 
    {
 
        channelMan.channelBuffer(ci)->clear();
 
    }
 
    plotMan->replot();
 
}
 

	
 
void MainWindow::onNumOfSamplesChanged(int value)
 
{
 
    numOfSamples = value;
 
    channelMan.setNumOfSamples(value);
 
    plotMan->replot();
 
}
 

	
 
void MainWindow::onNumOfChannelsChanged(unsigned value)
 
{
 
    unsigned int oldNum = plotMan->numOfCurves();
 
    unsigned numOfChannels = value;
 

	
 
    if (numOfChannels > oldNum)
 
    {
 
        // add new channels
 
        for (unsigned int i = oldNum; i < numOfChannels; i++)
 
        {
 
            plotMan->addCurve(channelMan.channelName(i), channelMan.channelBuffer(i));
 
        }
 
    }
 
    else if(numOfChannels < oldNum)
 
    {
 
        plotMan->removeCurves(oldNum - numOfChannels);
 
    }
 

	
 
    plotMan->replot();
 
}
 

	
 
void MainWindow::onSpsChanged(unsigned sps)
 
{
 
    spsLabel.setText(QString::number(sps) + "sps");
 
}
 

	
 
bool MainWindow::isDemoRunning()
 
{
 
    return ui->actionDemoMode->isChecked();
 
}
 

	
 
void MainWindow::enableDemo(bool enabled)
 
{
 
    if (enabled)
 
    {
 
        if (!serialPort.isOpen())
 
        {
 
            dataFormatPanel.enableDemo(true);
 
        }
 
        else
 
        {
 
            ui->actionDemoMode->setChecked(false);
 
        }
 
    }
 
    else
 
    {
 
        dataFormatPanel.enableDemo(false);
 
        ui->actionDemoMode->setChecked(false);
 
    }
 
}
 

	
 
void MainWindow::onExportCsv()
 
{
 
    bool wasPaused = ui->actionPause->isChecked();
 
    ui->actionPause->setChecked(true); // pause plotting
 

	
 
    QString fileName = QFileDialog::getSaveFileName(this, tr("Export CSV File"));
 

	
 
    if (fileName.isNull())  // user canceled export
 
    {
 
        ui->actionPause->setChecked(wasPaused);
 
    }
 
    else
 
    {
 
        Snapshot* snapshot = snapshotMan.makeSnapshot();
 
        snapshot->save(fileName);
 
        delete snapshot;
 
    }
 
}
 

	
 
void MainWindow::messageHandler(QtMsgType type,
 
                                const QMessageLogContext &context,
 
                                const QString &msg)
 
{
 
    QString logString;
 

	
 
    switch (type)
 
    {
 
#if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
 
        case QtInfoMsg:
 
            logString = "[Info] " + msg;
 
            break;
 
#endif
 
        case QtDebugMsg:
 
            logString = "[Debug] " + msg;
 
            break;
 
        case QtWarningMsg:
 
            logString = "[Warning] " + msg;
 
            break;
 
        case QtCriticalMsg:
 
            logString = "[Error] " + msg;
 
            break;
 
        case QtFatalMsg:
 
            logString = "[Fatal] " + msg;
 
            break;
 
    }
 

	
 
    if (ui != NULL) ui->ptLog->appendPlainText(logString);
 
    std::cerr << logString.toStdString() << std::endl;
 

	
 
    if (type != QtDebugMsg && ui != NULL)
 
    {
 
        ui->statusBar->showMessage(msg, 5000);
 
    }
 

	
 
    if (type == QtFatalMsg)
 
    {
 
        __builtin_trap();
 
    }
 
}
 

	
 
void MainWindow::saveAllSettings(QSettings* settings)
 
{
 
    saveMWSettings(settings);
 
    portControl.saveSettings(settings);
 
    dataFormatPanel.saveSettings(settings);
 
    channelMan.saveSettings(settings);
 
    plotControlPanel.saveSettings(settings);
 
    plotMan->saveSettings(settings);
 
    commandPanel.saveSettings(settings);
 
    recordPanel.saveSettings(settings);
 
}
 

	
 
void MainWindow::loadAllSettings(QSettings* settings)
 
{
 
    loadMWSettings(settings);
 
    portControl.loadSettings(settings);
 
    dataFormatPanel.loadSettings(settings);
 
    channelMan.loadSettings(settings);
 
    plotControlPanel.loadSettings(settings);
 
    plotMan->loadSettings(settings);
 
    commandPanel.loadSettings(settings);
 
    recordPanel.loadSettings(settings);
 
}
 

	
 
void MainWindow::saveMWSettings(QSettings* settings)
 
{
 
    // save window geometry
 
    settings->beginGroup(SettingGroup_MainWindow);
 
    settings->setValue(SG_MainWindow_Size, size());
 
    settings->setValue(SG_MainWindow_Pos, pos());
 
    // save active panel
 
    settings->setValue(SG_MainWindow_ActivePanel,
 
                       panelSettingMap.value(ui->tabWidget->currentIndex()));
 
    // save panel minimization
 
    settings->setValue(SG_MainWindow_HidePanels,
 
                       ui->tabWidget->hideAction.isChecked());
 
    // save window maximized state
 
    settings->setValue(SG_MainWindow_Maximized,
 
                       bool(windowState() & Qt::WindowMaximized));
 
    // save toolbar/dockwidgets state
 
    settings->setValue(SG_MainWindow_State, saveState());
 
    settings->endGroup();
 
}
 

	
 
void MainWindow::loadMWSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_MainWindow);
 
    // load window geometry
 
    resize(settings->value(SG_MainWindow_Size, size()).toSize());
 
    move(settings->value(SG_MainWindow_Pos, pos()).toPoint());
 

	
 
    // set active panel
 
    QString tabSetting =
 
        settings->value(SG_MainWindow_ActivePanel, QString()).toString();
 
    ui->tabWidget->setCurrentIndex(
 
        panelSettingMap.key(tabSetting, ui->tabWidget->currentIndex()));
 

	
 
    // hide panels
 
    ui->tabWidget->hideAction.setChecked(
 
        settings->value(SG_MainWindow_HidePanels,
 
                        ui->tabWidget->hideAction.isChecked()).toBool());
 

	
 
    // maximize window
 
    if (settings->value(SG_MainWindow_Maximized).toBool())
 
    {
 
        showMaximized();
 
    }
 

	
 
    // load toolbar/dockwidgets state
 
    restoreState(settings->value(SG_MainWindow_State).toByteArray());
 
    settings->setValue(SG_MainWindow_State, saveState());
 

	
 
    settings->endGroup();
 
}
 

	
 
void MainWindow::onSaveSettings()
 
{
 
    QString fileName = QFileDialog::getSaveFileName(
 
        this, tr("Save Settings"), QString(), "INI (*.ini)");
 

	
 
    if (!fileName.isNull()) // user canceled
 
    {
 
        QSettings settings(fileName, QSettings::IniFormat);
 
        saveAllSettings(&settings);
 
    }
 
}
 

	
 
void MainWindow::onLoadSettings()
 
{
 
    QString fileName = QFileDialog::getOpenFileName(
 
        this, tr("Load Settings"), QString(), "INI (*.ini)");
 

	
 
    if (!fileName.isNull()) // user canceled
 
    {
 
        QSettings settings(fileName, QSettings::IniFormat);
 
        loadAllSettings(&settings);
 
    }
 
}
src/recordpanel.cpp
Show inline comments
 
/*
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#include "recordpanel.h"
 
#include "ui_recordpanel.h"
 

	
 
#include <QIcon>
 
#include <QFile>
 
#include <QFileInfo>
 
#include <QMessageBox>
 
#include <QFileDialog>
 
#include <QRegularExpression>
 
#include <QtDebug>
 

	
 
#include <QtDebug>
 
#include "recordpanel.h"
 
#include "ui_recordpanel.h"
 
#include "setting_defines.h"
 

	
 
RecordPanel::RecordPanel(DataRecorder* recorder, ChannelManager* channelMan, QWidget *parent) :
 
    QWidget(parent),
 
    ui(new Ui::RecordPanel),
 
    recordToolBar(tr("Record Toolbar")),
 
    recordAction(QIcon::fromTheme("media-record"), tr("Record"), this)
 
{
 
    overwriteSelected = false;
 
    _recorder = recorder;
 
    _channelMan = channelMan;
 

	
 
    ui->setupUi(this);
 

	
 
    recordToolBar.setObjectName("tbRecord");
 

	
 
    recordAction.setCheckable(true);
 
    recordToolBar.addAction(&recordAction);
 
    ui->pbRecord->setDefaultAction(&recordAction);
 

	
 
    connect(ui->pbBrowse, &QPushButton::clicked,
 
            this, &RecordPanel::selectFile);
 
    connect(&recordAction, &QAction::triggered,
 
            this, &RecordPanel::onRecord);
 

	
 
    connect(ui->cbRecordPaused, SIGNAL(toggled(bool)),
 
            this, SIGNAL(recordPausedChanged(bool)));
 

	
 
    connect(ui->cbDisableBuffering, &QCheckBox::toggled,
 
            [this](bool enabled)
 
            {
 
                _recorder->disableBuffering = enabled;
 
            });
 
}
 

	
 
RecordPanel::~RecordPanel()
 
{
 
    delete ui;
 
}
 

	
 
QToolBar* RecordPanel::toolbar()
 
{
 
    return &recordToolBar;
 
}
 

	
 
bool RecordPanel::isRecording()
 
{
 
    return recordAction.isChecked();
 
}
 

	
 
bool RecordPanel::recordPaused()
 
{
 
    return ui->cbRecordPaused->isChecked();
 
}
 

	
 
bool RecordPanel::selectFile()
 
{
 
    QString fileName = QFileDialog::getSaveFileName(
 
        parentWidget(), tr("Select recording file"));
 

	
 
    if (fileName.isEmpty())
 
    {
 
        return false;
 
    }
 
    else
 
    {
 
        selectedFile = fileName;
 
        ui->lbFileName->setText(selectedFile);
 
        overwriteSelected = QFile::exists(fileName);
 
        return true;
 
    }
 
}
 

	
 
void RecordPanel::onRecord(bool start)
 
{
 
    if (!start)
 
    {
 
        stopRecording();
 
        return;
 
    }
 

	
 
    bool canceled = false;
 
    if (ui->leSeparator->text().isEmpty())
 
    {
 
        QMessageBox::critical(this, "Error",
 
                              "Column separator cannot be empty! Please select a separator.");
 
        ui->leSeparator->setFocus(Qt::OtherFocusReason);
 
        canceled = true;
 
    }
 

	
 
    // check file name
 
    if (!canceled && selectedFile.isEmpty() && !selectFile())
 
    {
 
        canceled = true;
 
    }
 

	
 
    if (!canceled && !overwriteSelected && QFile::exists(selectedFile))
 
    {
 
        if (ui->cbAutoIncrement->isChecked())
 
        {
 
            // TODO: should we increment even if user selected to replace?
 
            canceled = !incrementFileName();
 
        }
 
        else
 
        {
 
            canceled = !confirmOverwrite(selectedFile);
 
        }
 
    }
 

	
 
    if (canceled)
 
    {
 
        recordAction.setChecked(false);
 
    }
 
    else
 
    {
 
        overwriteSelected = false;
 
        startRecording();
 
    }
 
}
 

	
 
bool RecordPanel::incrementFileName(void)
 
{
 
    QFileInfo fileInfo(selectedFile);
 

	
 
    QString base = fileInfo.completeBaseName();
 
    QRegularExpression regex("(.*?)(\\d+)(?!.*\\d)(.*)");
 
    auto match = regex.match(base);
 

	
 
    if (match.hasMatch())
 
    {
 
        bool ok;
 
        int fileNum = match.captured(2).toInt(&ok);
 
        base = match.captured(1) + QString::number(fileNum + 1) + match.captured(3);
 
    }
 
    else
 
    {
 
        base += "_1";
 
    }
 

	
 
    QString suffix = fileInfo.suffix();;
 
    if (!suffix.isEmpty())
 
    {
 
        suffix = "." + suffix;
 
    }
 

	
 
    QString autoFileName = fileInfo.path() + "/" + base + suffix;
 

	
 
    // check if auto generated file name exists, ask user another name
 
    if (QFile::exists(autoFileName))
 
    {
 
        if (!confirmOverwrite(autoFileName))
 
        {
 
            return false;
 
        }
 
    }
 
    else
 
    {
 
        selectedFile = autoFileName;
 
    }
 

	
 
    ui->lbFileName->setText(selectedFile);
 
    return true;
 
}
 

	
 
bool RecordPanel::confirmOverwrite(QString fileName)
 
{
 
    // prepare message box
 
    QMessageBox mb(parentWidget());
 
    mb.setWindowTitle(tr("File Already Exists"));
 
    mb.setIcon(QMessageBox::Warning);
 
    mb.setText(tr("File (%1) already exists. How to continue?").arg(fileName));
 

	
 
    auto bCancel    = mb.addButton(QMessageBox::Cancel);
 
    auto bOverwrite = mb.addButton(tr("Overwrite"), QMessageBox::DestructiveRole);
 
    mb.addButton(tr("Select Another File"), QMessageBox::YesRole);
 

	
 
    mb.setEscapeButton(bCancel);
 

	
 
    // show message box
 
    mb.exec();
 

	
 
    if (mb.clickedButton() == bCancel)
 
    {
 
        return false;
 
    }
 
    else if (mb.clickedButton() == bOverwrite)
 
    {
 
        selectedFile = fileName;
 
        return true;
 
    }
 
    else                    // select button
 
    {
 
        return selectFile();
 
    }
 
}
 

	
 
void RecordPanel::startRecording(void)
 
{
 
    QStringList channelNames;
 
    if (ui->cbHeader->isChecked())
 
    {
 
        channelNames = _channelMan->infoModel()->channelNames();
 
    }
 
    _recorder->startRecording(selectedFile, getSeparator(), channelNames);
 
    emit recordStarted();
 
}
 

	
 
void RecordPanel::stopRecording(void)
 
{
 
    emit recordStopped();
 
    _recorder->stopRecording();
 
}
 

	
 
void RecordPanel::onPortClose()
 
{
 
    if (recordAction.isChecked() && ui->cbStopOnClose->isChecked())
 
    {
 
        stopRecording();
 
        recordAction.setChecked(false);
 
    }
 
}
 

	
 
QString RecordPanel::getSeparator() const
 
{
 
    QString sep = ui->leSeparator->text();
 
    sep.replace("\\t", "\t");
 
    return sep;
 
}
 

	
 
void RecordPanel::saveSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Record);
 
    settings->setValue(SG_Record_AutoIncrement, ui->cbAutoIncrement->isChecked());
 
    settings->setValue(SG_Record_RecordPaused, ui->cbRecordPaused->isChecked());
 
    settings->setValue(SG_Record_StopOnClose, ui->cbStopOnClose->isChecked());
 
    settings->setValue(SG_Record_Header, ui->cbHeader->isChecked());
 
    settings->setValue(SG_Record_DisableBuffering, ui->cbDisableBuffering->isChecked());
 
    settings->setValue(SG_Record_Separator, ui->leSeparator->text());
 
    settings->endGroup();
 
}
 

	
 
void RecordPanel::loadSettings(QSettings* settings)
 
{
 
    settings->beginGroup(SettingGroup_Record);
 
    ui->cbAutoIncrement->setChecked(
 
        settings->value(SG_Record_AutoIncrement, ui->cbAutoIncrement->isChecked()).toBool());
 
    ui->cbRecordPaused->setChecked(
 
        settings->value(SG_Record_RecordPaused, ui->cbRecordPaused->isChecked()).toBool());
 
    ui->cbStopOnClose->setChecked(
 
        settings->value(SG_Record_StopOnClose, ui->cbStopOnClose->isChecked()).toBool());
 
    ui->cbHeader->setChecked(
 
        settings->value(SG_Record_Header, ui->cbHeader->isChecked()).toBool());
 
    ui->cbDisableBuffering->setChecked(
 
        settings->value(SG_Record_DisableBuffering, ui->cbDisableBuffering->isChecked()).toBool());
 
    ui->leSeparator->setText(settings->value(SG_Record_Separator, ui->leSeparator->text()).toString());
 
    settings->endGroup();
 
}
src/recordpanel.h
Show inline comments
 
/*
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#ifndef RECORDPANEL_H
 
#define RECORDPANEL_H
 

	
 
#include <QWidget>
 
#include <QString>
 
#include <QToolBar>
 
#include <QAction>
 

	
 
#include "datarecorder.h"
 
#include "channelmanager.h"
 

	
 
namespace Ui {
 
class RecordPanel;
 
}
 

	
 
class RecordPanel : public QWidget
 
{
 
    Q_OBJECT
 

	
 
public:
 
    explicit RecordPanel(DataRecorder* recorder, ChannelManager* channelMan,
 
                         QWidget* parent = 0);
 
    ~RecordPanel();
 

	
 
    QToolBar* toolbar();
 

	
 
    bool isRecording();
 
    bool recordPaused();
 

	
 
    /// Stores settings into a `QSettings`
 
    void saveSettings(QSettings* settings);
 
    /// Loads settings from a `QSettings`.
 
    void loadSettings(QSettings* settings);
 

	
 
signals:
 
    void recordStarted();
 
    void recordStopped();
 
    void recordPausedChanged(bool enabled);
 

	
 
public slots:
 
    /// Must be called when port is closed
 
    void onPortClose();
 

	
 
private:
 
    Ui::RecordPanel *ui;
 
    QToolBar recordToolBar;
 
    QAction recordAction;
 
    QString selectedFile;
 
    bool overwriteSelected;
 
    DataRecorder* _recorder;
 
    ChannelManager* _channelMan;
 

	
 
    /**
 
     * @brief Increments the file name.
 
     *
 
     * If file name doesn't have a number at the end of it, a number is appended
 
     * with underscore starting from 1.
 
     *
 
     * @return false if user cancels
 
     */
 
    bool incrementFileName(void);
 

	
 
    /**
 
     * @brief Used to ask user confirmation if auto generated file
 
     * name exists.
 
     *
 
     * If user confirms overwrite, `selectedFile` is set to
 
     * `fileName`. User is also given option to select file and is
 
     * shown a file select dialog in this case.
 
     *
 
     * @param fileName auto generated file name.
 
     * @return false if user cancels
 
     */
 
    bool confirmOverwrite(QString fileName);
 

	
 
    void startRecording(void);
 
    void stopRecording(void);
 

	
 
    /// Returns separator text from ui. "\t" is converted to TAB
 
    /// character.
 
    QString getSeparator() const;
 

	
 
private slots:
 
    /**
 
     * @brief Opens up the file select dialog
 
     *
 
     * If you cancel the selection operation, currently selected file is not
 
     * changed.
 
     *
 
     * @return true if file selected, false if user cancels
 
     */
 
    bool selectFile();
 

	
 
    void onRecord(bool start);
 

	
 
};
 

	
 
#endif // RECORDPANEL_H
src/setting_defines.h
Show inline comments
 
/*
 
  Copyright © 2016 Hasan Yavuz Özderya
 
  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 <http://www.gnu.org/licenses/>.
 
*/
 

	
 
#ifndef SETTING_DEFINES_H
 
#define SETTING_DEFINES_H
 

	
 
const char SettingGroup_MainWindow[] = "MainWindow";
 
const char SettingGroup_Port[] = "Port";
 
const char SettingGroup_DataFormat[] = "DataFormat";
 
const char SettingGroup_Binary[] = "DataFormat_Binary";
 
const char SettingGroup_ASCII[] = "DataFormat_ASCII";
 
const char SettingGroup_CustomFrame[] = "DataFormat_CustomFrame";
 
const char SettingGroup_Channels[] = "Channels";
 
const char SettingGroup_Plot[] = "Plot";
 
const char SettingGroup_Commands[] = "Commands";
 
const char SettingGroup_Record[] = "Record";
 

	
 
// mainwindow setting keys
 
const char SG_MainWindow_Size[] = "size";
 
const char SG_MainWindow_Pos[] = "pos";
 
const char SG_MainWindow_ActivePanel[] = "activePanel";
 
const char SG_MainWindow_HidePanels[] = "hidePanels";
 
const char SG_MainWindow_Maximized[] = "maximized";
 
const char SG_MainWindow_State[] = "state";
 

	
 
// port setting keys
 
const char SG_Port_SelectedPort[] = "selectedPort";
 
const char SG_Port_BaudRate[] = "baudRate";
 
const char SG_Port_Parity[] = "parity";
 
const char SG_Port_DataBits[] = "dataBits";
 
const char SG_Port_StopBits[] = "stopBits";
 
const char SG_Port_FlowControl[] = "flowControl";
 

	
 
// data format panel keys
 
const char SG_DataFormat_Format[] = "format";
 

	
 
// binary stream reader keys
 
const char SG_Binary_NumOfChannels[] = "numOfChannels";
 
const char SG_Binary_NumberFormat[] = "numberFormat";
 
const char SG_Binary_Endianness[] = "endianness";
 

	
 
// ascii reader keys
 
const char SG_ASCII_NumOfChannels[] = "numOfChannels";
 

	
 
// framed reader keys
 
const char SG_CustomFrame_NumOfChannels[] = "numOfChannels";
 
const char SG_CustomFrame_FrameStart[] = "frameStart";
 
const char SG_CustomFrame_FixedSize[] = "fixedSize";
 
const char SG_CustomFrame_FrameSize[] = "frameSize";
 
const char SG_CustomFrame_NumberFormat[] = "numberFormat";
 
const char SG_CustomFrame_Endianness[] = "endianness";
 
const char SG_CustomFrame_Checksum[] = "checksum";
 
const char SG_CustomFrame_DebugMode[] = "debugMode";
 

	
 
// channel manager keys
 
const char SG_Channels_Channel[] = "channel";
 
const char SG_Channels_Name[] = "name";
 
const char SG_Channels_Color[] = "color";
 
const char SG_Channels_Visible[] = "visible";
 

	
 
// plot settings keys
 
const char SG_Plot_NumOfSamples[] = "numOfSamples";
 
const char SG_Plot_AutoScale[] = "autoScale";
 
const char SG_Plot_YMax[] = "yMax";
 
const char SG_Plot_YMin[] = "yMin";
 
const char SG_Plot_DarkBackground[] = "darkBackground";
 
const char SG_Plot_Grid[] = "grid";
 
const char SG_Plot_MinorGrid[] = "minorGrid";
 
const char SG_Plot_Legend[] = "legend";
 
const char SG_Plot_MultiPlot[] = "multiPlot";
 

	
 
// command setting keys
 
const char SG_Commands_Command[] = "command";
 
const char SG_Commands_Name[] = "name";
 
const char SG_Commands_Type[] = "type";
 
const char SG_Commands_Data[] = "data";
 

	
 
// record panel settings keys
 
const char SG_Record_AutoIncrement[]    = "autoIncrement";
 
const char SG_Record_RecordPaused[]     = "recordPaused";
 
const char SG_Record_StopOnClose[]      = "stopOnClose";
 
const char SG_Record_Header[]           = "header";
 
const char SG_Record_Separator[]        = "separator";
 
const char SG_Record_DisableBuffering[] = "disableBuffering";
 

	
 
#endif // SETTING_DEFINES_H
0 comments (0 inline, 0 general)