From: Michal Malý Date: Sun, 17 Nov 2013 01:44:08 +0000 (+0100) Subject: Initial commit X-Git-Tag: 0.1b~9 X-Git-Url: https://gitweb.devoid-pointer.net/?a=commitdiff_plain;h=db5357ea0e9648669b20bccd4c7c69b1cf2cac2f;p=anyanka.git Initial commit --- db5357ea0e9648669b20bccd4c7c69b1cf2cac2f diff --git a/Anyanka.pro b/Anyanka.pro new file mode 100644 index 0000000..779d7e0 --- /dev/null +++ b/Anyanka.pro @@ -0,0 +1,78 @@ +#------------------------------------------------- +# +# Project created by QtCreator 2013-10-28T18:00:16 +# +#------------------------------------------------- + +QT += core gui +QT -= qml_debug + +greaterThan(QT_MAJOR_VERSION, 4): QT += widgets + +TARGET = Anyanka +TEMPLATE = app + +unix { + LIBS += -ldl + QMAKE_CXXFLAGS += -std=c++11 -Wall +} + +INCLUDEPATH += libAGRE +# Add project root directory to work around VC11 include path scheme +INCLUDEPATH += "./" + +SOURCES += main.cpp\ + datafilesloader.cpp \ + agreinterface.cpp \ + datamanager.cpp \ + signal.cpp \ + singlerundata.cpp \ + gui/mainwindow.cpp \ + logger.cpp \ + gui/signalview.cpp \ + locallogger.cpp \ + gui/graphview.cpp \ + signalcontroller.cpp \ + integratedpeak.cpp \ + sequence.cpp \ + sequenceselectormodel.cpp \ + singlerunsselectormodel.cpp \ + signaldatatablemodel.cpp \ + gui/graphviewcontextmenu.cpp \ + integrator.cpp \ + integrationtablemodel.cpp \ + gui/aboutanyanka.cpp \ + globalinfo.cpp + +HEADERS += \ + datafilesloader.h \ + agreinterface.h \ + datamanager.h \ + signal.h \ + singlerundata.h \ + gui/mainwindow.h \ + logger.h \ + gui/signalview.h \ + locallogger.h \ + gui/graphview.h \ + signalcontroller.h \ + metatypes.h \ + mathhelpers.h \ + integratedpeak.h \ + sequence.h \ + sequenceselectormodel.h \ + singlerunsselectormodel.h \ + signaldatatablemodel.h \ + gui/graphviewcontextmenu.h \ + integrator.h \ + integrationtablemodel.h \ + gui/aboutanyanka.h \ + globalinfo.h + +FORMS += \ + gui/mainwindow.ui \ + gui/signalview.ui \ + gui/aboutanyanka.ui + +RESOURCES += \ + imgresources.qrc diff --git a/agreinterface.cpp b/agreinterface.cpp new file mode 100644 index 0000000..b022a97 --- /dev/null +++ b/agreinterface.cpp @@ -0,0 +1,145 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "agreinterface.h" +#include "logger.h" +#include +#ifdef Q_OS_UNIX +#include +#elif defined Q_OS_WIN32 +#include +#endif + +#ifdef Q_OS_UNIX +#define __LIBRARY_NAME "libAGRE.so" +#elif defined Q_OS_WIN +#define __LIBRARY_NAME "libAGRE.dll" +#endif + +#ifdef Q_OS_UNIX +IAGRE_Reader* (*AGREInterface::m_create_reader)(bool)(nullptr); +#elif defined Q_OS_WIN +IAGRE_Reader* (__cdecl* AGREInterface::m_create_reader)(bool)(NULL); +#endif +AGREInterface* AGREInterface::m_instance(nullptr); + +const QString AGREInterface::AGRE_LIBRARY_NAME(__LIBRARY_NAME); +const QString AGREInterface::ME_SENDER_STR("AGREInterface"); + +/* Public static methods */ + +AGREInterface* AGREInterface::instance() +{ + if (m_instance == nullptr) + m_instance = new AGREInterface(); + return m_instance; +} + +AGREInterface::AGREInterface(QObject* parent) : + QObject(parent), + m_inited(false), + m_lastAGREError(AGRE_ReturnCode::SUCCESS), + m_readerIFace(nullptr) +{ +} + +AGREInterface::ReturnCode AGREInterface::initialize() +{ + if (m_inited) + return ReturnCode::SUCCESS; + +#ifdef Q_OS_UNIX + m_libagre_handle = dlopen(__LIBRARY_NAME, RTLD_LAZY); + if (m_libagre_handle == nullptr) { +#elif defined Q_OS_WIN + m_libagre_handle = LoadLibrary(TEXT(__LIBRARY_NAME)); + if (m_libagre_handle == NULL) { +#endif + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Library " + AGRE_LIBRARY_NAME + " could not have been loaded."); + return ReturnCode::E_NO_LIBRARY; + } + +#ifdef Q_OS_UNIX + m_create_reader = (IAGRE_Reader* (*)(bool))dlsym(m_libagre_handle, "create_agre_reader"); + if (m_create_reader == nullptr) { +#elif defined Q_OS_WIN + m_create_reader = (IAGRE_Reader* (__cdecl*)(bool))GetProcAddress(static_cast(m_libagre_handle), "create_agre_reader"); + if (m_create_reader == NULL) { +#endif + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "create_agre_reader symbol could not have been resloved."); + return ReturnCode::E_CANNOT_RESOLVE; + } + + + m_readerIFace = m_create_reader(true); + if (m_readerIFace == nullptr) { + m_create_reader = nullptr; +#ifdef Q_OS_UNIX + dlclose(m_libagre_handle); +#elif defined Q_OS_WIN + FreeLibrary(static_cast(m_libagre_handle)); +#endif + return ReturnCode::E_CANNOT_GET_IFACE; + } + + m_inited = true; + return ReturnCode::SUCCESS; +} + +/* ENDOF public static methods */ + +/* Public methods */ + +const std::vector* AGREInterface::debugAGREInfo() +{ + if (!m_inited) + return nullptr; + + return &m_readerIFace->debug_info_list(); +} + +AGREInterface::ReturnCode AGREInterface::readFile(const std::string& fileName, std::shared_ptr& minfo) +{ + AGRE_ReturnCode ret; + + if (!m_inited) + return ReturnCode::E_NOT_INITED; + ret = m_readerIFace->read_file(fileName, minfo); + if (ret != AGRE_ReturnCode::SUCCESS) { + m_lastAGREError = ret; + return ReturnCode::E_CANNOT_READ_FILE; + } + + return ReturnCode::SUCCESS; +} + +AGREInterface::~AGREInterface() +{ + m_inited = false; + + m_readerIFace->destroy_agre_reader(); +#ifdef Q_OS_UNIX + dlclose(m_libagre_handle); +#elif defined Q_OS_WIN + FreeLibrary(static_cast(m_libagre_handle)); +#endif +} diff --git a/agreinterface.h b/agreinterface.h new file mode 100644 index 0000000..75d899c --- /dev/null +++ b/agreinterface.h @@ -0,0 +1,77 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#ifndef AGREINTERFACE_H +#define AGREINTERFACE_H + +#include +#include + +class AGREInterface : public QObject +{ + Q_OBJECT + Q_DISABLE_COPY(AGREInterface) + +public: + enum class ReturnCode { + SUCCESS, + E_NO_LIBRARY, + E_CANNOT_RESOLVE, + E_CANNOT_GET_IFACE, + E_NOT_INITED, + E_CANNOT_READ_FILE + }; + + const std::vector* debugAGREInfo(); + AGRE_ReturnCode lastAGREError() { AGRE_ReturnCode r = m_lastAGREError; m_lastAGREError = AGRE_ReturnCode::SUCCESS; return r; } + ReturnCode initialize(); + static AGREInterface* instance(); + ReturnCode readFile(const std::string& fileName, std::shared_ptr& minfo); + + static const QString AGRE_LIBRARY_NAME; + static const QString ME_SENDER_STR; + +private: + explicit AGREInterface(QObject* parent = 0); + ~AGREInterface(); + + bool m_inited; + AGRE_ReturnCode m_lastAGREError; + IAGRE_Reader* m_readerIFace; + +#ifdef Q_OS_UNIX + static IAGRE_Reader* (*m_create_reader)(bool collect_debug); +#elif defined Q_OS_WIN + static IAGRE_Reader* (__cdecl* m_create_reader)(bool collect_debug); +#endif + void* m_libagre_handle; + + static AGREInterface* m_instance; + +signals: + +public slots: + +}; + +#endif // AGREINTERFACE_H diff --git a/datafilesloader.cpp b/datafilesloader.cpp new file mode 100644 index 0000000..61a9a88 --- /dev/null +++ b/datafilesloader.cpp @@ -0,0 +1,89 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "datafilesloader.h" +#include "logger.h" +#include +#include + +const QString DataFilesLoader::ME_SENDER_STR("DataFilesLoader"); + +DataFilesLoader::DataFilesLoader(QObject *parent) : + QObject(parent) +{ + m_supportedFileTypes << "*.ch"; +} + +DataFilesLoader::ReturnCode DataFilesLoader::loadSingleRun(const QDir& path, std::vector>& dataFiles) +{ + for (const QString& s : path.entryList(m_supportedFileTypes, QDir::Files | QDir::NoDotAndDotDot)) { + std::shared_ptr minfo = nullptr; + QString absPath = path.absoluteFilePath(s); + + AGREInterface::ReturnCode ret = AGREInterface::instance()->readFile(absPath.toStdString(), minfo); + if (ret == AGREInterface::ReturnCode::SUCCESS) + dataFiles.push_back(minfo); + else if (ret == AGREInterface::ReturnCode::E_NOT_INITED) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "AGREInterface has not been initialized."); + QMessageBox::critical(nullptr, "AGREInterface error", "AGRE interface has not been initialized. This should not happen!\n" + "Application cannot continue."); + exit(1); + } else { + QString errDesc = errorToString(AGREInterface::instance()->lastAGREError()); + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "Error reading file '" + s + "'" + errDesc); + const std::vector* const dbgMsgs = AGREInterface::instance()->debugAGREInfo(); + if (dbgMsgs == nullptr) + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Null pointer to debug messages list. This should not happen!\n"); + else { + for (const std::string& s : *dbgMsgs) + Logger::log(Logger::Level::INFO, ME_SENDER_STR, QString("AGRE debug:") + QString(s.c_str())); + } + QMessageBox::warning(nullptr, "Error reading file '" + s + "'", errDesc); + return ReturnCode::E_READ_ERROR; + } + } + + if (dataFiles.size() == 0) + return ReturnCode::E_NO_FILES; + + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, QString::number(dataFiles.size()) + " files were found and successfully parsed"); + + return ReturnCode::SUCCESS; +} + +QString DataFilesLoader::errorToString(const AGRE_ReturnCode errorCode) +{ + switch (errorCode) { + case AGRE_ReturnCode::E_CANNOT_OPEN_FILE: + return "Cannot open file."; + case AGRE_ReturnCode::E_CORRUPTED_FILE: + return "File is corrupted."; + case AGRE_ReturnCode::E_INVALID_FILE: + return "File does not appear to be a ChemStation data file."; + case AGRE_ReturnCode::E_OUT_OF_RANGE: + return "File is too short to be a ChemStation data file."; + case AGRE_ReturnCode::E_READ_FAILED: + return "An error occured during reading."; + default: + return "Unspecified error " + QString::number(static_cast(errorCode)); + } +} diff --git a/datafilesloader.h b/datafilesloader.h new file mode 100644 index 0000000..a9b1c62 --- /dev/null +++ b/datafilesloader.h @@ -0,0 +1,57 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef DATAFILESLOADER_H +#define DATAFILESLOADER_H + +#include "agreinterface.h" +#include +#include + +class DataFilesLoader : public QObject +{ + Q_OBJECT +public: + enum class ReturnCode { + SUCCESS, + E_NO_FILES, + E_READ_ERROR, + E_FATAL + }; + + explicit DataFilesLoader(QObject* parent = 0); + ReturnCode loadSingleRun(const QDir& path, std::vector>& dataFiles); + +private: + QStringList m_supportedFileTypes; + + QString errorToString(const AGRE_ReturnCode errorCode); + + static const QString ME_SENDER_STR; + +signals: + +public slots: + +}; + +#endif // DATAFILESLOADER_H diff --git a/datamanager.cpp b/datamanager.cpp new file mode 100644 index 0000000..58d7201 --- /dev/null +++ b/datamanager.cpp @@ -0,0 +1,426 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "datamanager.h" +#include "logger.h" +#include + +#include + +const QString DataManager::ME_SENDER_STR("DataManager"); +const char DataManager::UNIT_KILOVOLTS_TEXT[] = {0x4B, 0x56, 0x00}; +const char DataManager::UNIT_MICROAMPERES_TEXT[] = {static_cast(0xB5), 0x41, 0x00}; +const char DataManager::UNIT_MILLIAU_TEXT[] = {0x6D, 0x41, 0x55, 0x00}; +const char DataManager::UNIT_MILLIVOLTS_TEXT[] = {0x6D, 0x56, 0x00}; +const char DataManager::UNIT_WATTS_TEXT[] = {0x57, 0x00}; +const QStringList DataManager::DATA_DIR_SUFFIX = { "*.D" }; + +DataManager::DataManager(QObject* parent) : + QObject(parent), + m_sequenceRejected(false) +{ + m_sequences.push_back(std::make_pair>("Single runs", std::shared_ptr(new Sequence()))); + m_seqSelModel.setSequences(&m_sequences); + m_sigNameCodec = QTextCodec::codecForName("ISO-8859-1"); +} + +std::shared_ptr DataManager::sequenceByKey(const QString& key) +{ + for (NameSequencePair p : m_sequences) { + if (p.first.compare(key) == 0) + return p.second; + } + return nullptr; +} + +int DataManager::sequenceIdxByKey(const QString& key) +{ + int idx = 0; + for (NameSequencePair p : m_sequences) { + qDebug() << p.first << key; + if (p.first.compare(key) == 0) + return idx; + idx++; + } + return -1; +} + +Signal::Equipment DataManager::equipmentFromFiletype(AGRE_Filetype filetype) +{ + switch (filetype) { + case AGRE_Filetype::CE_CCD: + case AGRE_Filetype::CE_CURRENT: + case AGRE_Filetype::CE_DAD: + case AGRE_Filetype::CE_POWER: + case AGRE_Filetype::CE_VOLTAGE: + return Signal::Equipment::CE; + default: + throw std::invalid_argument("Invalid type of file"); + } +} + +Signal::Resource DataManager::resourceFromFiletype(AGRE_Filetype filetype) +{ + switch (filetype) { + case AGRE_Filetype::CE_CCD: + return Signal::Resource::CE_CCD; + case AGRE_Filetype::CE_CURRENT: + return Signal::Resource::CE_CURRENT; + case AGRE_Filetype::CE_DAD: + return Signal::Resource::CE_DAD; + case AGRE_Filetype::CE_POWER: + return Signal::Resource::CE_POWER; + case AGRE_Filetype::CE_VOLTAGE: + return Signal::Resource::CE_VOLTAGE; + default: + throw std::invalid_argument("Invalid type of signal"); + } +} + +std::string DataManager::signalControllerKey(const QString& main, const QString& resource) +{ + return std::string(main.toStdString() + "_" + resource.toStdString()); +} + +Signal::YUnit DataManager::yunitFromUnitStr(const std::string& unit_str) +{ + static const QString LATIN_KILOVOLTS = QString::fromLatin1(DataManager::UNIT_KILOVOLTS_TEXT); + static const QString LATIN_MICROAMPERS = QString::fromLatin1(DataManager::UNIT_MICROAMPERES_TEXT); + static const QString LATIN_MILLIAU = QString::fromLatin1(DataManager::UNIT_MILLIAU_TEXT); + static const QString LATIN_MILLIVOLTS = QString::fromLatin1(DataManager::UNIT_MILLIVOLTS_TEXT); + static const QString LATIN_WATTS = QString::fromLatin1(DataManager::UNIT_WATTS_TEXT); + QString str = m_sigNameCodec->toUnicode(unit_str.c_str()); + + if (QString::compare(str, LATIN_MILLIVOLTS) == 0) + return Signal::YUnit::MILLIVOLTS; + else if (QString::compare(str, LATIN_KILOVOLTS) == 0) + return Signal::YUnit::KILOVOLTS; + else if (QString::compare(str, LATIN_MICROAMPERS) == 0) + return Signal::YUnit::MICROAMPERES; + else if (QString::compare(str, LATIN_MILLIAU) == 0) + return Signal::YUnit::MILLIAU; + else if (QString::compare(str, LATIN_WATTS) == 0) + return Signal::YUnit::WATTS; + else + throw std::invalid_argument(str.toStdString()); +} + +std::shared_ptr DataManager::loadSingleRun(QDir& dir) +{ + std::vector> dataFiles; + std::shared_ptr singleRun; + std::vector> sigs; + DataFilesLoader::ReturnCode ret; + LocalLoggerPtr llog = Logger::createLocalLog(); + llog->setPrintLevel(Logger::Level::WARNING); + + ret = m_dfl.loadSingleRun(dir, dataFiles); + if (ret == DataFilesLoader::ReturnCode::E_NO_FILES) { + QMessageBox::warning(nullptr, ME_SENDER_STR, "Directory " + dir.path() + " does not contain any ChemStation data files."); + return nullptr; + } + if (ret != DataFilesLoader::ReturnCode::SUCCESS) { + return nullptr; + } + + QStringList operatorNames; + QStringList methodNames; + QStringList sampleInfos; + QList dates; + QList times; + for (std::shared_ptr mi : dataFiles) { + Signal::Equipment eqp; + Signal::Resource res; + double samplingRate; + Signal::YUnit yu; + QDate date(mi->year, mi->month, mi->day); + QTime time(mi->hour, mi->minute, mi->second); + QString operatorName; + QString methodName; + QString sampleInfo; + + operatorName = QString::fromStdString(mi->operator_name); + methodName = QString::fromStdString(mi->method_name); + sampleInfo = QString::fromStdString(mi->sample_info); + + try { + eqp = equipmentFromFiletype(mi->file_type); + } catch (std::invalid_argument& ia) { + llog->log(Logger::ERROR, ME_SENDER_STR, ia.what()); + continue; + } + try { + res = resourceFromFiletype(mi->file_type); + } catch (std::invalid_argument& ia) { + llog->log(Logger::ERROR, ME_SENDER_STR, ia.what()); + continue; + } + try { + yu = yunitFromUnitStr(mi->y_units); + } catch (std::invalid_argument& ia) { + llog->log(Logger::ERROR, ME_SENDER_STR, QString("Invalid units ") + ia.what()); + continue; + } + samplingRate = mi->sampling_rate; + + /* Fill values */ + std::vector values; + for (const AGRE_TimeValuePair& tvp : mi->val_tbl) { + Signal::TimeValuePair stvp(tvp.first, tvp.second); + values.push_back(stvp); + } + std::shared_ptr _s(new Signal(eqp, res, samplingRate, yu, mi->dad_wavelength_abs, mi->dad_wavelength_ref, values)); + sigs.push_back(_s); + + if (!date.isValid()) { + llog->log(Logger::WARNING, ME_SENDER_STR, QString("Date ") + date.toString() + QString("is invalid.")); + } else + dates.append(date); + if (!time.isValid()) { + llog->log(Logger::WARNING, ME_SENDER_STR, QString("Time ") + time.toString() + QString("is invalid.")); + } else + times.append(time); + + operatorNames.append(operatorName); + methodNames.append(methodName); + sampleInfos.append(sampleInfo); + } + + /* Check that all common run informaton are the same */ + for (int i = 1; i < operatorNames.size(); i++) { + if (QString::compare(operatorNames.at(i-1), operatorNames.at(i)) != 0) { + QString w; + w += "Operator name in file " + QString::number(i-1) + " (" + operatorNames.at(i-1) + ") does not match that in file " + QString::number(i) + " (" + operatorNames.at(i) + ")"; + llog->log(Logger::WARNING, ME_SENDER_STR, w); + } + } + for (int i = 1; i < methodNames.size(); i++) { + if (QString::compare(methodNames.at(i-1), methodNames.at(i)) != 0) { + QString w; + w += "Method name in file " + QString::number(i-1) + " (" + methodNames.at(i-1) + ") does not match that in file " + QString::number(i) + " (" + methodNames.at(i) + ")"; + llog->log(Logger::WARNING, ME_SENDER_STR, w); + } + } + for (int i = 1; i < sampleInfos.size(); i++) { + if (QString::compare(sampleInfos.at(i-1), sampleInfos.at(i)) != 0) { + QString w; + w += "Sample info in file " + QString::number(i-1) + " (" + sampleInfos.at(i-1) + ") does not match that in file " + QString::number(i) + " (" + sampleInfos.at(i) + ")"; + llog->log(Logger::WARNING, ME_SENDER_STR, w); + } + } + for (int i = 1; i < dates.size(); i++) { + if (dates.at(i-1) != dates.at(i)) { + QString w; + w += "Date in file " + QString::number(i-1) + " (" + dates.at(i-1).toString() + ") does not match that in file " + QString::number(i) + " (" + dates.at(i).toString() + ")"; + llog->log(Logger::WARNING, ME_SENDER_STR, w); + } + } + for (int i = 1; i < times.size(); i++) { + if (times.at(i-1) != times.at(i)) { + QString w; + w += "Date in file " + QString::number(i-1) + " (" + times.at(i-1).toString() + ") does not match that in file" + QString::number(i) + " (" + times.at(i).toString() + ")"; + llog->log(Logger::WARNING, ME_SENDER_STR, w); + } + } + + singleRun = std::shared_ptr(new SingleRunData(methodNames.at(0), operatorNames.at(0), sampleInfos.at(0), dates.at(0), times.at(0), sigs, + dir.dirName(), this)); + + Logger::log(Logger::DEBUG, ME_SENDER_STR, "Single run successfully parsed"); + + return singleRun; +} + +void DataManager::showOneSignal(std::shared_ptr ctrl) +{ + SignalView* swp = new SignalView(); + + swp->setDataTableModel(ctrl->dataTableModel()); + swp->setIntegrationTableModel(ctrl->integrationTableModel()); + swp->setTypeText(ctrl->signal()->resourceToString()); + swp->setXUnits(ctrl->signal()->xunitToString()); + swp->setYUnits(ctrl->signal()->yunitToString()); + connect(swp, SIGNAL(crosshairErased()), ctrl.get(), SLOT(onViewCrosshairErased())); + connect(swp, SIGNAL(crosshairMoved(int,int)), ctrl.get(), SLOT(onViewCrosshairMoved(int,int))); + connect(swp, SIGNAL(integrated(int,int,int,int)), ctrl.get(), SLOT(onViewIntegrated(int,int,int,int))); + connect(swp, SIGNAL(redrawNeeded()), ctrl.get(), SLOT(onViewRedrawNeeded())); + connect(swp, SIGNAL(resized(int,int)), ctrl.get(), SLOT(onViewResized(int,int))); + connect(swp, SIGNAL(showContextMenu(int,int,QPoint)), ctrl.get(), SLOT(onViewShowContextMenu(int,int,QPoint))); + connect(swp, SIGNAL(zoomed(int,int,int,int)), ctrl.get(), SLOT(onViewZoomed(int,int,int,int))); + connect(ctrl.get(), SIGNAL(guiDrawGraph(double*,size_t,double,double)), swp, SLOT(onUpdateGraph(double*,size_t,double,double))); + connect(ctrl.get(), SIGNAL(viewCtxMenuClosed()), swp, SLOT(onCtxMenuClosed())); + connect(ctrl.get(), SIGNAL(viewDrawIntegration(int,double,int,double,int,double,QString,QString,bool)), swp, + SLOT(onDrawIntegration(int,double,int,double,int,double,QString,QString,bool))); + connect(ctrl.get(), SIGNAL(viewUpdateCurrentValues(double,double)), swp, SLOT(onUpdateCurrentValues(double,double))); + connect(ctrl.get(), SIGNAL(viewRemoveCurrentValues()), swp, SLOT(onRemoveCurrentValues())); + emit addToDashboard(swp); + + ctrl->draw(); +} + +/* Public slots */ + +void DataManager::onLoadSequence(const QString& dir) +{ + QDir rootDir(dir); + QString uniqueName; + int nameSuffix = 1; + bool isUnique = false; + std::shared_ptr nSeq(new Sequence()); + + for (const QString& subPath : rootDir.entryList(DATA_DIR_SUFFIX, QDir::Dirs | QDir::NoDotAndDotDot)) { + QDir subDir(rootDir.absoluteFilePath(subPath)); + std::shared_ptr srdata = loadSingleRun(subDir); + if (srdata == nullptr) + continue; + + nSeq->add(subPath, srdata); + } + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "Single runs loaded, count: " + QString::number(nSeq->count())); + if (nSeq->count() < 1) { + QMessageBox::warning(nullptr, "Data loading", "This directory does not appear to contain ChemStation sequence."); + return; + } + + /* Generate unique name for the sequence */ + uniqueName = rootDir.dirName(); + while (!isUnique) { + for (const NameSequencePair& nsp : m_sequences) { + if (nsp.first.compare(uniqueName) == 0) { + if (nameSuffix == 1) + uniqueName += QString("_1"); + else + uniqueName.replace(uniqueName.length()-1, 1, QString::number(nameSuffix)); + nameSuffix++; + } else + isUnique = true; + } + } + + /* Create signal controllers */ + for (const SingleRunPair& srp : *nSeq) { + std::shared_ptr srdata = srp.second; + for (size_t idx = 0; idx < srdata->signalCount(); idx++) { + std::shared_ptr controller(new SignalController(srdata->signalAt(idx))); + nSeq->addController(signalControllerKey(srdata->dirName(), srdata->signalAt(idx)->uidString()), controller); + } + qDebug() << srp.first; + } + + m_sequences.push_back(NameSequencePair(uniqueName, nSeq)); + m_seqSelModel.seqenceAdded(); + + emit setActiveSequenceIndex(m_sequences.size()-1); +} + +void DataManager::onLoadSingleRun(const QString& str) +{ + QString uniqueName; + int nameSuffix = 1; + std::shared_ptr srseq = m_sequences.at(0).second; + QDir dir(str); + std::shared_ptr srdata = loadSingleRun(dir); + if (srdata == nullptr) + return; + + /* Generate unique name for the single run data */ + uniqueName = dir.dirName(); + while (srseq->find(uniqueName) != srseq->end()) { + Logger::log(Logger::Level::INFO, ME_SENDER_STR, "[" + uniqueName + "]: Already there, adjusting."); + if (nameSuffix == 1) { + uniqueName += QString("_1"); + } else { + uniqueName.replace(uniqueName.length()-1, 1, QString::number(nameSuffix)); + } + nameSuffix++; + } + + /* Create signal controllers */ + for (size_t idx = 0; idx < srdata->signalCount(); idx++) { + std::shared_ptr controller(new SignalController(srdata->signalAt(idx))); + srseq->addController(signalControllerKey(srdata->dirName(), srdata->signalAt(idx)->uidString()), controller); + } + + srseq->add(uniqueName, srdata); + srseq->setSelectedRunKey(uniqueName); + emit setActiveSequenceIndex(0); +} + +void DataManager::onSequenceSelected(const QString& key) +{ + if (m_sequenceRejected) { + m_sequenceRejected = false; + return; + } + + m_activeSequence = sequenceByKey(key); + if (m_activeSequence == nullptr) { + Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "[" + key + "]: No such sequence."); + m_activeSequence = sequenceByKey(m_prevSequenceKey); + m_sequenceRejected = true; + emit setActiveSequenceIndex(sequenceIdxByKey(m_prevSequenceKey)); + return; + } + + if (m_activeSequence->count() < 1) { + Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "There are no runs in sequence [" + key + "]."); + QMessageBox::information(nullptr, "Data manager", "Sequence '" + key + "' does not contain any runs."); + /* Revert to previous sequence */ + m_activeSequence = sequenceByKey(m_prevSequenceKey); + m_sequenceRejected = true; + emit setActiveSequenceIndex(sequenceIdxByKey(m_prevSequenceKey)); + return; + } + + m_prevSequenceKey = key; + m_singleSelModel.setSingleRuns(m_activeSequence->allRuns()); + emit setActiveSingleRunIndex(m_activeSequence->selectedRunIdx()); +} + +void DataManager::onSingleRunSelected(const QString& key) +{ + qDebug() << "SRKEY" << key; + if (m_activeSequence == nullptr) + return; + + std::vector::iterator it = m_activeSequence->find(key); + if (it == m_activeSequence->end()) { + Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "[" + key + "]: No such single run."); + return; + } + + m_activeSequence->setSelectedRunKey(key); + + std::shared_ptr srdata = it->second; + emit cleanDashboard(); + for (size_t idx = 0; idx < srdata->signalCount(); idx++) { + std::shared_ptr controller = m_activeSequence->controller(signalControllerKey(srdata->dirName(), srdata->signalAt(idx)->uidString())); + if (controller == nullptr) { + Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "[" + srdata->dirName() + srdata->signalAt(idx)->uidString() + "]: No such controller."); + continue; + } + showOneSignal(controller); + } + + emit setSingleRunInfo(srdata->methodName(), srdata->operatorName(), srdata->sampleInfo(), srdata->date().toString() +" "+ srdata->time().toString()); + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "Single run changed."); +} diff --git a/datamanager.h b/datamanager.h new file mode 100644 index 0000000..bb7143e --- /dev/null +++ b/datamanager.h @@ -0,0 +1,92 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef DATAMANAGER_H +#define DATAMANAGER_H + +#include "datafilesloader.h" +#include "gui/signalview.h" +#include "sequence.h" +#include "sequenceselectormodel.h" +#include "singlerunsselectormodel.h" +#include +#include +#include +#include +#include +#include "metatypes.h" + + +class DataManager : public QObject +{ + Q_OBJECT +public: + explicit DataManager(QObject* parent = nullptr); + SequenceSelectorModel* sequenceSelectorModel() { return &m_seqSelModel; } + SingleRunsSelectorModel* singleRunsSelectorModel() { return &m_singleSelModel; } + +private: + Signal::Equipment equipmentFromFiletype(AGRE_Filetype filetype); + std::shared_ptr sequenceByKey(const QString& key); + int sequenceIdxByKey(const QString& key); + std::shared_ptr loadSingleRun(QDir& dir); + Signal::Resource resourceFromFiletype(AGRE_Filetype filetype); + std::string signalControllerKey(const QString& main, const QString& resource) ; + void showOneSignal(std::shared_ptr ctrl); + Signal::YUnit yunitFromUnitStr(const std::string& unit_str); + + std::shared_ptr m_activeSequence; + QString m_prevSequenceKey; + QString m_prevSingleRunKey; + SequenceSelectorModel m_seqSelModel; + bool m_sequenceRejected; + SingleRunsSelectorModel m_singleSelModel; + QTextCodec* m_sigNameCodec; + std::vector m_sequences; + DataFilesLoader m_dfl; + + static const QString ME_SENDER_STR; + static const char UNIT_KILOVOLTS_TEXT[]; + static const char UNIT_MICROAMPERES_TEXT[]; + static const char UNIT_MILLIAU_TEXT[]; + static const char UNIT_MILLIVOLTS_TEXT[]; + static const char UNIT_WATTS_TEXT[]; + + static const QStringList DATA_DIR_SUFFIX; + +signals: + void addToDashboard(SignalView* sw); + void cleanDashboard(); + void setActiveSequenceIndex(const int idx); + void setActiveSingleRunIndex(const int idx); + void setSingleRunInfo(const QString& method, const QString& operatorname, const QString& sample, const QString& datetime); + +public slots: + void onLoadSequence(const QString& dir); + void onLoadSingleRun(const QString& dir); + void onSequenceSelected(const QString& key); + void onSingleRunSelected(const QString& key); + + +}; + +#endif // DATAMANAGER_H diff --git a/globalinfo.cpp b/globalinfo.cpp new file mode 100644 index 0000000..a198435 --- /dev/null +++ b/globalinfo.cpp @@ -0,0 +1,27 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "globalinfo.h" + +const int GlobalInfo::APP_VERSION_MAJ(0); +const int GlobalInfo::APP_VERSION_MIN(1); +const QString GlobalInfo::APP_VERSION_REV("a"); diff --git a/globalinfo.h b/globalinfo.h new file mode 100644 index 0000000..24e2fa4 --- /dev/null +++ b/globalinfo.h @@ -0,0 +1,41 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef GLOBALINFO_H +#define GLOBALINFO_H + +#include + +class GlobalInfo { +private: + GlobalInfo(){} + +public: + static QString APP_VERSION_STRING() { return QString::number(GlobalInfo::APP_VERSION_MAJ) + "." + QString::number(GlobalInfo::APP_VERSION_MIN) + + GlobalInfo::APP_VERSION_REV; } + + static const int APP_VERSION_MAJ; + static const int APP_VERSION_MIN; + static const QString APP_VERSION_REV; +}; + +#endif // GLOBALINFO_H diff --git a/gui/aboutanyanka.cpp b/gui/aboutanyanka.cpp new file mode 100644 index 0000000..d3bae24 --- /dev/null +++ b/gui/aboutanyanka.cpp @@ -0,0 +1,42 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "aboutanyanka.h" +#include "ui_aboutanyanka.h" + +#include + +AboutAnyanka::AboutAnyanka(QWidget *parent) : + QDialog(parent), + ui(new Ui::AboutAnyanka) +{ + ui->setupUi(this); + ui->ql_version->setText(GlobalInfo::APP_VERSION_STRING()); + ui->ql_qtVersion->setText(qVersion()); + ui->ql_qtLogo->setPixmap(QPixmap("://resources/Qt_master_logo.png").scaled(ui->ql_qtLogo->size(), Qt::KeepAspectRatio, + Qt::SmoothTransformation)); +} + +AboutAnyanka::~AboutAnyanka() +{ + delete ui; +} diff --git a/gui/aboutanyanka.h b/gui/aboutanyanka.h new file mode 100644 index 0000000..8db8901 --- /dev/null +++ b/gui/aboutanyanka.h @@ -0,0 +1,45 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef ABOUTANYANKA_H +#define ABOUTANYANKA_H + +#include "globalinfo.h" +#include + +namespace Ui { + class AboutAnyanka; +} + +class AboutAnyanka : public QDialog +{ + Q_OBJECT + +public: + explicit AboutAnyanka(QWidget* parent = nullptr); + ~AboutAnyanka(); + +private: + Ui::AboutAnyanka *ui; +}; + +#endif // ABOUTANYANKA_H diff --git a/gui/aboutanyanka.ui b/gui/aboutanyanka.ui new file mode 100644 index 0000000..4bf4e57 --- /dev/null +++ b/gui/aboutanyanka.ui @@ -0,0 +1,133 @@ + + + AboutAnyanka + + + + 0 + 0 + 363 + 313 + + + + Dialog + + + + + 115 + 13 + 121 + 41 + + + + + 22 + 75 + true + + + + Anyanka + + + + + + 135 + 60 + 53 + 16 + + + + Version: + + + + + + 185 + 60 + 41 + 16 + + + + - + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 115 + 100 + 131 + 111 + + + + logo + + + Qt::AlignCenter + + + + + + 90 + 280 + 151 + 16 + + + + Using Qt 5 libraries version: + + + + + + 244 + 280 + 41 + 16 + + + + - + + + Qt::AlignLeading|Qt::AlignLeft|Qt::AlignVCenter + + + + + + 62 + 240 + 240 + 21 + + + + + 11 + 75 + true + + + + "Fear nothing except for bunnies!" + + + + + + diff --git a/gui/graphview.cpp b/gui/graphview.cpp new file mode 100644 index 0000000..262b74b --- /dev/null +++ b/gui/graphview.cpp @@ -0,0 +1,50 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "gui/graphview.h" + +GraphView::GraphView(QWidget *parent) : + QLabel(parent) +{ + setMouseTracking(true); +} + +void GraphView::leaveEvent(QEvent *) +{ + emit mouseCursorLeft(); +} + +void GraphView::mouseMoveEvent(QMouseEvent* ev) +{ + emit mouseMoved(ev->x(), ev->y()); +} + +void GraphView::mousePressEvent(QMouseEvent* ev) +{ + emit mouseButtonTriggered(ev->x(), ev->y(), ev->button(), ev->type(), ev->globalPos()); +} + +void GraphView::resizeEvent(QResizeEvent* ev) +{ + if (ev->size().width() > 1 && ev->size().height() > 1) + emit redrawNeeded(); +} diff --git a/gui/graphview.h b/gui/graphview.h new file mode 100644 index 0000000..895b420 --- /dev/null +++ b/gui/graphview.h @@ -0,0 +1,50 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef GRAPHVIEW_H +#define GRAPHVIEW_H + +#include +#include + +class GraphView : public QLabel +{ + Q_OBJECT +public: + explicit GraphView(QWidget* parent = nullptr); + void leaveEvent(QEvent* ); + void mouseMoveEvent(QMouseEvent* ev); + void mousePressEvent(QMouseEvent* ev); + +private: + void resizeEvent(QResizeEvent* ev); + +public slots: + +signals: + void mouseCursorLeft(); + void mouseMoved(const int x, const int y); + void mouseButtonTriggered(const int x, const int y, Qt::MouseButton button, QEvent::Type type, const QPoint& globalPos); + void redrawNeeded(); +}; + +#endif // GRAPHVIEW_H diff --git a/gui/graphviewcontextmenu.cpp b/gui/graphviewcontextmenu.cpp new file mode 100644 index 0000000..84552ce --- /dev/null +++ b/gui/graphviewcontextmenu.cpp @@ -0,0 +1,31 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "graphviewcontextmenu.h" + +GraphViewContextMenu::GraphViewContextMenu(QWidget* parent) : + QMenu(parent) +{ + m_acZoomOut = addAction("Zoom out"); + addSeparator(); + m_acDeletePeak = addAction("Delete peak"); +} diff --git a/gui/graphviewcontextmenu.h b/gui/graphviewcontextmenu.h new file mode 100644 index 0000000..1896f1c --- /dev/null +++ b/gui/graphviewcontextmenu.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#ifndef GRAPHVIEWCONTEXTMENU_H +#define GRAPHVIEWCONTEXTMENU_H + +#include + +class GraphViewContextMenu : public QMenu +{ + Q_OBJECT +public: + explicit GraphViewContextMenu(QWidget* parent = nullptr); + + QAction* m_acDeletePeak; + QAction* m_acZoomOut; + +private: + +signals: + +public slots: + +}; + +#endif // GRAPHVIEWCONTEXTMENU_H diff --git a/gui/mainwindow.cpp b/gui/mainwindow.cpp new file mode 100644 index 0000000..bc11e60 --- /dev/null +++ b/gui/mainwindow.cpp @@ -0,0 +1,159 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#include "gui/mainwindow.h" +#include "ui_mainwindow.h" +#include + +#include + +MainWindow::MainWindow(QWidget *parent) : + QMainWindow(parent), + ui(new Ui::MainWindow) +{ + ui->setupUi(this); + m_dashboard = new QSplitter(Qt::Vertical, this); + m_dashboard->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Expanding); + ui->qsca_dataView->setWidget(m_dashboard); + + connectActions(); + + m_loadDataFileDialog = new QFileDialog(this); + m_loadDataFileDialog->setFileMode(QFileDialog::Directory); + m_loadDataFileDialog->setOptions(QFileDialog::ReadOnly); +} + +/* Public methods */ +void MainWindow::setSequenceListModel(SequenceSelectorModel* model) +{ + ui->qcbox_sequence->setModel(model); +} + +void MainWindow::setSingleRunsListModel(SingleRunsSelectorModel* model) +{ + ui->qcbox_singleRun->setModel(model); +} + +/* Private methods */ + +#ifdef Q_OS_UNIX +void MainWindow::connectActions() noexcept +#elif defined Q_OS_WIN +void MainWindow::connectActions() +#endif +{ + /* Controls panel */ + connect(ui->qpb_integrate, SIGNAL(pressed()), this, SLOT(onIntegrateSelected())); + connect(ui->qpb_zoom, SIGNAL(pressed()), this, SLOT(onZoomSelected())); + connect(ui->actionLoad_single_run, SIGNAL(triggered()), this, SLOT(onLoadSingleRun())); + connect(ui->actionLoad_sequence, SIGNAL(triggered()), this, SLOT(onLoadSequence())); + connect(ui->actionAbout, SIGNAL(triggered()), this, SLOT(onAboutAnyanka())); + connect(ui->qcbox_sequence, SIGNAL(currentIndexChanged(QString)), this, SLOT(onSequenceSelected(QString))); + connect(ui->qcbox_singleRun, SIGNAL(currentIndexChanged(QString)), this, SLOT(onSingleRunSelected(QString))); +} + +/* Public slots */ +void MainWindow::onAddToDashboard(SignalView* sw) +{ + if (ui->qpb_integrate->isChecked()) + sw->setControlMode(GraphControlModes::INTEGRATE); + else if(ui->qpb_zoom->isChecked()) + sw->setControlMode(GraphControlModes::ZOOM); + + //m_itemsOnDashboard.push_back(sw); + connect(this, SIGNAL(controlModeChanged(GraphControlModes)), sw, SLOT(onControlModeChanged(GraphControlModes))); + m_dashboard->addWidget(sw); +} + +void MainWindow::onCleanDashboard() +{ + while (m_dashboard->count() > 0) + delete m_dashboard->widget(0); +} + +void MainWindow::onSetActiveSequenceIndex(const int idx) +{ + if (ui->qcbox_sequence->currentIndex() == idx) + emit onSequenceSelected(ui->qcbox_sequence->currentText()); + else + ui->qcbox_sequence->setCurrentIndex(idx); +} + +void MainWindow::onSetActiveSingleRunIndex(const int idx) +{ + ui->qcbox_singleRun->setCurrentIndex(idx); +} + +void MainWindow::onSetSingleRunInfo(const QString &method, const QString &operatorname, const QString &sample, const QString &datetime) +{ + ui->qle_methodName->setText(method); + ui->qle_operatorName->setText(operatorname); + ui->qle_sampleInfo->setText(sample); + ui->qle_dateTime->setText(datetime); +} + +/* Private slots */ +void MainWindow::onAboutAnyanka() +{ + AboutAnyanka aa(this); + aa.exec(); +} + +void MainWindow::onIntegrateSelected() +{ + emit controlModeChanged(GraphControlModes::INTEGRATE); +} +void MainWindow::onLoadSequence() +{ + if (m_loadDataFileDialog->exec() == QDialog::Accepted) + emit loadSequence(m_loadDataFileDialog->selectedFiles()[0]); +} + +void MainWindow::onLoadSingleRun() +{ + if (m_loadDataFileDialog->exec() == QDialog::Accepted) { + qDebug() << "Chosen file" << m_loadDataFileDialog->selectedFiles(); + emit loadSingleRun(m_loadDataFileDialog->selectedFiles()[0]); + } +} + +void MainWindow::onZoomSelected() +{ + emit controlModeChanged(GraphControlModes::ZOOM); +} + +/* Private slots */ +void MainWindow::onSequenceSelected(const QString &str) +{ + emit sequenceSelected(str); +} + +void MainWindow::onSingleRunSelected(const QString &str) +{ + emit singleRunSelected(str); +} + +MainWindow::~MainWindow() +{ + delete ui; +} diff --git a/gui/mainwindow.h b/gui/mainwindow.h new file mode 100644 index 0000000..5dc82ed --- /dev/null +++ b/gui/mainwindow.h @@ -0,0 +1,86 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef MAINWINDOW_H +#define MAINWINDOW_H + +#include "aboutanyanka.h" +#include "sequenceselectormodel.h" +#include "singlerunsselectormodel.h" +#include "signalview.h" +#include +#include +#include +#include +#include "metatypes.h" + +namespace Ui { + class MainWindow; +} + +class MainWindow : public QMainWindow +{ + Q_OBJECT + +public: + explicit MainWindow(QWidget* parent = 0); + ~MainWindow(); + void setSequenceListModel(SequenceSelectorModel* model); + void setSingleRunsListModel(SingleRunsSelectorModel* model); + +private: +#ifdef Q_OS_UNIX + void connectActions() noexcept; +#elif defined Q_OS_WIN + void connectActions(); +#endif + + //std::vector m_itemsOnDashboard; + QSplitter* m_dashboard; + QFileDialog* m_loadDataFileDialog; + Ui::MainWindow *ui; + +public slots: + void onAddToDashboard(SignalView* sw); + void onCleanDashboard(); + void onSetActiveSequenceIndex(const int idx); + void onSetActiveSingleRunIndex(const int idx); + void onSetSingleRunInfo(const QString& method, const QString& operatorname, const QString& sample, const QString& datetime); + +private slots: + void onAboutAnyanka(); + void onIntegrateSelected(); + void onLoadSequence(); + void onLoadSingleRun(); + void onSequenceSelected(const QString& str); + void onSingleRunSelected(const QString& str); + void onZoomSelected(); + +signals: + void controlModeChanged(GraphControlModes mode); + void loadSequence(const QString& dir); + void loadSingleRun(const QString& dir); + void sequenceSelected(const QString& str); + void singleRunSelected(const QString& str); +}; + +#endif // MAINWINDOW_H diff --git a/gui/mainwindow.ui b/gui/mainwindow.ui new file mode 100644 index 0000000..0dcb07f --- /dev/null +++ b/gui/mainwindow.ui @@ -0,0 +1,251 @@ + + + MainWindow + + + + 0 + 0 + 800 + 600 + + + + Anyanka + + + + + + + + + Sequence: + + + + + + + Run: + + + + + + + + + + + + + + + true + + + + + + false + + + + + + Zoom + + + true + + + true + + + true + + + + + + + Integrate + + + true + + + true + + + + + + + + + + QFrame::StyledPanel + + + QFrame::Raised + + + + + + Sample info: + + + + + + + Operator: + + + + + + + Method name: + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + 0 + 0 + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + Date/Time: + + + + + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + + + + + 0 + 0 + + + + true + + + + + 0 + 0 + 780 + 336 + + + + + 0 + 0 + + + + + + + + + + + 0 + 0 + 800 + 19 + + + + + Data + + + + + + + Help + + + + + + + + + true + + + + + Load single run + + + + + Load sequence + + + + + About + + + + + + diff --git a/gui/signalview.cpp b/gui/signalview.cpp new file mode 100644 index 0000000..6dcd17e --- /dev/null +++ b/gui/signalview.cpp @@ -0,0 +1,547 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#include "gui/signalview.h" +#include "ui_signalview.h" +#include "mathhelpers.h" +#include +#include +#include + +#include + +SignalView::SignalView(QWidget *parent) : + QWidget(parent), + m_graph(nullptr), + m_graphPlain(nullptr), + m_graphData(nullptr), + m_ctxMenuOpen(false), + m_mouseMode(SignalView::MouseMode::CROSSHAIR), + ui(new Ui::SignalView) +{ + ui->setupUi(this); + m_graphPainter = new QPainter(); + m_graphCrosshairX = -1; + m_graphCrosshairY = -1; + m_integrateStartX = -1; + + connect(ui->qw_graphView, SIGNAL(mouseCursorLeft()), this, SLOT(onGraphViewMouseCursorLeft())); + connect(ui->qw_graphView, SIGNAL(mouseMoved(int,int)), this, SLOT(onGraphViewMouseMoved(int,int))); + connect(ui->qw_graphView, SIGNAL(mouseButtonTriggered(int,int,Qt::MouseButton,QEvent::Type,QPoint)), this, + SLOT(onMouseButtonTriggered(int,int,Qt::MouseButton,QEvent::Type,QPoint))); + connect(ui->qw_graphView, SIGNAL(redrawNeeded()), this, SLOT(onGraphRedrawNeeded())); +} + +/* Public functions */ +void SignalView::setControlMode(GraphControlModes mode) +{ + m_graphCtrlMode = mode; +} + +void SignalView::setDataTableModel(SignalDataTableModel* model) +{ + ui->qtblv_signalDataTable->setModel(model); +} + +void SignalView::setIntegrationTableModel(IntegrationTableModel *model) +{ + ui->qtblv_integrationTable->setModel(model); +} + +void SignalView::setTypeText(const QString &text) +{ + ui->ql_signalType->setText(text); +} + +void SignalView::setXUnits(const QString &text) +{ + ui->ql_xUnits->setText(text); +} + +void SignalView::setYUnits(const QString &text) +{ + ui->ql_yUnits->setText(text); +} + +/* Private functions */ + +void SignalView::drawCrosshair(const int x, const int y) +{ + int w, h; + if (m_graph == nullptr) + return; + w = ui->qw_graphView->width(); + h = ui->qw_graphView->height(); + + m_graphPainter->begin(m_graph); + m_graphPainter->setPen(Qt::black); + /* Draw new crosshair */ + m_graphPainter->drawLine(0, y, w, y); /* Horizontal */ + m_graphPainter->drawLine(x, 0, x, h); /* Vertical */ + m_graphPainter->end(); + + m_graphCrosshairX = x; + m_graphCrosshairY = y; + + ui->qw_graphView->setPixmap(*m_graph); + +} + +void SignalView::drawIntegrationBaseline(const int x, const int y) +{ + if (m_graph == nullptr) + return; + m_graphPainter->begin(m_graph); + /* Draw new line */ + m_graphPainter->setPen(Qt::black); + m_graphPainter->drawLine(m_integrateStartX, m_integrateStartY, x, y); + m_graphPainter->end(); + + m_integrateStopX = x; + m_integrateStopY = y; + + ui->qw_graphView->setPixmap(*m_graph); +} + +void SignalView::drawZoomRect(const int x, const int y) +{ + int startX, startY, stopX, stopY; + if (m_graph == nullptr) + return; + + if (x < m_zoomRectStartX) { + startX = x; + stopX = m_zoomRectStartX; + } else { + startX = m_zoomRectStartX; + stopX = x; + } + if (y < m_zoomRectStartY) { + startY = y; + stopY = m_zoomRectStartY; + } else { + startY = m_zoomRectStartY; + stopY = y; + } + + eraseZoomRect(); + + m_graphPainter->begin(m_graph); + m_graphPainter->setPen(Qt::green); + // Top line + m_graphPainter->drawLine(startX, startY, stopX, startY); + // Left line + m_graphPainter->drawLine(startX, startY, startX, stopY); + // Right line + m_graphPainter->drawLine(stopX, startY, stopX, stopY); + // Bottom line + m_graphPainter->drawLine(startX, stopY, stopX, stopY); + + m_zoomRectLastX = x; + m_zoomRectLastY = y; + + m_graphPainter->end(); + + ui->qw_graphView->setPixmap(*m_graph); +} + +void SignalView::eraseCrosshair(bool apply) +{ + int w, h; + if (m_graph == nullptr) + return; + w = ui->qw_graphView->width(); + h = ui->qw_graphView->height(); + + if (m_graphCrosshairX != -1 || m_graphCrosshairY != -1) { + m_graphPainter->begin(m_graph); + m_graphPainter->drawPixmap(m_graphCrosshairX, 0, *m_graphPlain, m_graphCrosshairX, 0, 1, h); + m_graphPainter->drawPixmap(0, m_graphCrosshairY, *m_graphPlain, 0, m_graphCrosshairY, w, 1); + m_graphPainter->end(); + if (apply) + ui->qw_graphView->setPixmap(*m_graph); + } +} + +void SignalView::eraseIntegrationBaseline(bool apply) +{ + int lstartX, lstartY, lstopX, lstopY; + int w, h; + + if (m_graph == nullptr) + return; + if (m_integrateStartX < 0) + return; + + w = ui->qw_graphView->width(); + h = ui->qw_graphView->height(); + + if (m_integrateStartX > m_integrateStopX) { + lstartX = (m_integrateStopX > 1) ? m_integrateStopX - 1 : 0; + lstopX = (m_integrateStartX < w-1) ? m_integrateStartX + 1 : w-1; + } else { + lstartX = (m_integrateStartX > 1) ? m_integrateStartX -1 : 0; + lstopX = (m_integrateStopX < w-1) ? m_integrateStopX + 1 : w-1; + } + if (m_integrateStartY > m_integrateStopY) { + lstartY = (m_integrateStopY > 1) ? m_integrateStopY - 1 : 0; + lstopY = (m_integrateStartY < h-1) ? m_integrateStartY + 1 : h-1; + } else { + lstartY = (m_integrateStartY > 1) ? m_integrateStartY - 1 : 0; + lstopY = (m_integrateStopY < h-1) ? m_integrateStopY + 1 : h-1; + } + + m_graphPainter->begin(m_graph); + m_graphPainter->drawPixmap(lstartX, lstartY, *m_graphPlain, lstartX, lstartY, lstopX - lstartX+1, lstopY - lstartY+1); //FIXME: +1 should not be needed! + m_graphPainter->end(); + + if (apply) + ui->qw_graphView->setPixmap(*m_graph); +} + +void SignalView::eraseZoomRect(bool apply) +{ + int lstartX, lstartY, lstopX, lstopY; + if (m_graph == nullptr) + return; + + if (m_zoomRectLastX < m_zoomRectStartX) { + lstartX = m_zoomRectLastX; + lstopX = m_zoomRectStartX; + } else { + lstartX = m_zoomRectStartX; + lstopX = m_zoomRectLastX; + } + if (m_zoomRectLastY < m_zoomRectStartY) { + lstartY = m_zoomRectLastY; + lstopY = m_zoomRectStartY; + } else { + lstartY = m_zoomRectStartY; + lstopY = m_zoomRectLastY; + } + + m_graphPainter->begin(m_graph); + /* Erase any existing lines */ + // TODO: It might not be necessary to erase all lines every time - OPTIMIZE THIS !!! + // Top line + m_graphPainter->drawPixmap(lstartX, lstartY, *m_graphPlain, lstartX, lstartY, lstopX - lstartX, 1); + // Left line + m_graphPainter->drawPixmap(lstartX, lstartY, *m_graphPlain, lstartX, lstartY, 1, lstopY - lstartY); + // Right line + m_graphPainter->drawPixmap(lstopX, lstartY, *m_graphPlain, lstopX, lstartY, 1, lstopY - lstartY + 1); + // Bottom line + m_graphPainter->drawPixmap(lstartX, lstopY, *m_graphPlain, lstartX, lstopY, lstopX - lstartX + 1, 1); + m_graphPainter->end(); + + if (apply) + ui->qw_graphView->setPixmap(*m_graph); +} + +void SignalView::redrawGraph() +{ + int w = ui->qw_graphView->width(); + int h = ui->qw_graphView->height(); + uint lastColumn = 0; + double yAvg = 0; + double yStep; + QPixmap* fresh = new QPixmap(w, h); + + if (m_graphData == nullptr) + return; + if (w < 1 || h < 1) + return; + + fresh->fill(Qt::white); + m_graphPainter->begin(fresh); + m_graphPainter->setPen(QColor(Qt::blue)); + + /* Scaling */ + yStep = static_cast(h) / fabs(m_graphMax - m_graphMin); + + /* Downscale to grid */ + if (w < m_graphLen) { + double columnsPerPixel = static_cast(m_graphLen) / w; + uint columnsPerLastPixel = 0; + int* block = new int[w]; + //qDebug() << "CPP" << columnsPerPixel << "yStep" << yStep << "Dims" << h << w << "Data" << w*columnsPerPixel << m_graphLen; + for (uint i = 0; i < m_graphLen; i++) { + uint column = i / columnsPerPixel; + if (column != lastColumn) { + yAvg /= columnsPerLastPixel; + block[lastColumn] = h - yStep * (yAvg -m_graphMin); + //qDebug() << "YAVG" << block[lastColumn] << "CPLP" << columnsPerLastPixel; + yAvg = 0; + columnsPerLastPixel= 0; + lastColumn = column; + } + yAvg += m_graphData[i]; + columnsPerLastPixel++; + } + /* Add the last column */ + yAvg /= columnsPerLastPixel; + block[w-1] = h - yStep * (yAvg - m_graphMin); + /* Draw the pixmap */ + for (int i = 1; i < w; i++) + m_graphPainter->drawLine(i-1, floor(block[i-1]+0.5), i, floor(block[i]+0.5)); + //qDebug() << "LAST YAVG" << block[m_graphLen-1] << "CPLP" << columnsPerLastPixel; + delete[] block; + } else { /* Upscale to grid */ + double pixelsPerValue = static_cast(w) / m_graphLen; + uint cPixX; + uint pPixX = 0; + uint cPixY; + uint pPixY = floor((h - yStep * (m_graphData[0] - m_graphMin)) + 0.5); + for (int i = 1; i < m_graphLen; i++) { + cPixX= floor(i * pixelsPerValue + 0.5); + cPixY = h - yStep * (m_graphData[i] - m_graphMin); + //qDebug() << "Upscale" << pPixX << pPixY << cPixX << cPixY; + m_graphPainter->drawLine(pPixX, pPixY, cPixX, cPixY); + pPixX = cPixX; + pPixY = cPixY; + } + } + + m_graphPainter->end(); + if (m_graphPlain != nullptr) + delete m_graphPlain; + m_graphPlain = new QPixmap(*fresh); + ui->qw_graphView->setPixmap(*fresh); + if (m_graph != nullptr) + delete m_graph; + m_graph = fresh; +} + +void SignalView::resizeEvent(QResizeEvent* ev) +{ + Q_UNUSED(ev); + + emit resized(ui->qw_graphView->width(), ui->qw_graphView->height()); + emit redrawNeeded(); +} + +void SignalView::onUpdateCurrentValues(double x, double y) +{ + ui->qle_xValue->setText(QString::number(x)); + ui->qle_yValue->setText(QString::number(y)); +} + +void SignalView::onRemoveCurrentValues() +{ + ui->qle_xValue->setText("-"); + ui->qle_yValue->setText("-"); +} + +/* Public slots */ +void SignalView::onControlModeChanged(GraphControlModes mode) +{ + m_graphCtrlMode = mode; +} + +void SignalView::onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, const QString& time, + const QString& area, const bool valley) +{ + int w, h; + int startX, startY, stopX, stopY, peakStartX, peakStartY; + double yStep, xStep; + double dStartY, dStopY; + QFont font("arial", 10); + QFontMetrics fm(font); + int textWidth; + if (m_graphPlain == nullptr) + return; + + w = ui->qw_graphView->width(); + h = ui->qw_graphView->height(); + xStep = static_cast(w) / m_graphLen; + yStep = static_cast(h) / (m_graphMax - m_graphMin); + startX = floor(fromX * xStep + 0.5); + stopX = floor(toX * xStep + 0.5); + startY = floor((h - yStep * (fromY - m_graphMin)) + 0.5); + stopY = floor((h - yStep * (toY - m_graphMin)) + 0.5); + peakStartX = floor(peakX * xStep + 0.5); + peakStartY = floor(h - yStep * (peakY - m_graphMin) + 0.5); + + dStartY = h - yStep * (fromY - m_graphMin); + dStopY = h - yStep * (toY - m_graphMin); + + /*qDebug("Drawing peak"); + qDebug() << "RSX" << fromX << "RSY" << fromY << "REX" << toX << "REY" << toY; + qDebug() << "X step" << xStep << "Y step" << yStep; + qDebug() << "SX" << startX << "SY" << startY << "EX" << stopX << "EY" << stopY << "w/h" << w << h; + qDebug() << "DSY" << dStartY << "DEY" << dStopY << "Slope I" << (toY-fromY)/(toX-fromX) << "Slope D" << (dStopY-dStartY)/(toX-fromX); + qDebug("---");*/ + + m_graphPainter->begin(m_graphPlain); + m_graphPainter->setPen(Qt::red); + m_graphPainter->drawLine(startX, startY, stopX, stopY); + + /* Draw AREA and TIME caption */ + m_graphPainter->setFont(font); + m_graphPainter->setPen(Qt::black); + textWidth = fm.width(time); + if (peakStartY - textWidth < 2) + peakStartY += textWidth - peakStartY + 2; + m_graphPainter->save(); + m_graphPainter->translate(peakStartX, peakStartY); + m_graphPainter->rotate(-90); + m_graphPainter->drawText(0, 0, time); + m_graphPainter->rotate(+90); + m_graphPainter->restore(); + m_graphPainter->drawText(peakStartX + 5, peakStartY, area); + + m_graphPainter->end(); + m_graphPainter->begin(m_graph); + m_graphPainter->drawPixmap(0, 0, *m_graphPlain, 0, 0, w, h); + m_graphPainter->end(); + ui->qw_graphView->setPixmap(*m_graph); +} + +void SignalView::onUpdateGraph(double* data, size_t len, double min, double max) +{ + if (m_graphData != nullptr) + delete[] m_graphData; + m_graphData = data; + m_graphLen = len; + m_graphMin = min; + m_graphMax = max; + + redrawGraph(); +} +/* ENDOF public slots */ + +/* Private slots*/ +void SignalView::onGraphViewMouseCursorLeft() +{ + if(m_graph == nullptr) + return; + + ui->qw_graphView->setPixmap(*m_graphPlain); + + emit crosshairErased(); +} + +void SignalView::onGraphRedrawNeeded() +{ + emit redrawNeeded(); +} + +void SignalView::onMouseButtonTriggered(const int x, const int y, Qt::MouseButton button, QEvent::Type type, const QPoint& globalPos) +{ + if (m_ctxMenuOpen) + return; + + switch (button) { + case Qt::LeftButton: + switch (m_mouseMode) { + case SignalView::MouseMode::CROSSHAIR: + if (m_graphCtrlMode == GraphControlModes::ZOOM) { + m_zoomRectStartX = x; + m_zoomRectStartY = y; + m_zoomRectLastX = x; + m_zoomRectLastY = y; + m_mouseMode = SignalView::MouseMode::ZOOM; + } else if (m_graphCtrlMode == GraphControlModes::INTEGRATE) { + m_integrateStartX = x; + m_integrateStartY = y; + m_integrateStopX = x; + m_integrateStopY = y; + eraseCrosshair(); + m_graphCrosshairX = -1; + m_mouseMode = SignalView::MouseMode::INTEGRATE; + } + break; + case SignalView::MouseMode::ZOOM: + if (m_zoomRectLastX < m_zoomRectStartX) + std::swap(m_zoomRectLastX, m_zoomRectStartX); + if (m_zoomRectLastY < m_zoomRectStartY) + std::swap(m_zoomRectLastY, m_zoomRectStartY); + emit zoomed(m_zoomRectStartX, m_zoomRectStartY, m_zoomRectLastX, m_zoomRectLastY); + m_mouseMode = SignalView::MouseMode::CROSSHAIR; + break; + case SignalView::MouseMode::INTEGRATE: + if (m_integrateStartX > m_integrateStopX) + std::swap(m_integrateStartX, m_integrateStopX); + emit integrated(m_integrateStartX, m_integrateStartY, m_integrateStopX, m_integrateStopY); + eraseIntegrationBaseline(true); + m_integrateStartX = -1; + m_mouseMode = SignalView::MouseMode::CROSSHAIR; + break; + } + break; + case Qt::RightButton: + switch (m_mouseMode) { + case SignalView::MouseMode::CROSSHAIR: + m_ctxMenuOpen = true; + emit showContextMenu(x, y, globalPos); + break; + case SignalView::MouseMode::ZOOM: + eraseZoomRect(true); + m_mouseMode = SignalView::MouseMode::CROSSHAIR; + break; + case SignalView::MouseMode::INTEGRATE: + eraseIntegrationBaseline(true); + m_mouseMode = SignalView::MouseMode::CROSSHAIR; + break; + default: + break; + } + break; + default: + break; + } +} + +void SignalView::onGraphViewMouseMoved(const int x, const int y) +{ + switch (m_mouseMode) { + case SignalView::MouseMode::CROSSHAIR: + eraseCrosshair(); + drawCrosshair(x, y); + break; + case SignalView::MouseMode::ZOOM: + eraseCrosshair(); + drawZoomRect(x, y); + break; + case SignalView::MouseMode::INTEGRATE: + eraseIntegrationBaseline(); + drawIntegrationBaseline(x, y); + break; + } + + emit crosshairMoved(x, y); +} + +SignalView::~SignalView() +{ + if (m_graphPainter->isActive()) + m_graphPainter->end(); + delete ui; + if (m_graph != nullptr) + delete m_graph; + if (m_graphPlain != nullptr) + delete m_graphPlain; + if (m_graphData != nullptr) + delete[] m_graphData; + + delete m_graphPainter; +} diff --git a/gui/signalview.h b/gui/signalview.h new file mode 100644 index 0000000..2c0b66c --- /dev/null +++ b/gui/signalview.h @@ -0,0 +1,120 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SIGNALVIEW_H +#define SIGNALVIEW_H + +#include "graphview.h" +#include "integrationtablemodel.h" +#include "signaldatatablemodel.h" +#include +#include +#include "../metatypes.h" + +namespace Ui { + class SignalView; +} + +class SignalView : public QWidget +{ + Q_OBJECT + +public: + enum class MouseMode { + CROSSHAIR, + ZOOM, + INTEGRATE + }; + + explicit SignalView(QWidget* parent = nullptr); + ~SignalView(); + void setControlMode(GraphControlModes mode); + void setDataTableModel (SignalDataTableModel* model); + void setIntegrationTableModel(IntegrationTableModel* model); + void setTypeText(const QString& text); + void setXUnits(const QString& text); + void setYUnits(const QString& text); + +private: + void drawCrosshair(const int x, const int y); + void drawIntegrationBaseline(const int x, const int y); + void drawZoomRect(const int x, const int y); + void eraseCrosshair(bool apply = false); + void eraseIntegrationBaseline(bool apply = false); + void eraseZoomRect(bool apply = false); + void redrawGraph(); + void resizeEvent(QResizeEvent* ev); + + QPainter* m_graphPainter; + /* Dataset to plot */ + QPixmap* m_graph; + QPixmap* m_graphPlain; + double* m_graphData; + double m_graphMin; + double m_graphMax; + size_t m_graphLen; + /* Crosshair */ + int m_graphCrosshairX; + int m_graphCrosshairY; + /* Integration baseline */ + int m_integrateStartX; + int m_integrateStartY; + int m_integrateStopX; + int m_integrateStopY; + /* Zooming rectangle */ + int m_zoomRectStartX; + int m_zoomRectStartY; + int m_zoomRectLastX; + int m_zoomRectLastY; + + bool m_ctxMenuOpen; + GraphControlModes m_graphCtrlMode; + MouseMode m_mouseMode; + Ui::SignalView* ui; + +public slots: + void onControlModeChanged(GraphControlModes mode); + void onCtxMenuClosed() { m_ctxMenuOpen = false; } + void onDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, + const QString& time, const QString& area, const bool valley); + void onMouseButtonTriggered(const int x, const int y, Qt::MouseButton button, QEvent::Type type, const QPoint& globalPos); + void onUpdateCurrentValues(double x, double y); + void onRemoveCurrentValues(); + void onUpdateGraph(double* arr, size_t len, double min, double max); + +private slots: + void onGraphRedrawNeeded(); + void onGraphViewMouseCursorLeft(); + void onGraphViewMouseMoved(const int x, const int y); + +signals: + void crosshairErased(); + void crosshairMoved(const int x, const int y); + void integrated(const int fromX, const int fromY, const int toX, const int toY); + void redrawNeeded(); + void resized(const int x, const int y); + void showContextMenu(const int x, const int y, const QPoint& globalPos); + void unzoomed(); + void zoomed(const int fromX, const int fromY, const int toX, const int toY); +}; + +#endif // SIGNALVIEW_H diff --git a/gui/signalview.ui b/gui/signalview.ui new file mode 100644 index 0000000..03f18f1 --- /dev/null +++ b/gui/signalview.ui @@ -0,0 +1,185 @@ + + + SignalView + + + + 0 + 0 + 475 + 366 + + + + + 0 + 0 + + + + Form + + + + + + + + Type: + + + + + + + + + + + + + + + + + 0 + 0 + + + + + 0 + 0 + + + + QTabWidget::Rounded + + + 0 + + + Qt::ElideNone + + + + Graph + + + + + + + 0 + 0 + + + + + 0 + 10 + + + + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + - + + + Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter + + + true + + + + + + + Y: + + + + + + + - + + + + + + + + 0 + 0 + + + + - + + + + + + + X: + + + + + + + + + + Data + + + + + + + + + + Integration + + + + + + + + + + + + + + GraphView + QWidget +
gui/graphview.h
+ 1 +
+
+ + +
diff --git a/imgresources.qrc b/imgresources.qrc new file mode 100644 index 0000000..3ff4708 --- /dev/null +++ b/imgresources.qrc @@ -0,0 +1,5 @@ + + + resources/Qt_master_logo.png + + diff --git a/integratedpeak.cpp b/integratedpeak.cpp new file mode 100644 index 0000000..64ae1da --- /dev/null +++ b/integratedpeak.cpp @@ -0,0 +1,50 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "integratedpeak.h" + +IntegratedPeak::IntegratedPeak(IntegratedPeak::Type type, double auc, size_t peakIdx, double peakTime, double peakVal, + size_t fromIdx, size_t toIdx, double fromTime, double toTime, double fromY, double toY, QObject* parent) : + QObject(parent), + m_auc(auc), + m_fromIdx(fromIdx), + m_fromTime(fromTime), + m_fromY(fromY), + m_peakIdx(peakIdx), + m_peakTime(peakTime), + m_peakVal(peakVal), + m_toIdx(toIdx), + m_toTime(toTime), + m_toY(toY), + m_type(type) +{ +} + +double IntegratedPeak::baselineSlope() const +{ + return (m_toY - m_fromY) / (m_toIdx - m_fromIdx); +} + +double IntegratedPeak::height() const +{ + return fabs(m_peakVal - (baselineSlope() * (m_peakIdx - m_fromIdx) + m_fromY)); +} diff --git a/integratedpeak.h b/integratedpeak.h new file mode 100644 index 0000000..7db458d --- /dev/null +++ b/integratedpeak.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#ifndef INTEGRATEDPEAK_H +#define INTEGRATEDPEAK_H + +#include + +class IntegratedPeak : public QObject +{ + Q_OBJECT +public: + enum class Type { + 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, + double fromY, double toY, QObject* parent = nullptr); + double auc() const { return m_auc; } + double baselineSlope() const; + size_t fromIdx() const { return m_fromIdx; } + double fromY() const { return m_fromY; } + double fromTime() const { return m_fromTime;} + double height() const; + size_t peakIdx() const { return m_peakIdx; } + double peakTime() const { return m_peakTime; } + 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; } + +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; + +public slots: +signals: + +}; + +#endif // INTEGRATEDPEAK_H diff --git a/integrationtablemodel.cpp b/integrationtablemodel.cpp new file mode 100644 index 0000000..9a2c200 --- /dev/null +++ b/integrationtablemodel.cpp @@ -0,0 +1,76 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "integrationtablemodel.h" + +IntegrationTableModel::IntegrationTableModel(const std::shared_ptr integrator, QObject* parent) : + QAbstractTableModel(parent), + m_integrator(integrator) +{} + +QVariant IntegrationTableModel::data(const QModelIndex& index, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + if (index.row() >= m_integrator->peakCount()) + return QVariant(); + + const std::shared_ptr peak = m_integrator->peakByIdx(index.row()); + switch (index.column()) { + case 0: + return QString::number(peak->peakTime(), 'f', 4); + case 1: + return QString::number(peak->auc(), 'f', 4); + case 2: + return QString::number(peak->height(), 'f', 4); + case 3: + return QString::number(peak->width(), 'f', 4); + } + return QVariant(); +} + +QVariant IntegrationTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QString("Time"); + case 1: + return QString("Area"); + case 2: + return QString("Height"); + case 3: + return QString("Width"); + } + } + return QVariant(); +} + +int IntegrationTableModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + + if (m_integrator == nullptr) + return 0; + + return m_integrator->peakCount(); +} diff --git a/integrationtablemodel.h b/integrationtablemodel.h new file mode 100644 index 0000000..90daac4 --- /dev/null +++ b/integrationtablemodel.h @@ -0,0 +1,45 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef INTEGRATIONTABLEMODEL_H +#define INTEGRATIONTABLEMODEL_H + +#include "integrator.h" +#include +#include + +class IntegrationTableModel : public QAbstractTableModel +{ +public: + IntegrationTableModel(const std::shared_ptr integrator, QObject* parent = nullptr); + QVariant data(const QModelIndex& index, int role) const; + void doneReset() { endResetModel(); } + int columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 4; } + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + void prepReset() { beginResetModel(); } + int rowCount(const QModelIndex& parent) const; + +private: + const std::shared_ptr m_integrator; +}; + +#endif // INTEGRATIONTABLEMODEL_H diff --git a/integrator.cpp b/integrator.cpp new file mode 100644 index 0000000..8a811d2 --- /dev/null +++ b/integrator.cpp @@ -0,0 +1,157 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "integrator.h" +#include "logger.h" + +const QString Integrator::ME_SENDER_STR("Integrator"); + +Integrator::Integrator(std::shared_ptr signal, QObject* parent) : + QObject(parent), + m_signal(signal) +{} + +Integrator::ReturnCode Integrator::deletePeak(size_t idx) +{ + std::multimap>::iterator it = m_peaks.begin(); + while (it != m_peaks.end() && it->first <= idx) { + if (it->first <= idx && it->second->toIdx() > idx) { /* Peak is surrounding the cursor */ + m_peaks.erase(it); + return ReturnCode::SUCCESS; + } + it++; + } + return ReturnCode::E_NO_PEAK; +} + +Integrator::ReturnCode Integrator::integrate(size_t startIdx, size_t stopIdx, double startY, double stopY, std::shared_ptr& peak) +{ + size_t peakValueIdx; + size_t startIntersIdx, stopIntersIdx; + size_t i; + double auc = 0.0; + double slope; + double startYVal, stopYVal; + bool expectDownward; + IntegratedPeak::Type peakType; + + if (startIdx > m_signal->valuesCount()-1) { + Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "Invalid startIdx value " + QString::number(startIdx)); + return ReturnCode::E_INVAL; + } + if (stopIdx > m_signal->valuesCount()-1) { + Logger::log(Logger::Level::ERROR, ME_SENDER_STR, "Invalid stopIdx value " + QString::number(stopIdx)); + return ReturnCode::E_INVAL; + } + + slope = (stopY - startY) / (stopIdx - startIdx); + /* Try to find first intersection */ + if (startY < m_signal->valueAt(startIdx)) { + expectDownward = true; + //qDebug() << "Expecting downward peak"; + } else { + expectDownward = false; + //qDebug() << "Expecting upward peak"; + } + for (i = startIdx; i <= stopIdx; i++) { + double value = m_signal->valueAt(i); + double blY = slope * (i - startIdx) + startY; + + if (expectDownward && blY > value) { /* Found first intersection */ + startIntersIdx = i-1; + startYVal = blY; + break; + } else if (!expectDownward && blY < value) { /* Found first intersection */ + startIntersIdx = i-1; + startYVal = blY; + break; + } + } + if (i > stopIdx) { + Logger::log(Logger::Level::INFO, ME_SENDER_STR, "First intersection not found."); + return ReturnCode::E_NO_FIRST_INTERSECT; + } + + /* Try to find second intersection */ + for (; i <= stopIdx; i++) { + double value = m_signal->valueAt(i); + double blY = slope * (i - startIdx) + startY; + + if (expectDownward && blY <= value) { + stopIntersIdx = i; + stopYVal = blY; + break; + } else if (!expectDownward && blY >= value) { + stopIntersIdx = i; + stopYVal = blY; + break; + } + } + if (i > stopIdx) { + Logger::log(Logger::Level::INFO, ME_SENDER_STR, "Second intersection not found."); + return ReturnCode::E_NO_SECOND_INTERSECT; + } + + /* Integrate */ + peakValueIdx = startIntersIdx; + for (i = startIntersIdx+1; i < stopIntersIdx; i++) { + double blXA = slope * (i - startIntersIdx - 1) + startY; + double blXB = slope * (i - startIntersIdx) + startY; + double valA = m_signal->valueAt(i-1); + double valB = m_signal->valueAt(i); + double tA = m_signal->timeAt(i-1); + double tB = m_signal->timeAt(i); + double avgH = (valB + valA)/2 - (blXB + blXA)/2; + double area = avgH * (tB - tA); + auc += area; + if (expectDownward && valB < m_signal->valueAt(peakValueIdx)) peakValueIdx = i; + else if (!expectDownward && valB > m_signal->valueAt(peakValueIdx)) peakValueIdx = i; + //qDebug() << area << auc; + } + /* Convert AUC to positive and seconds */ + auc = fabs(auc) * 60; + + /* Add peak to list */ + if (expectDownward) peakType = IntegratedPeak::Type::VALLEY; + else peakType = IntegratedPeak::Type::PEAK; + + /*qDebug("Peak integrated"); + qDebug() << "SXC" << fromX << "SYC" << fromY << "EXC" << toX << "EYC" << toY << "Slope" << slope; + qDebug() << "1st(X, Y, vY)" << startIntersIdx << m_signal->valueAt(startIntersIdx) << startYVal; + qDebug() << "2nd(X, Y, vY)" << stopIntersIdx << m_signal->valueAt(stopIntersIdx) << stopYVal; + qDebug("---");*/ + + peak = std::shared_ptr(new IntegratedPeak(peakType, auc, peakValueIdx, m_signal->timeAt(peakValueIdx), m_signal->valueAt(peakValueIdx), + startIntersIdx, stopIntersIdx, m_signal->timeAt(startIntersIdx), m_signal->timeAt(stopIntersIdx), + startYVal, stopYVal)); + m_peaks.insert(std::pair>(startIdx, peak)); + + return ReturnCode::SUCCESS; +} + +const std::shared_ptr Integrator::peakByIdx(const int idx) const +{ + std::multimap>::const_iterator it = m_peaks.begin(); + std::advance(it, idx); + + return it->second; +} diff --git a/integrator.h b/integrator.h new file mode 100644 index 0000000..15fd076 --- /dev/null +++ b/integrator.h @@ -0,0 +1,64 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef INTEGRATOR_H +#define INTEGRATOR_H + +#include "integratedpeak.h" +#include "signal.h" +#include +#include +#include + +class Integrator : public QObject +{ + Q_OBJECT +public: + enum class ReturnCode { + SUCCESS, + E_NO_FIRST_INTERSECT, + E_NO_SECOND_INTERSECT, + E_INVAL, + E_NO_PEAK + }; + + explicit Integrator(std::shared_ptr signal, QObject* parent = nullptr); + ReturnCode deletePeak(size_t idx); + ReturnCode integrate(size_t startIdx, size_t stopIdx, double startY, double stopY, std::shared_ptr& peak); + const std::shared_ptr peakByIdx(const int idx) const; + std::multimap>::const_iterator peaksCBegin() const { return m_peaks.cbegin(); } + std::multimap>::const_iterator peaksCEnd() const { return m_peaks.cend(); } + size_t peakCount() const { return m_peaks.size(); } + +private: + std::multimap> m_peaks; + std::shared_ptr m_signal; + + static const QString ME_SENDER_STR; + +signals: + +public slots: + +}; + +#endif // INTEGRATOR_H diff --git a/locallogger.cpp b/locallogger.cpp new file mode 100644 index 0000000..a808d2c --- /dev/null +++ b/locallogger.cpp @@ -0,0 +1,62 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "locallogger.h" + +/* Static methods */ +LocalLoggerPtr LocalLogger::create(QObject* parent) +{ + return std::shared_ptr(new LocalLogger(parent)); +} + +/* ENDOF static methods */ + +LocalLogger::LocalLogger(QObject* parent) : Logger(parent) +{ +} + +void LocalLogger::log(Level level, const QString& sender, const QString& message, bool global) +{ + LoggedInfo li = std::make_tuple(QDateTime::currentMSecsSinceEpoch(), level, sender, message); + this->m_lock.lock(); + this->m_logged.push_back(li); + this->m_lock.unlock(); + + if (global) + Logger::log(level, sender, message, true); + if (level >= m_printLogLevel) + readLast(); +} + +void LocalLogger::read(Level level, uint length) +{ + Logger::read(level, length, this); +} + +void LocalLogger::readLast() +{ + Logger::readLast(this); +} + +LocalLogger::~LocalLogger() +{ +} diff --git a/locallogger.h b/locallogger.h new file mode 100644 index 0000000..efc160c --- /dev/null +++ b/locallogger.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#ifndef LOCALLOGGER_H +#define LOCALLOGGER_H + +#include "logger.h" + +class LocalLogger : public Logger +{ +public: + static LocalLoggerPtr create(QObject* parent); + ~LocalLogger(); + void log(Level level, const QString& sender, const QString& message, bool global = false); + void read(Level level, uint length = 0); + void readLast(); + void setPrintLevel(Level level) { m_printLogLevel = level; } + +private: + LocalLogger(QObject* parent = nullptr); +}; + +#endif // LOCALLOGGER_H diff --git a/logger.cpp b/logger.cpp new file mode 100644 index 0000000..e153eed --- /dev/null +++ b/logger.cpp @@ -0,0 +1,136 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "logger.h" + +#include + +Logger* Logger::s_globalInstance(nullptr); + +/* Static methods */ +LocalLoggerPtr Logger::createLocalLog() +{ + LocalLoggerPtr localLogger(LocalLogger::create(s_globalInstance)); + + return localLogger; +} + +void Logger::initializeGlobal() +{ + if (Logger::s_globalInstance == nullptr) + Logger::s_globalInstance = new Logger(); +} + +const QString Logger::levelToString(Level level) +{ + switch (level) { + case DEBUG: + return "DEBUG"; + case INFO: + return "INFO"; + case WARNING: + return "WARNING"; + case ERROR: + return "ERROR"; + case CRITICAL: + return "CRITICAL"; + default: + return "UNKNOWN!"; + } +} + +void Logger::log(Level level, const QString& sender, const QString& message, bool suppressPrint) +{ + LoggedInfo li = std::make_tuple(QDateTime::currentMSecsSinceEpoch(), level, sender, message); + s_globalInstance->m_lock.lock(); + s_globalInstance->m_logged.push_back(li); + s_globalInstance->m_lock.unlock(); + + if (level >= s_globalInstance->m_printLogLevel && !suppressPrint) + readLast(); +} + +void Logger::read(Level level, uint length, Logger* logger) +{ + Logger* _logger; + size_t logSize; + size_t from, to; + + if (logger == nullptr) + _logger = s_globalInstance; + else + _logger = logger; + logSize = _logger->m_logged.size(); + + if (logSize < 1) + return; + + if (length == 0) { + from = 0; + to = logSize - 1; + } else if (logSize < length) { + from = 0; + to = logSize - 1; + } else { + from = logSize - length - 1; + to = logSize - 1; + } + + while (from <= to) { + const LoggedInfo& li = _logger->m_logged.at(from); + if (std::get<1>(li) >= level) { + QString out; + out = QString::number(std::get<0>(li)); + out += " - " + Logger::levelToString(std::get<1>(li)); + out += ": " + std::get<2>(li); + out += ":: " + std::get<3>(li); + qDebug() << out; + } + from++; + } +} + +void Logger::readLast(Logger* logger) +{ + Logger* _logger; + QString out; + + if (logger == nullptr) + _logger = s_globalInstance; + else + _logger = logger; + + const LoggedInfo& li = _logger->m_logged.back(); + out = QString::number(std::get<0>(li)) + " - " + Logger::levelToString(std::get<1>(li)) + ": " + std::get<2>(li) + ":: " + std::get<3>(li); + qDebug() << out; +} + +/* ENDOF static methods */ + +Logger::Logger(QObject* parent) : QObject(parent) +{ +} + +Logger::~Logger() +{ + Logger::read(Logger::Level::DEBUG); +} diff --git a/logger.h b/logger.h new file mode 100644 index 0000000..2288688 --- /dev/null +++ b/logger.h @@ -0,0 +1,72 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + + +#ifndef LOGGER_H +#define LOGGER_H + +#include +#include +#include +#include +#include + +class LocalLogger; +typedef std::shared_ptr LocalLoggerPtr; + +class Logger : public QObject +{ + Q_OBJECT +public: + enum Level { + DEBUG = 0, + INFO = 1, + WARNING = 2, + ERROR = 3, + CRITICAL = 4 + }; + + ~Logger(); + static LocalLoggerPtr createLocalLog(); + static void initializeGlobal(); + static void log(Level level, const QString& sender, const QString& message, bool suppressPrint = false); + static void read(Level level, uint length = 0, Logger* logger = nullptr); + static void readLast(Logger* logger = nullptr); + static void setPrintLevel(Level level) { s_globalInstance->m_printLogLevel = level; } + +protected: + typedef std::tuple LoggedInfo; + + static const QString levelToString(Level level); + + explicit Logger(QObject* parent = nullptr); + + std::mutex m_lock; + std::vector m_logged; + Level m_printLogLevel; + + static Logger* s_globalInstance; + +}; + +#include "locallogger.h" +#endif // LOGGER_H diff --git a/main.cpp b/main.cpp new file mode 100644 index 0000000..bb4b0e3 --- /dev/null +++ b/main.cpp @@ -0,0 +1,75 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "gui/mainwindow.h" +#include "datamanager.h" +#include "logger.h" +#include +#include + +int main(int argc, char *argv[]) +{ + QApplication a(argc, argv); + +#warning Revisit the metatype shared_ptr registration + qRegisterMetaType>(); + + Logger::initializeGlobal(); + Logger::setPrintLevel(Logger::Level::DEBUG); + std::unique_ptr dMgr; + std::unique_ptr mWin; + + AGREInterface::ReturnCode aRet = AGREInterface::instance()->initialize(); + if (aRet == AGREInterface::ReturnCode::E_NO_LIBRARY) { + QMessageBox::critical(nullptr, "Dependencies error", "Library " + AGREInterface::AGRE_LIBRARY_NAME + " could not have been found.\n" + "The application cannot continue."); + return 1; + } else if (aRet == AGREInterface::ReturnCode::E_CANNOT_RESOLVE) { + QMessageBox::critical(nullptr, "libAGRE error", "Some symbols from " + AGREInterface::AGRE_LIBRARY_NAME + " could not have been resolved.\n" + "The application cannot continue."); + return 1; + } else if (aRet == AGREInterface::ReturnCode::E_CANNOT_GET_IFACE) { + QMessageBox::critical(nullptr, "libAGRE error", "An error occured during data reader initialization.\nThe application cannot continue."); + return 1; + } + + dMgr = std::unique_ptr(new DataManager); + mWin = std::unique_ptr(new MainWindow); + mWin->setSequenceListModel(dMgr->sequenceSelectorModel()); + mWin->setSingleRunsListModel(dMgr->singleRunsSelectorModel()); + /* Default to no sequence on load */ + mWin->onSetActiveSequenceIndex(-1); + /* Connect SIGNALS/SLOTS */ + QObject::connect(dMgr.get(), SIGNAL(addToDashboard(SignalView*)), mWin.get(), SLOT(onAddToDashboard(SignalView*))); + QObject::connect(dMgr.get(), SIGNAL(cleanDashboard()), mWin.get(), SLOT(onCleanDashboard())); + 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(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))); + QObject::connect(mWin.get(), SIGNAL(singleRunSelected(QString)), dMgr.get(), SLOT(onSingleRunSelected(QString))); + + mWin->show(); + + return a.exec(); +} diff --git a/mathhelpers.h b/mathhelpers.h new file mode 100644 index 0000000..f608875 --- /dev/null +++ b/mathhelpers.h @@ -0,0 +1,33 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef MATHHELPERS_H +#define MATHHELPERS_H + +template T average(T* vals, size_t len) { + T sum = 0; + for (int i = 0; i < len; i++) + sum += vals[i]; + return sum / len; +} + +#endif // MATHHELPERS_H diff --git a/metatypes.h b/metatypes.h new file mode 100644 index 0000000..96323fe --- /dev/null +++ b/metatypes.h @@ -0,0 +1,36 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef METATYPES_H +#define METATYPES_H + +class SignalView; +typedef std::shared_ptr SignalViewPtr; +Q_DECLARE_METATYPE(SignalViewPtr); + +enum class GraphControlModes { + ZOOM, + INTEGRATE +}; +Q_DECLARE_METATYPE(GraphControlModes); + +#endif // METATYPES_H diff --git a/resources/Qt_master_logo.png b/resources/Qt_master_logo.png new file mode 100644 index 0000000..6d48935 Binary files /dev/null and b/resources/Qt_master_logo.png differ diff --git a/sequence.cpp b/sequence.cpp new file mode 100644 index 0000000..52b415d --- /dev/null +++ b/sequence.cpp @@ -0,0 +1,110 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "sequence.h" + +Sequence::Sequence(QObject* parent) : + QObject(parent) +{ +} + +Sequence::Sequence(std::vector& singleRuns, QObject* parent) : + QObject(parent), + m_selectedRunKey(""), + m_singleRuns(singleRuns) +{ +} + +int Sequence::add(QString name, std::shared_ptr value) +{ + m_singleRuns.push_back(SingleRunPair(name, value)); + m_selectedRunKey = name; + return m_singleRuns.size()-1; +} + +void Sequence::addController(const std::string& uid, std::shared_ptr controller) +{ + m_signalCtrls[uid] = controller; +} + +SingleRunPair Sequence::at(const size_t idx) +{ + return m_singleRuns.at(idx); +} + +std::shared_ptr Sequence::controller(const std::string& uid) +{ + std::unordered_map>::iterator it; + it = m_signalCtrls.find(uid); + if (it == m_signalCtrls.end()) + return nullptr; + else + return it->second; +} + +std::vector::iterator Sequence::find(const QString& key) +{ + std::vector::iterator it = m_singleRuns.begin(); + while (it != m_singleRuns.end()) { + if (it->first == key) + return it; + it++; + } + return m_singleRuns.end(); +} + +void Sequence::remove(const QString& key) +{ + std::vector::iterator it = m_singleRuns.begin(); + while (it++ != m_singleRuns.end()) { + if (it->first == key) + m_singleRuns.erase(it); + } +} + +void Sequence::removeAt(const size_t idx) +{ + if (m_singleRuns.size() > idx) + m_singleRuns.erase(m_singleRuns.begin()+idx); +} + +int Sequence::selectedRunIdx() const +{ + int idx = 0; + for (const SingleRunPair& p : m_singleRuns) { + if (p.first.compare(m_selectedRunKey) == 0) + return idx; + idx++; + } + return -1; +} + +int Sequence::singleRunToIdx(const QString& key) const +{ + int idx = 0; + for (const SingleRunPair& p : m_singleRuns) { + if (p.first.compare(key) == 0) + return idx; + idx++; + } + return -1; +} diff --git a/sequence.h b/sequence.h new file mode 100644 index 0000000..c8f986e --- /dev/null +++ b/sequence.h @@ -0,0 +1,70 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SEQUENCE_H +#define SEQUENCE_H + +#include "signalcontroller.h" +#include "singlerundata.h" +#include +#include +#include +#include + +typedef std::pair> SingleRunPair; + +class Sequence : public QObject +{ + Q_OBJECT +public: + explicit Sequence(QObject* parent = nullptr); + explicit Sequence(std::vector& singleRuns, QObject* parent = nullptr); + int add(QString name, std::shared_ptr value); + void addController(const std::string& uid, std::shared_ptr controller); + const std::vector* allRuns() const { return &m_singleRuns; } + SingleRunPair at(size_t idx); + std::vector::iterator begin() { return m_singleRuns.begin(); } + std::vector::const_iterator cbegin() const { return m_singleRuns.cbegin(); } + std::vector::const_iterator cend() const { return m_singleRuns.cend(); } + std::shared_ptr controller(const std::string& uid); + size_t count() const { return m_singleRuns.size(); } + std::vector::iterator end() { return m_singleRuns.end(); } + std::vector::iterator find(const QString& key); + void remove(const QString& key); + void removeAt(const size_t idx); + const QString& selectedRunKey() const { return m_selectedRunKey; } + int selectedRunIdx() const; + int singleRunToIdx(const QString& key) const; + void setSelectedRunKey(const QString& key) { m_selectedRunKey = key; } + +private: + QString m_selectedRunKey; + std::unordered_map> m_signalCtrls; + std::vector m_singleRuns; + +signals: + +public slots: + +}; + +#endif // SEQUENCE_H diff --git a/sequenceselectormodel.cpp b/sequenceselectormodel.cpp new file mode 100644 index 0000000..d37d6c9 --- /dev/null +++ b/sequenceselectormodel.cpp @@ -0,0 +1,75 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "sequenceselectormodel.h" + +SequenceSelectorModel::SequenceSelectorModel(QObject* parent) : + QAbstractListModel(parent), + m_activeSequences(nullptr) +{ +} + +SequenceSelectorModel::SequenceSelectorModel(const std::vector* sequences, QObject* parent) : + QAbstractListModel(parent), + m_activeSequences(sequences) +{ +} + +QVariant SequenceSelectorModel::data(const QModelIndex& index, int role) const +{ + int seqIdx = 0; + if (role != Qt::DisplayRole) + return QVariant(); + if (m_activeSequences == nullptr) + return QVariant(); + + std::vector::const_iterator cit = m_activeSequences->cbegin(); + while (cit != m_activeSequences->cend()) { + if (seqIdx == index.row()) + return cit->first; + seqIdx++; cit++; + } + return QVariant(); +} + +int SequenceSelectorModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + + if (m_activeSequences == nullptr) + return 0; + else + return m_activeSequences->size(); +} + +void SequenceSelectorModel::seqenceAdded() +{ + beginInsertRows(QModelIndex(), m_activeSequences->size()-1, m_activeSequences->size()-1); + endInsertRows(); +} + +void SequenceSelectorModel::setSequences(const std::vector* sequences) +{ + beginResetModel(); + m_activeSequences = sequences; + endResetModel(); +} diff --git a/sequenceselectormodel.h b/sequenceselectormodel.h new file mode 100644 index 0000000..9b2441f --- /dev/null +++ b/sequenceselectormodel.h @@ -0,0 +1,46 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SEQUENCESELECTORMODEL_H +#define SEQUENCESELECTORMODEL_H + +#include "sequence.h" +#include +#include + +typedef std::pair> NameSequencePair; + +class SequenceSelectorModel : public QAbstractListModel +{ +public: + SequenceSelectorModel(QObject* parent = nullptr); + SequenceSelectorModel(const std::vector* sequences, QObject* parent = nullptr); + QVariant data(const QModelIndex& index, int role) const; + void seqenceAdded(); + int rowCount(const QModelIndex& parent) const; + void setSequences(const std::vector* sequence); + +private: + const std::vector* m_activeSequences; +}; + +#endif // SEQUENCESELECTORMODEL_H diff --git a/signal.cpp b/signal.cpp new file mode 100644 index 0000000..4262ee2 --- /dev/null +++ b/signal.cpp @@ -0,0 +1,137 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "signal.h" +#include + +const QString Signal::RES_ABSORBANCE("Absorbance"); +const QString Signal::RES_CCD("CCD"); +const QString Signal::RES_CURRENT("Current"); +const QString Signal::RES_DAD("Absorbance"); +const QString Signal::RES_POWER("Power"); +const QString Signal::RES_VOLTAGE("Voltage"); +const QString Signal::KILOVOTLS_SI("KV"); +const QString Signal::MICROAMPERES_SI("\u03BCA"); +const QString Signal::MILLIAU_SI("mAU"); +const QString Signal::MILLIVOLTS_SI("mV"); +const QString Signal::WATTS_SI("W"); + +Signal::Signal(const Equipment equipment, const Resource resource, const double samplingRate, const YUnit yunit, uint16_t wavelengthAbs, uint16_t wavelengthRef, + const std::vector& values, QObject* parent) : + QObject(parent), + m_equipment(equipment), + m_resource(resource), + m_samplingRate(samplingRate), + m_values(values), + m_wavelengthAbs(wavelengthAbs), + m_wavelengthRef(wavelengthRef), + m_xunit(XUnit::MINUTE), + m_yunit(yunit) +{ + m_min = DBL_MAX; + m_max = DBL_MIN; + for (Signal::TimeValuePair tvp : values) { + if (tvp.second < m_min) m_min = tvp.second; + if (tvp.second > m_max) m_max = tvp.second; + } +} + +std::vector::const_iterator Signal::iteratorFrom(const size_t idx) const +{ + return m_values.cbegin()+idx-1; +} + +Signal::TimeValuePair Signal::pairAt(const size_t idx) const +{ + if (idx >= m_values.size()) + return m_values.back(); + else + return m_values.at(idx); +} + +QString Signal::resourceToString() const { + switch (m_resource) { + case Resource::CE_ABSORBANCE: + return RES_ABSORBANCE; + case Resource::CE_CCD: + return RES_CCD; + case Resource::CE_CURRENT: + return RES_CURRENT; + case Resource::CE_DAD: + return RES_DAD; + case Resource::CE_POWER: + return RES_POWER; + case Resource::CE_VOLTAGE: + return RES_VOLTAGE; + default: + return "?"; + } +} + +double Signal::timeAt(const size_t idx) const +{ + if (idx >= m_values.size()) + return m_values.back().first; + else + return m_values.at(idx).first; +} + +QString Signal::uidString() const +{ + return resourceToString() + QString::number(m_wavelengthAbs) + QString::number(m_wavelengthRef); +} + +double Signal::valueAt(const size_t idx) const +{ + if (idx >= m_values.size()) + return m_values.back().second; + else + return m_values.at(idx).second; +} + +QString Signal::xunitToString() const +{ + switch (m_xunit) { + case XUnit::MINUTE: + return "min"; + default: + return "?"; + } +} + +QString Signal::yunitToString() const +{ + switch (m_yunit) { + case YUnit::KILOVOLTS: + return KILOVOTLS_SI; + case YUnit::MICROAMPERES: + return MICROAMPERES_SI; + case YUnit::MILLIAU: + return MILLIAU_SI; + case YUnit::MILLIVOLTS: + return MILLIVOLTS_SI; + case YUnit::WATTS: + return WATTS_SI; + default: + return "?"; + } +} diff --git a/signal.h b/signal.h new file mode 100644 index 0000000..4284029 --- /dev/null +++ b/signal.h @@ -0,0 +1,109 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SIGNAL_H +#define SIGNAL_H + +#include +#include + +class Signal : public QObject +{ + Q_OBJECT +public: + typedef std::pair TimeValuePair; + enum class Equipment { + CE + }; + enum class Resource { + CE_ABSORBANCE, + CE_CCD, + CE_CURRENT, + CE_DAD, + CE_POWER, + CE_VOLTAGE + }; + enum class XUnit { + MINUTE + }; + enum class YUnit { + KILOVOLTS, + MICROAMPERES, + MILLIAU, + MILLIVOLTS, + WATTS + }; + + 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& values, QObject* parent = nullptr); + Equipment equipment() const { return m_equipment; } + std::vector::const_iterator iteratorFrom(const size_t idx) const; + double maximum() const { return m_max; } + double minimum() const { return m_min; } + TimeValuePair pairAt(const size_t idx) const; + Resource resource() const { return m_resource; } + QString resourceToString() const; + double timeAt(const size_t idx) const; + double samplingRate() const { return m_samplingRate; } + QString uidString() const; + double valueAt(const size_t idx) const; + std::vector::const_iterator valuesBegin() const { return m_values.begin(); } + size_t valuesCount() const { return m_values.size(); } + std::vector::const_iterator valuesEnd() const { return m_values.end(); } + uint16_t wavelengthAbsorbed() const { return m_wavelengthAbs; } + uint16_t wavelengthReference() const { return m_wavelengthRef; } + XUnit xunit() const { return m_xunit; } + QString xunitToString() const; + YUnit yunit() const { return m_yunit; } + QString yunitToString() const; + +private: + const Equipment m_equipment; + double m_max; + double m_min; + const Resource m_resource; + const double m_samplingRate; + const std::vector m_values; + const uint16_t m_wavelengthAbs; + const uint16_t m_wavelengthRef; + const XUnit m_xunit; + const YUnit m_yunit; + + static const QString RES_ABSORBANCE; + static const QString RES_CCD; + static const QString RES_CURRENT; + static const QString RES_DAD; + static const QString RES_POWER; + static const QString RES_VOLTAGE; + static const QString KILOVOTLS_SI; + static const QString MICROAMPERES_SI; + static const QString MILLIAU_SI; + static const QString MILLIVOLTS_SI; + static const QString WATTS_SI; + +signals: + +public slots: + +}; + +#endif // SIGNAL_H diff --git a/signalcontroller.cpp b/signalcontroller.cpp new file mode 100644 index 0000000..65cfc96 --- /dev/null +++ b/signalcontroller.cpp @@ -0,0 +1,288 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "signalcontroller.h" +#include "logger.h" +#include + +#include + +const QString SignalController::ME_SENDER_STR("SignalController"); + +SignalController::SignalController(std::shared_ptr signal, QObject* parent) : + QObject(parent), + m_signal(signal) +{ + m_integrator = std::shared_ptr(new Integrator(signal)); + m_integTblModel = new IntegrationTableModel(m_integrator); + m_dtblModel = new SignalDataTableModel(signal); + + m_yMin = m_signal->minimum() - m_signal->minimum()*0.05; + m_yMax = m_signal->maximum() + m_signal->maximum()*0.05; + m_fromIdx = 0; m_toIdx = m_signal->valuesCount()-1; + + connect(m_ctxMenu.m_acZoomOut, SIGNAL(triggered()), this, SLOT(onCtxMenuUnzoom())); + connect(m_ctxMenu.m_acDeletePeak, SIGNAL(triggered()), this, SLOT(onCtxMenuDeletePeak())); +} + +/* Public methods */ +void SignalController::draw() +{ + drawGraph(); + drawAllPeaks(); +} + +/* Private methods */ + +void SignalController::drawAllPeaks() +{ + std::multimap>::const_iterator cit = m_integrator->peaksCBegin(); + while (cit != m_integrator->peaksCEnd()) + drawIntegratedPeak((cit++)->second); +} + +void SignalController::drawFullGraph() +{ + m_yMin = m_signal->minimum() - m_signal->minimum()*0.05; + m_yMax = m_signal->maximum() + m_signal->maximum()*0.05; + if (m_signal->valuesCount() < 1) + return; + m_fromIdx = 0; m_toIdx = m_signal->valuesCount()-1; + + qDebug() << m_yMin << m_yMax; + + drawGraph(); +} + +void SignalController::drawGraph() +{ + double* arr; + size_t len; + + if (m_fromIdx > m_toIdx) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "From index lower than to index"); + return; + } + if (m_fromIdx == m_toIdx) + return; //Nothing to zoom to + if (m_toIdx >= m_signal->valuesCount() || m_fromIdx >= m_signal->valuesCount()) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid value index"); + return; + } + len = m_toIdx - m_fromIdx; + + arr = new double[len]; + for (uint idx = 0; idx < len; idx++) + arr[idx] = m_signal->valueAt(idx + m_fromIdx); + + emit guiDrawGraph(arr, len, m_yMin, m_yMax); +} + +void SignalController::drawIntegratedPeak(const std::shared_ptr peak) +{ + int fromX, toX; + double fromY, toY; + bool valley; + if (peak->toIdx() < m_fromIdx || peak->fromIdx() > m_toIdx) /* Peak is not in view X-wise, skip it */ + return; + + /* Check if the start of the baseline is in view */ + if (peak->fromIdx()-1 >= m_fromIdx) { + fromX = peak->fromIdx() - m_fromIdx; + fromY = peak->fromY(); + } else { + fromX = 0; + double bls = peak->baselineSlope(); + fromY = bls * (m_fromIdx - peak->fromIdx()) + peak->fromY(); + qDebug() << "Start OOR" << fromY << bls; + } + /* Check if the end of the baseline is in view */ + if (peak->toIdx() <= m_toIdx) { + toX = peak->toIdx() - m_fromIdx; + toY = peak->toY(); + } else { + double bls = peak->baselineSlope(); + toX = m_toIdx - m_fromIdx; + toY = bls * (m_toIdx - peak->fromIdx()) + peak->fromY(); + qDebug() << "Stop OOR" << toY << bls; + } + + /*qDebug("Drawing integration"); + qDebug() << "SX" << fromX << "SY" << fromY << "EX" << toX << "EY" << toY; + qDebug() << "Slope P" << peak->baselineSlope() << "Slope M" << (toY-fromY)/(toX-fromX); + qDebug("---");*/ + + const QString peakTime = QString::number(peak->peakTime(), 'f', 4); + const QString peakArea = QString("A: ") + QString::number(peak->auc(), 'f', 4); + if (peak->type() == IntegratedPeak::Type::PEAK) + valley = false; + else + valley = true; + emit viewDrawIntegration(fromX, fromY, toX, toY, peak->peakIdx() - m_fromIdx, m_signal->valueAt(peak->peakIdx()), peakTime, peakArea, valley); +} + +bool SignalController::updateConstraints(const int fromX, const int fromY, const int toX, const int toY) +{ + size_t xRange = m_toIdx - m_fromIdx; + double yRange; + if (m_viewWidth < 1) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid viewWidth value " + QString::number(m_viewWidth)); + return false; + } + if (m_viewHeight < 1) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "Invalid viewHeight value " + QString::number(m_viewHeight)); + return false; + } + if (fromX > toX) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "fromX > toX"); + return false; + } + if (fromY > toY) { + Logger::log(Logger::Level::CRITICAL, ME_SENDER_STR, "fromY > toY"); + return false; + } + + m_toIdx = (xRange * toX) / m_viewWidth + m_fromIdx; + m_fromIdx = (xRange * fromX) / m_viewWidth + m_fromIdx; + + yRange = m_yMax - m_yMin; + m_yMin = (yRange * toY / -m_viewHeight) + m_yMax; + m_yMax = (yRange * fromY / -m_viewHeight) + m_yMax; + + //qDebug() << fromX << fromY << toX << toY; + //qDebug() << m_fromIdx << m_toIdx << m_yMin << m_yMax; + + return true; +} + +double SignalController::yValToCoord(double y) +{ + return m_viewHeight / (m_yMax - m_yMin) * (y - m_yMin); +} + +/* Public slots */ + +void SignalController::onCtxMenuUnzoom() +{ + drawFullGraph(); + drawAllPeaks(); +} + +void SignalController::onCtxMenuDeletePeak() +{ + size_t xRange = m_toIdx - m_fromIdx; + size_t idx = (xRange * m_ctxMenuX) / m_viewWidth + m_fromIdx; + + m_integTblModel->prepReset(); + Integrator::ReturnCode ret = m_integrator->deletePeak(idx); + + switch (ret) { + case Integrator::ReturnCode::SUCCESS: + onViewRedrawNeeded(); + break; + case Integrator::ReturnCode::E_NO_PEAK: + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "No peak to delete."); + break; + default: + break; + } + m_integTblModel->doneReset(); + +} + +void SignalController::onViewCrosshairErased() +{ + emit viewRemoveCurrentValues(); +} + +void SignalController::onViewCrosshairMoved(const int x, const int y) +{ + Q_UNUSED(y); + + size_t xRange = m_toIdx - m_fromIdx; + int valueIdx = (xRange * x) / m_viewWidth + m_fromIdx; + Signal::TimeValuePair tvp = m_signal->pairAt(valueIdx); + emit viewUpdateCurrentValues(tvp.first, tvp.second); +} + +void SignalController::onViewIntegrated(const int fromX, const int fromY, const int toX, const int toY) +{ + size_t xRange = m_toIdx - m_fromIdx; + size_t startIdx = (xRange * fromX) / m_viewWidth + m_fromIdx; + size_t stopIdx = (xRange * toX) / m_viewWidth + m_fromIdx; + double yRange = m_yMax - m_yMin; + double blStartY = (yRange * (m_viewHeight - fromY) / m_viewHeight) + m_yMin; + double blStopY = (yRange * (m_viewHeight - toY) / m_viewHeight) + m_yMin; + Integrator::ReturnCode ret; + std::shared_ptr peak; + + m_integTblModel->prepReset(); + ret = m_integrator->integrate(startIdx, stopIdx, blStartY, blStopY, peak); + + switch (ret) { + case Integrator::ReturnCode::SUCCESS: + drawIntegratedPeak(peak); + break; + case Integrator::ReturnCode::E_NO_FIRST_INTERSECT: + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "no first intersect"); + break; + case Integrator::ReturnCode::E_NO_SECOND_INTERSECT: + Logger::log(Logger::Level::DEBUG, ME_SENDER_STR, "no first intersect"); + break; + default: + break; + } + m_integTblModel->doneReset(); +} + +void SignalController::onViewRedrawNeeded() +{ + drawGraph(); + drawAllPeaks(); +} + +void SignalController::onViewResized(const int w, const int h) +{ + m_viewHeight = h; + m_viewWidth = w; +} + +void SignalController::onViewShowContextMenu(const int x, const int y, const QPoint& globalPos) +{ + m_ctxMenuX = x; m_ctxMenuY = y; + m_ctxMenu.exec(globalPos); + emit viewCtxMenuClosed(); +} + +void SignalController::onViewZoomed(const int fromX, const int fromY, const int toX, const int toY) +{ + if (!updateConstraints(fromX, fromY, toX, toY)) + return; + drawGraph(); + drawAllPeaks(); +} + +SignalController::~SignalController() +{ + delete m_dtblModel; + delete m_integTblModel; +} diff --git a/signalcontroller.h b/signalcontroller.h new file mode 100644 index 0000000..a21d65c --- /dev/null +++ b/signalcontroller.h @@ -0,0 +1,92 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SIGNALCONTROLLER_H +#define SIGNALCONTROLLER_H + +#include "gui/graphviewcontextmenu.h" +#include "integrationtablemodel.h" +#include "integrator.h" +#include "signal.h" +#include "signaldatatablemodel.h" +#include +#include + +class SignalController : public QObject +{ + Q_OBJECT +public: + explicit SignalController(std::shared_ptr signal, QObject* parent = nullptr); + ~SignalController(); + void draw(); + SignalDataTableModel* dataTableModel() { return m_dtblModel; } + IntegrationTableModel* integrationTableModel() { return m_integTblModel;} + const std::shared_ptr signal() const { return m_signal; } + +private: + GraphViewContextMenu m_ctxMenu; + int m_ctxMenuX; + int m_ctxMenuY; + SignalDataTableModel* m_dtblModel; + uint m_fromIdx; + uint m_toIdx; + IntegrationTableModel* m_integTblModel; + std::shared_ptr m_integrator; + std::shared_ptr m_signal; + int m_viewHeight; + int m_viewWidth; + double m_yMax; + double m_yMin; + + void drawAllPeaks(); + void drawFullGraph(); + void drawGraph(); + void drawIntegratedPeak(const std::shared_ptr peak); + bool updateConstraints(const int fromX, const int fromY, const int toX, const int toY); + double yValToCoord(double y); + + static const QString ME_SENDER_STR; + +public slots: + void onCtxMenuUnzoom(); + void onCtxMenuDeletePeak(); + void onViewCrosshairErased(); + void onViewCrosshairMoved(const int x, const int y); + void onViewIntegrated(const int fromX, const int fromY, const int toX, const int toY); + void onViewRedrawNeeded(); + void onViewResized(const int w, const int h); + void onViewShowContextMenu(const int x, const int y, const QPoint& globalPos); + void onViewZoomed(const int fromX, const int fromY, const int toX, const int toY); + +signals: + void viewCtxMenuClosed(); + void viewDrawIntegration(const int fromX, const double fromY, const int toX, const double toY, const int peakX, const double peakY, const QString& peakTime, + const QString& peakArea, const bool valley); + void viewUpdateCurrentValues(double x, double y); + void viewRemoveCurrentValues(); + void guiDrawGraph(double* data, size_t len, double min, double max); + void fillDataList(); + + +}; + +#endif // SIGNALCONTROLLER_H diff --git a/signaldatatablemodel.cpp b/signaldatatablemodel.cpp new file mode 100644 index 0000000..e3f8544 --- /dev/null +++ b/signaldatatablemodel.cpp @@ -0,0 +1,68 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "signaldatatablemodel.h" + +SignalDataTableModel::SignalDataTableModel(std::shared_ptr signal, QObject* parent) : + QAbstractTableModel(parent), + m_signal(signal) +{} + +QVariant SignalDataTableModel::data(const QModelIndex& index, int role) const +{ + if (role != Qt::DisplayRole) + return QVariant(); + if (index.row() >= m_signal->valuesCount()) + return QVariant(); + + if (index.column() == 0) + return m_signal->timeAt(index.row()); + else if (index.column() == 1) + return m_signal->valueAt(index.row()); + + return QVariant(); +} + +QVariant SignalDataTableModel::headerData(int section, Qt::Orientation orientation, int role) const +{ + if (role == Qt::DisplayRole && orientation == Qt::Horizontal) { + switch (section) { + case 0: + return QString("Time ") + m_signal->xunitToString() + QString("]"); + case 1: + return QString("Value [") + m_signal->yunitToString() + QString("]"); + default: + return QVariant(); + } + } + return QVariant(); +} + +int SignalDataTableModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + + if (m_signal == nullptr) + return 0; + else + return m_signal->valuesCount(); +} diff --git a/signaldatatablemodel.h b/signaldatatablemodel.h new file mode 100644 index 0000000..2435a91 --- /dev/null +++ b/signaldatatablemodel.h @@ -0,0 +1,43 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SIGNALDATATABLEMODEL_H +#define SIGNALDATATABLEMODEL_H + +#include "signal.h" +#include +#include + +class SignalDataTableModel : public QAbstractTableModel +{ +public: + SignalDataTableModel(std::shared_ptr signal, QObject* parent = nullptr); + QVariant data(const QModelIndex& index, int role) const; + QVariant headerData(int section, Qt::Orientation orientation, int role) const; + int columnCount(const QModelIndex& parent) const { Q_UNUSED(parent); return 2; } + int rowCount(const QModelIndex& parent) const; + +private: + std::shared_ptr m_signal; +}; + +#endif // SIGNALDATATABLEMODEL_H diff --git a/singlerundata.cpp b/singlerundata.cpp new file mode 100644 index 0000000..fd5ef76 --- /dev/null +++ b/singlerundata.cpp @@ -0,0 +1,36 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "singlerundata.h" + +SingleRunData::SingleRunData(const QString& methodName, const QString& operatorName, const QString& sampleInfo, const QDate date, const QTime time, + std::vector>& sigs, const QString& dirname, QObject *parent) : + QObject(parent), + m_date(date), + m_dirName(dirname), + m_methodName(methodName), + m_operatorName(operatorName), + m_sampleInfo(sampleInfo), + m_signals(sigs), + m_time(time) +{ +} diff --git a/singlerundata.h b/singlerundata.h new file mode 100644 index 0000000..2fdd88c --- /dev/null +++ b/singlerundata.h @@ -0,0 +1,62 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SINGLERUNDATA_H +#define SINGLERUNDATA_H + +#include "signal.h" +#include +#include +#include +#include + +class SingleRunData : public QObject +{ + Q_OBJECT +public: + explicit SingleRunData(const QString& methodName, const QString& operatorName, const QString& sampleInfo, const QDate date, const QTime time, + std::vector>& sigs, const QString& dirname, QObject *parent = 0); + QDate date() const { return m_date; } + QString dirName() const { return m_dirName; } + QString methodName() const { return m_methodName; } + QString operatorName() const { return m_operatorName; } + QString sampleInfo() const { return m_sampleInfo; } + std::shared_ptr signalAt(size_t idx) { return m_signals.at(idx); } + size_t signalCount() const { return m_signals.size(); } + QTime time() const { return m_time; } + +private: + const QDate m_date; + const QString m_dirName; + const QString m_methodName; + const QString m_operatorName; + const QString m_sampleInfo; + std::vector> m_signals; + const QTime m_time; + +signals: + +public slots: + +}; + +#endif // SINGLERUNDATA_H diff --git a/singlerunsselectormodel.cpp b/singlerunsselectormodel.cpp new file mode 100644 index 0000000..4ee0293 --- /dev/null +++ b/singlerunsselectormodel.cpp @@ -0,0 +1,65 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#include "singlerunsselectormodel.h" + +#include + +SingleRunsSelectorModel::SingleRunsSelectorModel(QObject *parent) : + QAbstractListModel(parent), + m_singleRuns(nullptr) +{ +} + +QVariant SingleRunsSelectorModel::data(const QModelIndex& index, int role) const +{ + int runIdx = 0; + if (role != Qt::DisplayRole) + return QVariant(); + if (m_singleRuns == nullptr) + return QVariant(); + + std::vector::const_iterator cit = m_singleRuns->cbegin(); + while (cit != m_singleRuns->cend()) { + if (runIdx == index.row()) + return cit->first; + runIdx++; cit++; + } + return QVariant(); +} + +int SingleRunsSelectorModel::rowCount(const QModelIndex& parent) const +{ + Q_UNUSED(parent); + + if (m_singleRuns == nullptr) + return 0; + else + return m_singleRuns->size(); +} + +void SingleRunsSelectorModel::setSingleRuns(const std::vector* singleRuns) +{ + beginResetModel(); + m_singleRuns = singleRuns; + endResetModel(); +} diff --git a/singlerunsselectormodel.h b/singlerunsselectormodel.h new file mode 100644 index 0000000..a343ab3 --- /dev/null +++ b/singlerunsselectormodel.h @@ -0,0 +1,47 @@ +/* + Copyright (c) 2013 Michal Malý + + Permission is hereby granted, free of charge, to any person obtaining a copy + of this software and associated documentation files (the "Software"), to deal + in the Software without restriction, including without limitation the rights + to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + copies of the Software, and to permit persons to whom the Software is + furnished to do so, subject to the following conditions: + + The above copyright notice and this permission notice shall be included in + all copies or substantial portions of the Software. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN + THE SOFTWARE. +*/ + +#ifndef SINGLERUNSSELECTORMODEL_H +#define SINGLERUNSSELECTORMODEL_H + +#include "sequence.h" +#include + +class SingleRunsSelectorModel : public QAbstractListModel +{ + Q_OBJECT +public: + explicit SingleRunsSelectorModel(QObject* parent = nullptr); + QVariant data(const QModelIndex& index, int role) const; + int rowCount(const QModelIndex& parent) const; + void setSingleRuns(const std::vector* singleRuns); + +private: + const std::vector* m_singleRuns; + +signals: + +public slots: + +}; + +#endif // SINGLERUNSSELECTORMODEL_H