integrator.cpp \
integrationtablemodel.cpp \
gui/aboutanyanka.cpp \
- globalinfo.cpp
+ globalinfo.cpp \
+ gui/exportrawdatadialog.cpp \
+ rawdataexporter.cpp \
+ rawdatawriter.cpp \
+ csvrawdatawriter.cpp
HEADERS += \
datafilesloader.h \
integrationtablemodel.h \
gui/aboutanyanka.h \
globalinfo.h \
- windows_defines.h
+ windows_defines.h \
+ gui/exportrawdatadialog.h \
+ rawdataexporter.h \
+ rawdatawriter.h \
+ csvrawdatawriter.h
FORMS += \
gui/mainwindow.ui \
gui/signalview.ui \
- gui/aboutanyanka.ui
+ gui/aboutanyanka.ui \
+ gui/exportrawdatadialog.ui
RESOURCES += \
imgresources.qrc
--- /dev/null
+#include "csvrawdatawriter.h"
+
+CSVRawDataWriter::CSVRawDataWriter(QObject* parent) :
+ RawDataWriter("csv", parent)
+{
+ locale = QLocale::system();
+}
+
+QString CSVRawDataWriter::filename(const QString& name, const QString& extra) const
+{
+ QString newname = name;
+ if (extra.compare("") != 0)
+ return newname + "_" + extra + "." + FILE_EXTENSION;
+ else
+ return newname + "." + FILE_EXTENSION;
+}
+
+QString CSVRawDataWriter::line(const double time, const double value) const
+{
+ return locale.toString(time) + ";" + locale.toString(value);
+}
--- /dev/null
+#ifndef CSVRAWDATAWRITER_H
+#define CSVRAWDATAWRITER_H
+
+#include "rawdatawriter.h"
+#include <QtCore/QLocale>
+
+class CSVRawDataWriter : public RawDataWriter
+{
+public:
+ CSVRawDataWriter(QObject* parent = nullptr);
+ QString filename(const QString& name, const QString& extra) const;
+ QString line(const double time, const double value) const;
+
+private:
+ QLocale locale;
+};
+
+#endif // CSVRAWDATAWRITER_H
#include "datamanager.h"
#include "logger.h"
+#include "rawdataexporter.h"
+#include "gui/exportrawdatadialog.h"
#include <QtWidgets/QMessageBox>
#include <QDebug>
/* Public slots */
+void DataManager::onExportRawData()
+{
+ RawDataExporter exporter(this);
+ ExportRawDataDialog dlg(RawDataExporter::SUPPORTED_FORMATS);
+ std::vector<std::shared_ptr<Signal>> sigs;
+
+ if (m_activeSequence == nullptr)
+ return;
+ if (m_activeSequence->selectedRun() == nullptr)
+ return;
+
+ for (int idx = 0; idx < m_activeSequence->selectedRun()->signalCount(); idx++)
+ dlg.addAvailableSignal(m_activeSequence->selectedRun()->signalAt(idx)->descriptiveName());
+
+ RawDataExporter::ReturnCode ret;
+ do {
+ int dret = dlg.exec();
+ if (dret == QDialog::Rejected)
+ return;
+ else if (dret == QDialog::Accepted && dlg.selectedSignalsCount() < 1) {
+ QMessageBox::information(nullptr, "Data export error", "No signals selected");
+ ret = RawDataExporter::ReturnCode::E_TRYAGAIN;
+ } else if (dret == QDialog::Accepted && dlg.selectedSignalsCount() >= 1) {
+ sigs.clear();
+ for (int idx = 0; idx < dlg.signalsCount(); idx++) {
+ if (dlg.isSignalSelected(idx))
+ sigs.push_back(m_activeSequence->selectedRun()->signalAt(idx));
+ }
+ ret = exporter.exportData(sigs, dlg.destination(), dlg.format());
+ }
+ } while (ret == RawDataExporter::ReturnCode::E_TRYAGAIN);
+}
+
void DataManager::onLoadSequence(const QString& dir)
{
QDir rootDir(dir);
void setSingleRunInfo(const QString& method, const QString& operatorname, const QString& sample, const QString& datetime);
public slots:
+ void onExportRawData();
void onLoadSequence(const QString& dir);
void onLoadSingleRun(const QString& dir);
void onSequenceSelected(const QString& key);
--- /dev/null
+#include "exportrawdatadialog.h"
+#include "ui_exportrawdatadialog.h"
+#include <QtWidgets/QFileDialog>
+
+ExportRawDataDialog::ExportRawDataDialog(const QStringList& supportedFormats, QWidget* parent) :
+ QDialog(parent),
+ m_supportedFormats(supportedFormats),
+ ui(new Ui::ExportRawDataDialog)
+{
+ ui->setupUi(this);
+
+ ui->qcbox_formats->addItems(supportedFormats);
+
+ connect(ui->qpb_browse, SIGNAL(clicked()), this, SLOT(onBrowseClicked()));
+ connect(ui->qpb_ok, SIGNAL(clicked()), this, SLOT(accept()));
+ connect(ui->qpb_cancel, SIGNAL(clicked()), this, SLOT(reject()));
+}
+
+/* Public functions */
+
+void ExportRawDataDialog::addAvailableSignal(const QString& name)
+{
+ QCheckBox* cbox = new QCheckBox(name, this);
+ m_signalCheckboxes.push_back(cbox);
+ ui->form_availSigs->addWidget(cbox);
+}
+
+QString ExportRawDataDialog::destination() const
+{
+ return ui->qle_destPath->text();
+}
+
+int ExportRawDataDialog::format() const
+{
+ return ui->qcbox_formats->currentIndex();
+}
+
+bool ExportRawDataDialog::isSignalSelected(int idx) const
+{
+ if (idx >= m_signalCheckboxes.size())
+ return false;
+ else
+ return m_signalCheckboxes.at(idx)->isChecked();
+}
+
+int ExportRawDataDialog::selectedSignalsCount() const
+{
+ int cnt = 0;
+ for (const QCheckBox* cbox : m_signalCheckboxes) {
+ if (cbox->isChecked())
+ cnt++;
+ }
+ return cnt;
+}
+
+/* Private slots */
+
+void ExportRawDataDialog::onBrowseClicked()
+{
+ QFileDialog fdlg(this, "Pick destination", QString());
+ if (fdlg.exec() == QDialog::Accepted)
+ ui->qle_destPath->setText(fdlg.selectedFiles()[0]);
+}
+
+ExportRawDataDialog::~ExportRawDataDialog()
+{
+ delete ui;
+}
--- /dev/null
+#ifndef EXPORTRAWDATADIALOG_H
+#define EXPORTRAWDATADIALOG_H
+
+#include <QtWidgets/QCheckBox>
+#include <QtWidgets/QDialog>
+#include <QtWidgets/QLabel>
+
+namespace Ui {
+ class ExportRawDataDialog;
+}
+
+class ExportRawDataDialog : public QDialog
+{
+ Q_OBJECT
+
+public:
+ explicit ExportRawDataDialog(const QStringList& supportedFormats, QWidget* parent = nullptr);
+ ~ExportRawDataDialog();
+ void addAvailableSignal(const QString& name);
+ QString destination() const;
+ int format() const;
+ bool isSignalSelected(int idx) const;
+ int selectedSignalsCount() const;
+ int signalsCount() const { return m_signalCheckboxes.size(); }
+
+private:
+ std::vector<QCheckBox*> m_signalCheckboxes;
+ QStringList m_supportedFormats;
+ Ui::ExportRawDataDialog* ui;
+
+private slots:
+ void onBrowseClicked();
+
+};
+
+#endif // EXPORTRAWDATADIALOG_H
--- /dev/null
+<?xml version="1.0" encoding="UTF-8"?>
+<ui version="4.0">
+ <class>ExportRawDataDialog</class>
+ <widget class="QDialog" name="ExportRawDataDialog">
+ <property name="geometry">
+ <rect>
+ <x>0</x>
+ <y>0</y>
+ <width>400</width>
+ <height>300</height>
+ </rect>
+ </property>
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="windowTitle">
+ <string>Export raw data</string>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout">
+ <item>
+ <layout class="QGridLayout" name="gridLayout">
+ <item row="0" column="1">
+ <widget class="QPushButton" name="qpb_browse">
+ <property name="text">
+ <string>Browse</string>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="1">
+ <widget class="QComboBox" name="qcbox_formats">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Fixed" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="minimumSize">
+ <size>
+ <width>100</width>
+ <height>0</height>
+ </size>
+ </property>
+ <property name="maximumSize">
+ <size>
+ <width>100</width>
+ <height>16777215</height>
+ </size>
+ </property>
+ </widget>
+ </item>
+ <item row="1" column="0">
+ <widget class="QLabel" name="ql_formatCap">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="Minimum">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Format:</string>
+ </property>
+ </widget>
+ </item>
+ <item row="0" column="0">
+ <widget class="QLineEdit" name="qle_destPath">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Expanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ <item>
+ <widget class="QWidget" name="widget" native="true">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="Preferred" vsizetype="MinimumExpanding">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <layout class="QVBoxLayout" name="verticalLayout_2">
+ <item>
+ <widget class="QLabel" name="ql_availSigsCap">
+ <property name="sizePolicy">
+ <sizepolicy hsizetype="MinimumExpanding" vsizetype="Fixed">
+ <horstretch>0</horstretch>
+ <verstretch>0</verstretch>
+ </sizepolicy>
+ </property>
+ <property name="text">
+ <string>Available signals:</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <layout class="QFormLayout" name="form_availSigs"/>
+ </item>
+ </layout>
+ </widget>
+ </item>
+ <item>
+ <layout class="QHBoxLayout" name="horizontalLayout">
+ <item>
+ <spacer name="horizontalSpacer">
+ <property name="orientation">
+ <enum>Qt::Horizontal</enum>
+ </property>
+ <property name="sizeHint" stdset="0">
+ <size>
+ <width>40</width>
+ <height>20</height>
+ </size>
+ </property>
+ </spacer>
+ </item>
+ <item>
+ <widget class="QPushButton" name="qpb_ok">
+ <property name="text">
+ <string>OK</string>
+ </property>
+ </widget>
+ </item>
+ <item>
+ <widget class="QPushButton" name="qpb_cancel">
+ <property name="text">
+ <string>Cancel</string>
+ </property>
+ </widget>
+ </item>
+ </layout>
+ </item>
+ </layout>
+ </widget>
+ <resources/>
+ <connections/>
+</ui>
/* Controls panel */
connect(ui->qpb_integrate, SIGNAL(pressed()), this, SLOT(onIntegrateSelected()));
connect(ui->qpb_zoom, SIGNAL(pressed()), this, SLOT(onZoomSelected()));
+
+ /* DATA menu */
connect(ui->actionLoad_single_run, SIGNAL(triggered()), this, SLOT(onLoadSingleRun()));
connect(ui->actionLoad_sequence, SIGNAL(triggered()), this, SLOT(onLoadSequence()));
+ /* EXPORT menu */
+ connect(ui->actionRaw_values, SIGNAL(triggered()), this, SLOT(onExportRawData()));
+ /* HELP menu */
connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(onAboutAnyanka()));
+ /* RUN/SEQ cboxes*/
connect(ui->qcbox_sequence, SIGNAL(currentIndexChanged(QString)), this, SLOT(onSequenceSelected(QString)));
connect(ui->qcbox_singleRun, SIGNAL(currentIndexChanged(QString)), this, SLOT(onSingleRunSelected(QString)));
}
private slots:
void onAboutAnyanka();
+ void onExportRawData() { emit exportRawData(); }
void onIntegrateSelected();
void onLoadSequence();
void onLoadSingleRun();
signals:
void controlModeChanged(GraphControlModes mode);
+ void exportRawData();
void loadSequence(const QString& dir);
void loadSingleRun(const QString& dir);
void sequenceSelected(const QString& str);
</property>
<addaction name="actionAbout"/>
</widget>
+ <widget class="QMenu" name="menuExport">
+ <property name="title">
+ <string>Export</string>
+ </property>
+ <addaction name="actionRaw_values"/>
+ <addaction name="actionIntegration_results"/>
+ <addaction name="separator"/>
+ <addaction name="actionGraph_as_image"/>
+ </widget>
<addaction name="menuData"/>
+ <addaction name="menuExport"/>
<addaction name="menuHelp"/>
</widget>
<widget class="QStatusBar" name="statusbar">
<string>About</string>
</property>
</action>
+ <action name="actionRaw_values">
+ <property name="text">
+ <string>Raw data</string>
+ </property>
+ </action>
+ <action name="actionIntegration_results">
+ <property name="text">
+ <string>Integration results</string>
+ </property>
+ </action>
+ <action name="actionGraph_as_image">
+ <property name="text">
+ <string>Graph as image</string>
+ </property>
+ </action>
</widget>
<resources/>
<connections/>
QObject::connect(dMgr.get(), SIGNAL(setActiveSequenceIndex(int)), mWin.get(), SLOT(onSetActiveSequenceIndex(int)));
QObject::connect(dMgr.get(), SIGNAL(setActiveSingleRunIndex(int)), mWin.get(), SLOT(onSetActiveSingleRunIndex(int)));
QObject::connect(dMgr.get(), SIGNAL(setSingleRunInfo(QString,QString,QString,QString)), mWin.get(), SLOT(onSetSingleRunInfo(QString,QString,QString,QString)));
+ QObject::connect(mWin.get(), SIGNAL(exportRawData()), dMgr.get(), SLOT(onExportRawData()));
QObject::connect(mWin.get(), SIGNAL(loadSingleRun(QString)), dMgr.get(), SLOT(onLoadSingleRun(QString)));
QObject::connect(mWin.get(), SIGNAL(loadSequence(QString)), dMgr.get(), SLOT(onLoadSequence(QString)));
QObject::connect(mWin.get(), SIGNAL(sequenceSelected(QString)), dMgr.get(), SLOT(onSequenceSelected(QString)));
--- /dev/null
+#include "rawdataexporter.h"
+#include "csvrawdatawriter.h"
+#include <QtCore/QDir>
+#include <QtCore/QLocale>
+#include <QtWidgets/QMessageBox>
+
+const QStringList RawDataExporter::SUPPORTED_FORMATS = QStringList() << "Comma separated values (CSV)";
+
+RawDataExporter::RawDataExporter(QObject* parent) :
+ QObject(parent)
+{
+}
+
+/* Public methods */
+
+RawDataExporter::ReturnCode RawDataExporter::exportData(const std::vector<std::shared_ptr<Signal>> sigs, const QString& path, const int formatIdx)
+{
+ QDir dir(path);
+ QFile file(path);
+ QString plainName = file.fileName();
+ OutputFormats format;
+ RawDataExporter::ReturnCode ret;
+ RawDataWriter* writer;
+
+ if (formatIdx < 0 || formatIdx >= SUPPORTED_FORMATS.length()) {
+ QMessageBox::warning(nullptr, "Data export error", "Invalid format selected");
+ return ReturnCode::E_TRYAGAIN;
+ }
+
+ if (path.compare("") == 0) {
+ QMessageBox::information(nullptr, "Data export error", "No output file specified.");
+ return ReturnCode::E_TRYAGAIN;
+ }
+ if (dir.exists()) {
+ QMessageBox::information(nullptr, "Data export error", "Selected path points to a directory. Name of output file is required.");
+ return ReturnCode::E_TRYAGAIN;
+ }
+
+ if (file.exists()) {
+ int ret = QMessageBox::question(nullptr, "Data export", "The selected file already exists. Do you wish to overwrite it?");
+ if (ret == QMessageBox::No)
+ return ReturnCode::E_TRYAGAIN;
+ else
+ file.remove();
+ }
+
+ format = formatIdxToFormat(formatIdx);
+ switch (format) {
+ case OutputFormats::CSV:
+ writer = new CSVRawDataWriter(this);
+ break;
+ }
+
+ if (sigs.size() > 1)
+ file.setFileName(writer->filename(plainName, sigs.at(0)->descriptiveName()));
+ else
+ file.setFileName(writer->filename(plainName));
+
+ /* Export first signal to file */
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ QMessageBox::critical(nullptr, "Data export error", "Cannot open output file for writing.");
+ delete writer;
+ return ReturnCode::E_FAILED;
+ }
+
+ ret = writeToFile(file, writer, sigs.at(0));
+ if (ret != ReturnCode::SUCCESS) {
+ file.close();
+ delete writer;
+ return ret;
+ }
+
+ for (int idx = 1; idx < sigs.size(); idx++) {
+ file.setFileName(writer->filename(plainName, sigs.at(idx)->descriptiveName()));
+ if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
+ QMessageBox::critical(nullptr, "Data export error", "Cannot open output file '" + file.fileName() + "' for writing.");
+ return ReturnCode::E_FAILED;
+ }
+ ret = writeToFile(file, writer, sigs.at(idx));
+ file.close();
+ if (ret != ReturnCode::SUCCESS)
+ break;
+ }
+
+ delete writer;
+ return ret;
+}
+
+/* Private methods */
+
+RawDataExporter::OutputFormats RawDataExporter::formatIdxToFormat(const int idx)
+{
+ switch (idx) {
+ case 0:
+ return OutputFormats::CSV;
+ }
+}
+
+RawDataExporter::ReturnCode RawDataExporter::writeToFile(QFile& file, const RawDataWriter* writer, const std::shared_ptr<Signal> signal)
+{
+ QString line;
+ QByteArray bytes;
+ qint64 w;
+
+ bytes = QString("time;value\n").toUtf8();
+ w = file.write(bytes);
+ if (w < bytes.size())
+ return ReturnCode::E_FAILED;
+
+ for (int idx = 0; idx < signal->valuesCount(); idx++) {
+ line = writer->line(signal->timeAt(idx), signal->valueAt(idx));
+ bytes = line.toUtf8();
+ w = file.write(bytes);
+ if (w < bytes.size())
+ return ReturnCode::E_FAILED;
+ }
+
+ return ReturnCode::SUCCESS;
+}
--- /dev/null
+#ifndef RAWDATAEXPORTER_H
+#define RAWDATAEXPORTER_H
+
+#include "rawdatawriter.h"
+#include "signal.h"
+#include <memory>
+#include <QtCore/QFile>
+#include <QtCore/QObject>
+
+class RawDataExporter : public QObject
+{
+ Q_OBJECT
+public:
+ enum class ReturnCode {
+ SUCCESS,
+ E_TRYAGAIN,
+ E_FAILED
+ };
+
+ explicit RawDataExporter(QObject* parent = nullptr);
+ ReturnCode exportData(std::vector<std::shared_ptr<Signal>> sigs, const QString& path, const int formatIdx);
+
+ static const QStringList SUPPORTED_FORMATS;
+private:
+ enum class OutputFormats {
+ CSV
+ };
+
+ OutputFormats formatIdxToFormat(const int idx);
+ QString toCSVLine(double time, double value);
+ ReturnCode writeToFile(QFile& file, const RawDataWriter* writer, const std::shared_ptr<Signal> signal);
+
+signals:
+
+public slots:
+
+};
+
+#endif // RAWDATAEXPORTER_H
--- /dev/null
+#include "rawdatawriter.h"
+
+RawDataWriter::RawDataWriter(const QString& extension, QObject* parent) :
+ QObject(parent),
+ FILE_EXTENSION(extension)
+{
+}
--- /dev/null
+#ifndef RAWDATAWRITER_H
+#define RAWDATAWRITER_H
+
+#include <QtCore/QObject>
+
+class RawDataWriter : public QObject
+{
+ Q_OBJECT
+public:
+ explicit RawDataWriter(const QString& extension, QObject* parent = nullptr);
+ virtual QString filename(const QString& name, const QString& extra = QString()) const = 0;
+ virtual QString line(const double time, const double value) const = 0;
+
+ const QString FILE_EXTENSION;
+
+signals:
+
+public slots:
+
+};
+
+#endif // RAWDATAWRITER_H
Sequence::Sequence(std::vector<SingleRunPair>& singleRuns, QObject* parent) :
QObject(parent),
m_selectedRunKey(""),
+ m_selectedRunIdx(-1),
m_singleRuns(singleRuns)
{
}
m_singleRuns.erase(m_singleRuns.begin()+idx);
}
+std::shared_ptr<SingleRunData> Sequence::selectedRun()
+{
+ if (m_selectedRunKey.compare("") == 0 || m_selectedRunIdx == -1)
+ return nullptr;
+
+ return m_singleRuns.at(m_selectedRunIdx).second;
+}
+
int Sequence::selectedRunIdx() const
{
int idx = 0;
void remove(const QString& key);
void removeAt(const size_t idx);
const QString& selectedRunKey() const { return m_selectedRunKey; }
+ std::shared_ptr<SingleRunData> selectedRun();
int selectedRunIdx() const;
int singleRunToIdx(const QString& key) const;
- void setSelectedRunKey(const QString& key) { m_selectedRunKey = key; }
+ void setSelectedRunKey(const QString& key) { m_selectedRunKey = key; m_selectedRunIdx = singleRunToIdx(key); }
private:
QString m_selectedRunKey;
+ int m_selectedRunIdx;
std::unordered_map<std::string, std::shared_ptr<SignalController>> m_signalCtrls;
std::vector<SingleRunPair> m_singleRuns;
}
}
+QString Signal::descriptiveName() const
+{
+ QString name = resourceToString();
+ if (m_resource == Resource::CE_DAD)
+ name += "_SIG" + QString::number(m_wavelengthAbs) + "_REF" + QString::number(m_wavelengthRef);
+ return name;
+}
+
std::vector<Signal::TimeValuePair>::const_iterator Signal::iteratorFrom(const size_t idx) const
{
return m_values.cbegin()+idx-1;
explicit Signal(const Equipment equipment, const Resource resource, const double samplingRate, const YUnit yunit, const uint16_t wavelengthAbs,
const uint16_t wavelengthRef, const std::vector<TimeValuePair>& values, QObject* parent = nullptr);
+ QString descriptiveName() const;
Equipment equipment() const { return m_equipment; }
std::vector<TimeValuePair>::const_iterator iteratorFrom(const size_t idx) const;
double maximum() const { return m_max; }