From db5357ea0e9648669b20bccd4c7c69b1cf2cac2f Mon Sep 17 00:00:00 2001 From: =?utf8?q?Michal=20Mal=C3=BD?= Date: Sun, 17 Nov 2013 02:44:08 +0100 Subject: [PATCH 1/1] Initial commit --- Anyanka.pro | 78 +++++ agreinterface.cpp | 145 ++++++++++ agreinterface.h | 77 +++++ datafilesloader.cpp | 89 ++++++ datafilesloader.h | 57 ++++ datamanager.cpp | 426 +++++++++++++++++++++++++++ datamanager.h | 92 ++++++ globalinfo.cpp | 27 ++ globalinfo.h | 41 +++ gui/aboutanyanka.cpp | 42 +++ gui/aboutanyanka.h | 45 +++ gui/aboutanyanka.ui | 133 +++++++++ gui/graphview.cpp | 50 ++++ gui/graphview.h | 50 ++++ gui/graphviewcontextmenu.cpp | 31 ++ gui/graphviewcontextmenu.h | 46 +++ gui/mainwindow.cpp | 159 ++++++++++ gui/mainwindow.h | 86 ++++++ gui/mainwindow.ui | 251 ++++++++++++++++ gui/signalview.cpp | 547 +++++++++++++++++++++++++++++++++++ gui/signalview.h | 120 ++++++++ gui/signalview.ui | 185 ++++++++++++ imgresources.qrc | 5 + integratedpeak.cpp | 50 ++++ integratedpeak.h | 72 +++++ integrationtablemodel.cpp | 76 +++++ integrationtablemodel.h | 45 +++ integrator.cpp | 157 ++++++++++ integrator.h | 64 ++++ locallogger.cpp | 62 ++++ locallogger.h | 43 +++ logger.cpp | 136 +++++++++ logger.h | 72 +++++ main.cpp | 75 +++++ mathhelpers.h | 33 +++ metatypes.h | 36 +++ resources/Qt_master_logo.png | Bin 0 -> 34038 bytes sequence.cpp | 110 +++++++ sequence.h | 70 +++++ sequenceselectormodel.cpp | 75 +++++ sequenceselectormodel.h | 46 +++ signal.cpp | 137 +++++++++ signal.h | 109 +++++++ signalcontroller.cpp | 288 ++++++++++++++++++ signalcontroller.h | 92 ++++++ signaldatatablemodel.cpp | 68 +++++ signaldatatablemodel.h | 43 +++ singlerundata.cpp | 36 +++ singlerundata.h | 62 ++++ singlerunsselectormodel.cpp | 65 +++++ singlerunsselectormodel.h | 47 +++ 51 files changed, 4951 insertions(+) create mode 100644 Anyanka.pro create mode 100644 agreinterface.cpp create mode 100644 agreinterface.h create mode 100644 datafilesloader.cpp create mode 100644 datafilesloader.h create mode 100644 datamanager.cpp create mode 100644 datamanager.h create mode 100644 globalinfo.cpp create mode 100644 globalinfo.h create mode 100644 gui/aboutanyanka.cpp create mode 100644 gui/aboutanyanka.h create mode 100644 gui/aboutanyanka.ui create mode 100644 gui/graphview.cpp create mode 100644 gui/graphview.h create mode 100644 gui/graphviewcontextmenu.cpp create mode 100644 gui/graphviewcontextmenu.h create mode 100644 gui/mainwindow.cpp create mode 100644 gui/mainwindow.h create mode 100644 gui/mainwindow.ui create mode 100644 gui/signalview.cpp create mode 100644 gui/signalview.h create mode 100644 gui/signalview.ui create mode 100644 imgresources.qrc create mode 100644 integratedpeak.cpp create mode 100644 integratedpeak.h create mode 100644 integrationtablemodel.cpp create mode 100644 integrationtablemodel.h create mode 100644 integrator.cpp create mode 100644 integrator.h create mode 100644 locallogger.cpp create mode 100644 locallogger.h create mode 100644 logger.cpp create mode 100644 logger.h create mode 100644 main.cpp create mode 100644 mathhelpers.h create mode 100644 metatypes.h create mode 100644 resources/Qt_master_logo.png create mode 100644 sequence.cpp create mode 100644 sequence.h create mode 100644 sequenceselectormodel.cpp create mode 100644 sequenceselectormodel.h create mode 100644 signal.cpp create mode 100644 signal.h create mode 100644 signalcontroller.cpp create mode 100644 signalcontroller.h create mode 100644 signaldatatablemodel.cpp create mode 100644 signaldatatablemodel.h create mode 100644 singlerundata.cpp create mode 100644 singlerundata.h create mode 100644 singlerunsselectormodel.cpp create mode 100644 singlerunsselectormodel.h 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 0000000000000000000000000000000000000000..6d48935f306358ccd47f4c0d51acc445cac3aa06 GIT binary patch literal 34038 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 literal 0 HcmV?d00001 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 -- 2.43.5