From cfab3681d7075e0545ffd1a9df449089e62b14cc Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Mal=C3=BD?= Date: Tue, 11 Mar 2014 23:22:16 +0100 Subject: [PATCH] Initial support for saving user data to JSON-formatted files --- Anyanka.pro | 6 ++- datafilesloader.h | 2 +- datamanager.cpp | 49 ++++++++++++++++++++++- datamanager.h | 1 + gui/mainwindow.cpp | 1 + gui/mainwindow.h | 2 + gui/mainwindow.ui | 7 ++++ integratedpeak.cpp | 94 ++++++++++++++++++++++++++++++++++++++++++-- integratedpeak.h | 46 ++++++++++++++++------ integrator.cpp | 7 ++++ integrator.h | 1 + jsonserializable.cpp | 5 +++ jsonserializable.h | 14 +++++++ main.cpp | 1 + signalcontroller.cpp | 47 +++++++++++++++++++++- signalcontroller.h | 7 +++- singlerundata.cpp | 3 +- singlerundata.h | 4 +- 18 files changed, 273 insertions(+), 24 deletions(-) create mode 100644 jsonserializable.cpp create mode 100644 jsonserializable.h diff --git a/Anyanka.pro b/Anyanka.pro index 0833939..23bccc1 100644 --- a/Anyanka.pro +++ b/Anyanka.pro @@ -52,7 +52,8 @@ SOURCES += main.cpp\ csvdatawriterbackend.cpp \ gui/exportgraphtoimagedialog.cpp \ imagedrawer.cpp \ - signaldrawer.cpp + signaldrawer.cpp \ + jsonserializable.cpp HEADERS += \ datafilesloader.h \ @@ -86,7 +87,8 @@ HEADERS += \ gui/exportgraphtoimagedialog.h \ imagedrawer.h \ signaldrawer.h \ - enumflags.h + enumflags.h \ + jsonserializable.h FORMS += \ gui/mainwindow.ui \ diff --git a/datafilesloader.h b/datafilesloader.h index 7baff76..513141c 100644 --- a/datafilesloader.h +++ b/datafilesloader.h @@ -39,7 +39,7 @@ public: E_FATAL }; - explicit DataFilesLoader(QObject* parent = 0); + explicit DataFilesLoader(QObject* parent = nullptr); ReturnCode loadSingleRun(const QDir& path, std::vector>& dataFiles); private: diff --git a/datamanager.cpp b/datamanager.cpp index 11b9ae5..0e4061e 100644 --- a/datamanager.cpp +++ b/datamanager.cpp @@ -25,6 +25,7 @@ #include "logger.h" #include "gui/exportgraphtoimagedialog.h" #include "gui/exportrawdatadialog.h" +#include #include #include @@ -314,6 +315,25 @@ std::shared_ptr DataManager::loadSingleRun(QDir& dir) _c = std::shared_ptr(new SignalController(_s)); + /* Try to read saved user data from JSON file */ + QFile jsonFile(dir.path() + "/" + "ank_" + QString::fromStdString(_s->descriptiveName()) + ".json"); + if (jsonFile.exists()) { + if (jsonFile.open(QFile::ReadOnly)) { + QByteArray bytes = jsonFile.readAll(); + QJsonDocument jsonDoc = QJsonDocument::fromJson(bytes); + QJsonObject jso = jsonDoc.object(); + + if (!_c->deserialize(jso)) { + llog->log(Logger::Level::WARNING, ME_SENDER_STR, "Failed to deserialize saved user data for " + QString::fromStdString(_s->descriptiveName())); + QMessageBox::warning(nullptr, "Load single run", "Saved user data for signal " + QString::fromStdString(_s->descriptiveName()) + + "could not have been read properly."); + } + } else + llog->log(Logger::Level::WARNING, ME_SENDER_STR, "Cannot open saved user data file " + QString::fromStdString(_s->descriptiveName()) + + " for reading."); + } else + llog->log(Logger::Level::DEBUG, ME_SENDER_STR, "No user data file for " + QString::fromStdString(_s->descriptiveName())); + sigs[_s->descriptiveName()] = _s; ctrls[_s->descriptiveName()] = _c; } @@ -356,7 +376,7 @@ std::shared_ptr DataManager::loadSingleRun(QDir& dir) } singleRun = std::shared_ptr(new SingleRunData(methodNames.at(0), operatorNames.at(0), sampleInfos.at(0), dates.at(0), times.at(0), sigs, ctrls, - dir.dirName(), this)); + dir.dirName(), dir.absolutePath(), this)); Logger::log(Logger::DEBUG, ME_SENDER_STR, "Single run successfully parsed"); @@ -555,6 +575,33 @@ void DataManager::onLoadSingleRun(const QString& str) emit setActiveSequenceIndex(0); } +void DataManager::onSaveChanges() +{ + std::shared_ptr sr = m_activeSequence->selectedRun(); + std::unordered_map>::const_iterator cit; + + if (m_activeSequence == nullptr) + return; + + const std::unordered_map>& ctrls = sr->allControllers(); + cit = ctrls.cbegin(); + + while (cit != ctrls.cend()) { + QJsonValue val = cit->second->serialize(); + QJsonDocument doc(val.toObject()); + QFile f(sr->absPath() + "/" + "ank_" + QString::fromStdString(cit->first) + ".json"); + QByteArray bytes = doc.toJson(); + + f.open(QFile::WriteOnly); + f.write(bytes.data(), bytes.size()); + + qDebug() << val; + f.close(); + ++cit; + } + +} + void DataManager::onSequenceSelected(const QString& key) { if (m_sequenceRejected) { diff --git a/datamanager.h b/datamanager.h index 1e5bc3c..0775feb 100644 --- a/datamanager.h +++ b/datamanager.h @@ -95,6 +95,7 @@ public slots: void onExportRawData(); void onLoadSequence(const QString& dir); void onLoadSingleRun(const QString& dir); + void onSaveChanges(); void onSequenceSelected(const QString& key); void onSingleRunSelected(const QString& key); diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp index 43fd34f..68d6d7f 100644 --- a/gui/mainwindow.cpp +++ b/gui/mainwindow.cpp @@ -70,6 +70,7 @@ void MainWindow::connectActions() /* DATA menu */ connect(ui->actionLoad_single_run, SIGNAL(triggered()), this, SLOT(onLoadSingleRun())); connect(ui->actionLoad_sequence, SIGNAL(triggered()), this, SLOT(onLoadSequence())); + connect(ui->actionSave_changes, SIGNAL(triggered()), this, SLOT(onSaveChanges())); /* EXPORT menu */ connect(ui->actionRaw_values, SIGNAL(triggered()), this, SLOT(onExportRawData())); connect(ui->actionIntegration_results, SIGNAL(triggered()), this, SLOT(onExportPeaks())); diff --git a/gui/mainwindow.h b/gui/mainwindow.h index 1e9d391..f181d2d 100644 --- a/gui/mainwindow.h +++ b/gui/mainwindow.h @@ -76,6 +76,7 @@ private slots: void onIntegrateSelected(); void onLoadSequence(); void onLoadSingleRun(); + void onSaveChanges() { emit saveChanges(); } void onSequenceSelected(const QString& str); void onSingleRunSelected(const QString& str); void onSWFullSizeToggle(); @@ -88,6 +89,7 @@ signals: void exportRawData(); void loadSequence(const QString& dir); void loadSingleRun(const QString& dir); + void saveChanges(); void sequenceSelected(const QString& str); void singleRunSelected(const QString& str); }; diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui index f63e23a..2748af2 100644 --- a/gui/mainwindow.ui +++ b/gui/mainwindow.ui @@ -215,6 +215,8 @@ + + @@ -270,6 +272,11 @@ Graph as image + + + Save changes + + diff --git a/integratedpeak.cpp b/integratedpeak.cpp index 64ae1da..2644491 100644 --- a/integratedpeak.cpp +++ b/integratedpeak.cpp @@ -21,17 +21,31 @@ */ #include "integratedpeak.h" +#include -IntegratedPeak::IntegratedPeak(IntegratedPeak::Type type, double auc, size_t peakIdx, double peakTime, double peakVal, +const QString IntegratedPeak::KEY_AUC("auc"); +const QString IntegratedPeak::KEY_FROMIDX("fromIdx"); +const QString IntegratedPeak::KEY_FROMTIME("fromTime"); +const QString IntegratedPeak::KEY_FROMY("fromY"); +const QString IntegratedPeak::KEY_PEAKIDX("peakIdx"); +const QString IntegratedPeak::KEY_PEAKTIME("peakTime"); +const QString IntegratedPeak::KEY_PEAKY("peakY"); +const QString IntegratedPeak::KEY_TOIDX("toIdx"); +const QString IntegratedPeak::KEY_TOTIME("toTime"); +const QString IntegratedPeak::KEY_TOY("toY"); +const QString IntegratedPeak::KEY_TYPE("type"); + +IntegratedPeak::IntegratedPeak(IntegratedPeak::Type type, double auc, size_t peakIdx, double peakTime, double peakY, size_t fromIdx, size_t toIdx, double fromTime, double toTime, double fromY, double toY, QObject* parent) : QObject(parent), + JSONSerializable(), m_auc(auc), m_fromIdx(fromIdx), m_fromTime(fromTime), m_fromY(fromY), m_peakIdx(peakIdx), m_peakTime(peakTime), - m_peakVal(peakVal), + m_peakY(peakY), m_toIdx(toIdx), m_toTime(toTime), m_toY(toY), @@ -44,7 +58,81 @@ double IntegratedPeak::baselineSlope() const return (m_toY - m_fromY) / (m_toIdx - m_fromIdx); } +bool IntegratedPeak::deserialize(const QJsonValue& value) +{ + QJsonObject jso; + QString type; + + if (!value.isObject()) + return false; + + jso = value.toObject(); + + if (!jso.contains(KEY_AUC)) + return false; + m_auc = jso.value(KEY_AUC).toDouble(); + if (!jso.contains(KEY_FROMIDX)) + return false; + m_fromIdx = jso.value(KEY_FROMIDX).toDouble(); + if (!jso.contains(KEY_FROMTIME)) + return false; + m_fromTime = jso.value(KEY_FROMTIME).toDouble(); + if (!jso.contains(KEY_FROMY)) + return false; + m_fromY = jso.value(KEY_FROMY).toDouble(); + + if (!jso.contains(KEY_PEAKIDX)) + return false; + m_peakIdx = jso.value(KEY_PEAKIDX).toDouble(); + if (!jso.contains(KEY_PEAKTIME)) + return false; + m_peakTime = jso.value(KEY_PEAKTIME).toDouble(); + if (!jso.contains(KEY_PEAKY)) + return false; + m_peakY = jso.value(KEY_PEAKY).toDouble(); + + if (!jso.contains(KEY_TOIDX)) + return false; + m_toIdx = jso.value(KEY_TOIDX).toDouble(); + if (!jso.contains(KEY_TOTIME)) + return false; + m_toTime = jso.value(KEY_TOTIME).toDouble(); + if (!jso.contains(KEY_TOY)) + return false; + m_toY = jso.value(KEY_TOY).toDouble(); + + if (!jso.contains(KEY_TYPE)) + return false; + type = jso.value(KEY_TYPE).toString(); + if (type.compare("PEAK") == 0) + m_type = Type::PEAK; + else + m_type = Type::VALLEY; + + return true; +} + double IntegratedPeak::height() const { - return fabs(m_peakVal - (baselineSlope() * (m_peakIdx - m_fromIdx) + m_fromY)); + return fabs(m_peakY - (baselineSlope() * (m_peakIdx - m_fromIdx) + m_fromY)); +} + +QJsonValue IntegratedPeak::serialize() const +{ + QJsonObject jso; + QString type = (m_type == Type::PEAK) ? "PEAK" : "VALLEY"; + + jso.insert(KEY_AUC, QJsonValue(m_auc)); + jso.insert(KEY_FROMIDX, QJsonValue(static_cast(m_fromIdx))); + jso.insert(KEY_FROMTIME, QJsonValue(m_fromTime)); + jso.insert(KEY_FROMY, QJsonValue(m_fromY)); + jso.insert(KEY_PEAKIDX, QJsonValue(static_cast(m_peakIdx))); + jso.insert(KEY_PEAKTIME, QJsonValue(m_peakTime)); + jso.insert(KEY_PEAKY, QJsonValue(m_peakY)); + jso.insert(KEY_TOIDX, QJsonValue(static_cast(m_toIdx))); + jso.insert(KEY_TOTIME, QJsonValue(m_toTime)); + jso.insert(KEY_TOY, QJsonValue(m_toY)); + jso.insert(KEY_TYPE, QJsonValue(type)); + + return jso; } diff --git a/integratedpeak.h b/integratedpeak.h index 7db458d..89228e3 100644 --- a/integratedpeak.h +++ b/integratedpeak.h @@ -24,18 +24,21 @@ #ifndef INTEGRATEDPEAK_H #define INTEGRATEDPEAK_H +#include "jsonserializable.h" #include -class IntegratedPeak : public QObject +class IntegratedPeak : public QObject, public JSONSerializable { Q_OBJECT public: enum class Type { + INVALID, PEAK, VALLEY }; - explicit IntegratedPeak(Type type, double auc, size_t peakIdx, double peakTime, double peakVal, size_t fromIdx, size_t toIdx, double fromTime, double toTime, + explicit IntegratedPeak() : m_type(Type::INVALID) {} + explicit IntegratedPeak(Type type, double auc, size_t peakIdx, double peakTime, double peakY, size_t fromIdx, size_t toIdx, double fromTime, double toTime, double fromY, double toY, QObject* parent = nullptr); double auc() const { return m_auc; } double baselineSlope() const; @@ -45,24 +48,41 @@ public: double height() const; size_t peakIdx() const { return m_peakIdx; } double peakTime() const { return m_peakTime; } + double peakY() const { return m_peakY; } size_t toIdx() const { return m_toIdx; } double toTime() const { return m_toTime; } double toY() const { return m_toY; } Type type() const { return m_type; } double width() const { return m_toTime - m_fromTime; } + virtual bool deserialize(const QJsonValue& value); + virtual QJsonValue serialize() const; + private: - const double m_auc; - const size_t m_fromIdx; - const double m_fromTime; - const double m_fromY; - const size_t m_peakIdx; - const double m_peakTime; - const double m_peakVal; - const size_t m_toIdx; - const double m_toTime; - const double m_toY; - const Type m_type; + double m_auc; + size_t m_fromIdx; + double m_fromTime; + double m_fromY; + size_t m_peakIdx; + double m_peakTime; + double m_peakY; + size_t m_toIdx; + double m_toTime; + double m_toY; + Type m_type; + + static const QString KEY_AUC; + static const QString KEY_FROMIDX; + static const QString KEY_FROMTIME; + static const QString KEY_FROMY; + static const QString KEY_PEAKIDX; + static const QString KEY_PEAKTIME; + static const QString KEY_PEAKY; + static const QString KEY_TOIDX; + static const QString KEY_TOTIME; + static const QString KEY_TOY; + static const QString KEY_TYPE; + public slots: signals: diff --git a/integrator.cpp b/integrator.cpp index 6c37612..6aa8ed0 100644 --- a/integrator.cpp +++ b/integrator.cpp @@ -32,6 +32,13 @@ Integrator::Integrator(std::shared_ptr signal, QObject* parent) : m_signal(signal) {} +void Integrator::addPeak(std::shared_ptr peak) +{ + emit addingPeak(m_peaksList.count()); + m_peaksList.append(peak); + emit peakAdded(); +} + std::shared_ptr Integrator::deletePeak(size_t idx) { size_t ctr = 0; diff --git a/integrator.h b/integrator.h index 6522da2..b170f69 100644 --- a/integrator.h +++ b/integrator.h @@ -43,6 +43,7 @@ public: }; explicit Integrator(std::shared_ptr signal, QObject* parent = nullptr); + void addPeak(std::shared_ptr peak); std::shared_ptr deletePeak(size_t idx); std::vector> allPeaks(); ReturnCode integrate(size_t startIdx, size_t stopIdx, double startY, double stopY, std::shared_ptr& peak); diff --git a/jsonserializable.cpp b/jsonserializable.cpp new file mode 100644 index 0000000..9105edc --- /dev/null +++ b/jsonserializable.cpp @@ -0,0 +1,5 @@ +#include "jsonserializable.h" + +JSONSerializable::JSONSerializable() +{ +} diff --git a/jsonserializable.h b/jsonserializable.h new file mode 100644 index 0000000..1a40121 --- /dev/null +++ b/jsonserializable.h @@ -0,0 +1,14 @@ +#ifndef JSONSERIALIZABLE_H +#define JSONSERIALIZABLE_H + +#include + +class JSONSerializable +{ +public: + JSONSerializable(); + virtual QJsonValue serialize() const = 0; + virtual bool deserialize(const QJsonValue& value) = 0; +}; + +#endif // JSONSERIALIZABLE_H diff --git a/main.cpp b/main.cpp index 3d0bfaa..fe29bd1 100644 --- a/main.cpp +++ b/main.cpp @@ -72,6 +72,7 @@ int main(int argc, char *argv[]) QObject::connect(mWin.get(), SIGNAL(exportGraphToImage()),dMgr.get(), SLOT(onExportGraphToImage())); QObject::connect(mWin.get(), SIGNAL(exportPeaks()), dMgr.get(), SLOT(onExportPeaks())); QObject::connect(mWin.get(), SIGNAL(exportRawData()), dMgr.get(), SLOT(onExportRawData())); + QObject::connect(mWin.get(), SIGNAL(saveChanges()), dMgr.get(), SLOT(onSaveChanges())); 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))); diff --git a/signalcontroller.cpp b/signalcontroller.cpp index 88f685c..ce2fd6d 100644 --- a/signalcontroller.cpp +++ b/signalcontroller.cpp @@ -24,15 +24,19 @@ #include "logger.h" #include #include - +#include +#include #include const double SignalController::RELATIVE_MAX = 100.0; const double SignalController::RELATIVE_MIN = 0.0; const QString SignalController::ME_SENDER_STR("SignalController"); +const QString SignalController::KEY_PEAKS("peaks"); + SignalController::SignalController(std::shared_ptr signal, QObject* parent) : QObject(parent), + JSONSerializable(), m_signal(signal) { m_integrator = std::shared_ptr(new Integrator(signal)); @@ -66,6 +70,33 @@ PeakDrawData SignalController::deletePeak(const double x) return genPeakDrawData(peak); } +bool SignalController::deserialize(const QJsonValue& value) +{ + QJsonObject jso; + QJsonArray jsPeakArray; + + if (!value.isObject()) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, "JSON value is notan object"); + return false; + } + jso = value.toObject(); + + /* Add peaks */ + if (!jso.contains(KEY_PEAKS)) { + Logger::log(Logger::Level::WARNING, ME_SENDER_STR, "JSON object does not contain \"peaks\" array"); + return false; + } + jsPeakArray = jso.value(KEY_PEAKS).toArray(); + for (QJsonValueRef ref : jsPeakArray) { + std::shared_ptr peak(new IntegratedPeak()); + + peak->deserialize(ref); + m_integrator->addPeak(peak); + } + + return true; +} + std::shared_ptr SignalController::getGraphDrawData(const double fromX, const double toX) { size_t fromIdx, toIdx; @@ -215,6 +246,20 @@ PeakDrawData SignalController::integratePeak(const double fromX, const double fr return PeakDrawData(); } +QJsonValue SignalController::serialize() const +{ + QJsonObject jso; + QJsonArray jsPeakArray; + + for (std::shared_ptr peak : m_integrator->allPeaks()) { + QJsonValue val = peak->serialize(); + jsPeakArray.append(val); + } + + jso.insert("peaks", jsPeakArray); + return jso; +} + /** Private methods **/ PeakDrawData SignalController::genPeakDrawData(const std::shared_ptr peak) diff --git a/signalcontroller.h b/signalcontroller.h index 139ba9c..e4b9b3d 100644 --- a/signalcontroller.h +++ b/signalcontroller.h @@ -26,6 +26,7 @@ #include "integrationtablemodel.h" #include "integrator.h" +#include "jsonserializable.h" #include "signal.h" #include "signaldatatablemodel.h" #include @@ -82,7 +83,7 @@ struct RulerDrawData { const double relStep; }; -class SignalController : public QObject +class SignalController : public QObject, public JSONSerializable { Q_OBJECT public: @@ -92,6 +93,7 @@ public: ~SignalController(); SignalDataTableModel* dataTableModel() { return m_dtblModel; } PeakDrawData deletePeak(const double x); + virtual bool deserialize(const QJsonValue& value); Signal::TimeValuePair getXYValues(const double x); std::shared_ptr getGraphDrawData(const double fromX, const double toX); std::vector getPeaksDrawData(const double fromX, const double fromY, const double toX, const double toY); @@ -104,6 +106,7 @@ public: PeakDrawData integratePeak(const double fromX, const double fromY, const double toX, const double toY); IntegrationTableModel* integrationTableModel() { return m_integTblModel;} void setGUIViewport(const double fromX, const double fromY, const double toX, const double toY); + virtual QJsonValue serialize() const; const std::shared_ptr signal() const { return m_signal; } const std::shared_ptr cIntegrator() const { return m_integrator; } @@ -131,6 +134,8 @@ private: static const QString ME_SENDER_STR; + static const QString KEY_PEAKS; + public slots: signals: diff --git a/singlerundata.cpp b/singlerundata.cpp index 4c70828..0b39ff8 100644 --- a/singlerundata.cpp +++ b/singlerundata.cpp @@ -24,8 +24,9 @@ SingleRunData::SingleRunData(const QString& methodName, const QString& operatorName, const QString& sampleInfo, const QDate date, const QTime time, std::unordered_map>& sigs, std::unordered_map>& ctrls, - const QString& dirname, QObject *parent) : + const QString& dirname, const QString& absPath, QObject *parent) : QObject(parent), + m_absPath(absPath), m_date(date), m_dirName(dirname), m_methodName(methodName), diff --git a/singlerundata.h b/singlerundata.h index c3d96cb..b0f1c1d 100644 --- a/singlerundata.h +++ b/singlerundata.h @@ -37,7 +37,8 @@ class SingleRunData : public QObject public: explicit SingleRunData(const QString& methodName, const QString& operatorName, const QString& sampleInfo, const QDate date, const QTime time, std::unordered_map>& sigs, std::unordered_map>& ctrls, - const QString& dirname, QObject* parent = nullptr); + const QString& dirname, const QString& absPath, QObject* parent = nullptr); + QString absPath() const { return m_absPath; } const std::unordered_map>& allControllers() const { return m_ctrls; } std::vector allKeys() const; const std::unordered_map>& allSignals() const { return m_signals; } @@ -52,6 +53,7 @@ public: QTime time() const { return m_time; } private: + const QString m_absPath; const QDate m_date; const QString m_dirName; const QString m_methodName; -- 2.43.5