diff --git a/GUI/coregui/DataLoaders/AbstractDataLoader.cpp b/GUI/coregui/DataLoaders/AbstractDataLoader.cpp index cc59ee25f091591b17a6875910d0eb13bf8e6a4a..e5ca3caa0519f994cde30bd28d0077aaab59ef60 100644 --- a/GUI/coregui/DataLoaders/AbstractDataLoader.cpp +++ b/GUI/coregui/DataLoaders/AbstractDataLoader.cpp @@ -39,3 +39,8 @@ QByteArray AbstractDataLoader::defaultProperties() const cloned->initWithDefaultProperties(); return cloned->serialize(); } + +bool AbstractDataLoader::fillImportDetailsTable(QTableWidget*, bool, bool, bool) const +{ + return false; +} diff --git a/GUI/coregui/DataLoaders/AbstractDataLoader.h b/GUI/coregui/DataLoaders/AbstractDataLoader.h index 6e85c39f9506ac85bdfa23b563cae6beee1a09e8..b06dd2b74bb416ead25b6324109e194592493361 100644 --- a/GUI/coregui/DataLoaders/AbstractDataLoader.h +++ b/GUI/coregui/DataLoaders/AbstractDataLoader.h @@ -19,6 +19,8 @@ class QString; class QByteArray; class QGroupBox; class QCustomPlot; +class QTableWidget; +class RealDataItem; #include <QtCore> @@ -49,7 +51,7 @@ public: //! Returns every internal setting so it can be restored completely virtual QByteArray serialize() const; - //! #TODO: how to deliver errors? VersionException...? + //! #baTODO: how to deliver errors? VersionException...? virtual void deserialize(const QByteArray& data); virtual QByteArray defaultProperties() const; @@ -57,6 +59,14 @@ public: //! Returns a Qt rich text formatted preview virtual QString preview(const QString& filepath, QCustomPlot* plotWidget) const = 0; + virtual void importFile(const QString& filename, RealDataItem* item, QStringList* errors, + QStringList* warnings) const = 0; + + //! Fill the import details table with information from the last call to importFile + //! return false if not supported. + //! This base implementation returns false. + virtual bool fillImportDetailsTable(QTableWidget* table, bool fileContent, bool rawContent, + bool processedContent) const; signals: void propertiesChanged(); }; diff --git a/GUI/coregui/DataLoaders/AbstractDataLoader1D.h b/GUI/coregui/DataLoaders/AbstractDataLoader1D.h index d91e37ccaa2e698da7459b8f02fec5ca9d8708a2..fd7f60c6ab8dbc4d3534821d7167a0279f2cefff 100644 --- a/GUI/coregui/DataLoaders/AbstractDataLoader1D.h +++ b/GUI/coregui/DataLoaders/AbstractDataLoader1D.h @@ -16,7 +16,6 @@ #define BORNAGAIN_GUI_COREGUI_DATALOADERS_ABSTRACTDATALOADER1D_H #include "GUI/coregui/DataLoaders/AbstractDataLoader.h" -#include <QVector> class AbstractDataLoader1D : public AbstractDataLoader { }; diff --git a/GUI/coregui/DataLoaders/AutomaticDataLoader1D.cpp b/GUI/coregui/DataLoaders/AutomaticDataLoader1D.cpp index 0b14d45eb4eb1b3656da22bdaa5fbc04bd359c1e..8a9fd297ca43d43cd76e5d64212e0ae52cd0b007 100644 --- a/GUI/coregui/DataLoaders/AutomaticDataLoader1D.cpp +++ b/GUI/coregui/DataLoaders/AutomaticDataLoader1D.cpp @@ -15,6 +15,7 @@ #include "GUI/coregui/DataLoaders/AutomaticDataLoader1D.h" #include "Base/Axis/PointwiseAxis.h" #include "Device/Data/OutputData.h" +#include "Device/Histo/IntensityDataIOFactory.h" #include "Device/InputOutput/DataFormatUtils.h" #include "qcustomplot.h" #include <QFile> @@ -161,6 +162,16 @@ AbstractDataLoader* AutomaticDataLoader1D::clone() const return loader; } +void AutomaticDataLoader1D::importFile(const QString& filename, RealDataItem* item, + QStringList* errors, QStringList* warnings) const +{ + // #baimport implement legacy loader + /* + auto data = IntensityDataIOFactory::readReflectometryData(filename.toStdString()); + return OutputData<double>(std::move(*data)); + */ +} + QString AutomaticDataLoader1D::outputDataToTable(const OutputData<double>& outputData) const { auto size = outputData.axis(0).size(); diff --git a/GUI/coregui/DataLoaders/AutomaticDataLoader1D.h b/GUI/coregui/DataLoaders/AutomaticDataLoader1D.h index 05215d5fb6cc1a3b3a4c77baf97d654ecd4f72eb..b8e9a89e628553a227a61f08c3427bbf4d008fbd 100644 --- a/GUI/coregui/DataLoaders/AutomaticDataLoader1D.h +++ b/GUI/coregui/DataLoaders/AutomaticDataLoader1D.h @@ -26,6 +26,8 @@ public: virtual QString persistentClassName() const override; virtual QString preview(const QString& filepath, QCustomPlot* plotWidget) const override; virtual AbstractDataLoader* clone() const override; + virtual void importFile(const QString& filename, RealDataItem* item, QStringList* errors, + QStringList* warnings) const override; private: QString outputDataToTable(const OutputData<double>& outputData) const; diff --git a/GUI/coregui/DataLoaders/QREDataLoader.cpp b/GUI/coregui/DataLoaders/QREDataLoader.cpp index 92bf8ce69543973522a717e73cc1fce3d832b623..7d240c18ef8eaf1f88f7f552076e6ae291084b3d 100644 --- a/GUI/coregui/DataLoaders/QREDataLoader.cpp +++ b/GUI/coregui/DataLoaders/QREDataLoader.cpp @@ -13,10 +13,18 @@ // ************************************************************************************************ #include "GUI/coregui/DataLoaders/QREDataLoader.h" +#include "../Axis/PointwiseAxis.h" +#include "Device/Histo/IntensityDataIOFactory.h" #include "Device/InputOutput/DataFormatUtils.h" +#include "Device/Unit/AxisNames.h" #include "GUI/coregui/DataLoaders/QREDataLoaderProperties.h" +#include "Models/DataItem.h" +#include "Models/JobItemUtils.h" +#include "Models/RealDataItem.h" +#include "Models/SpecularDataItem.h" #include "qcustomplot.h" #include "ui_QREDataLoaderProperties.h" +#include "utils/ImportDataInfo.h" #include <QFile> #include <QString> #include <QTextStream> @@ -107,155 +115,42 @@ QString QREDataLoader::persistentClassName() const QString QREDataLoader::preview(const QString& filepath, QCustomPlot* plotWidget) const { - /* - QFile file(filepath); - if (!file.open(QFile::ReadOnly)) { - return "File '" + filepath + "' could not be opened"; - } - - const QStringList headerPrefixes = - (m_headerPrefix.trimmed().isEmpty()) ? QStringList() : m_headerPrefix.split(","); - - const auto lineIsHeader = [headerPrefixes](const QString& line) { - for (auto prefix : headerPrefixes) { - if (line.startsWith(prefix.trimmed())) - return true; - } - - return false; - }; - - const auto skippedLines = expandLineNumberPattern(m_linesToSkip); - const auto lineShouldBeSkipped = [skippedLines](int lineNr) { - for (auto pair : skippedLines) { - if (lineNr >= pair.first && lineNr <= pair.second) - return true; - } - return false; - }; - - // calc map with factors - // #TODO: wrong result when more than one dataType points to same column with different factors - QMap<int, double> factorsOfColumns; - for (auto dataType : m_columnDefinitions.keys()) { - const auto& colDef = m_columnDefinitions[dataType]; - if (colDef.enabled) - factorsOfColumns[colDef.column] = colDef.factor; - } - - QVector<QVector<double>> entriesAsDouble; - QVector<QStringList> entriesAsString; - QTextStream in(&file); - int lineNr = 0; - int lastColumnCount = -1; - while (!in.atEnd()) { - QString line = in.readLine().trimmed(); - lineNr++; - - if (lineIsHeader(line) || lineShouldBeSkipped(lineNr) || line.isEmpty()) - continue; - - QStringList lineEntries = line.split(m_separator); - - if (lastColumnCount == -1) - lastColumnCount = lineEntries.count(); - else if (lastColumnCount != lineEntries.count()) - return QString( - "Error: number of columns is not constant over all lines (found in line %1)") - .arg(lineNr); - - QVector<double> rowEntriesAsDouble; - QStringList rowEntriesAsString; // already with factors - - for (int col = 0; col < lineEntries.count(); col++) { - bool ok = false; - double val = lineEntries[col].toDouble(&ok); - if (!ok) - val = NAN; // #TODO: review - else - val *= factorsOfColumns.value(col, 1.0); - - rowEntriesAsDouble << val; - rowEntriesAsString << QString::number(val); - } - - entriesAsDouble << rowEntriesAsDouble; - entriesAsString << rowEntriesAsString; - } - - // validate - There is at least one row and at least two columns - size_t nrows = entriesAsDouble.size(); - if (nrows < 1) - return "Error: no numerical values found"; - size_t ncols = entriesAsDouble[0].size(); - if (ncols < 2) - return "Error: Minimum 2 columns required"; - - // Assign Q vs R, dR, dQ: - QStringList tableEntries; - - QMap<int, QString> typeStr; - typeStr[0] = "Q"; - typeStr[1] = "R"; - typeStr[2] = "E"; - typeStr[3] = "ignored"; - - for (int col = 0; col < ncols; col++) { - QString headerText = typeStr[3]; - for (auto dataType : m_columnDefinitions.keys()) { - if (m_columnDefinitions[dataType].column == col - && m_columnDefinitions[dataType].enabled) { - headerText = typeStr[(int)dataType]; - break; - } - } - - tableEntries << headerText; - } - - for (auto line : entriesAsString) - tableEntries << line; - - QString s = bold("Data table:"); - - s += table((int)ncols, tableEntries, "border=\"1\" cellpadding=\"10\" cellspacing=\"0\"", true); - // -- create plot - int qCol = m_columnDefinitions[DataType::Q].column; - int rCol = m_columnDefinitions[DataType::R].column; int dRCol = m_columnDefinitions[DataType::dR].column; QVector<double> qVec; QVector<double> rVec; - QVector<double> drVec; + QVector<double> eVec; - const bool addDR = m_columnDefinitions[DataType::dR].enabled && (ncols > dRCol); + const bool addError = + m_columnDefinitions[DataType::dR].enabled && (m_parsingResult.columnCount > dRCol); - for (const auto lineAsDoubles : entriesAsDouble) { - qVec << lineAsDoubles[qCol]; - rVec << lineAsDoubles[rCol]; - if (addDR) - drVec << lineAsDoubles[dRCol]; - } + for (const auto d : m_parsingResult.qValues) + qVec << d.second; + + for (const auto d : m_parsingResult.rValues) + rVec << d.second; + + if (addError) + for (const auto d : m_parsingResult.eValues) + eVec << d.second; auto graph = plotWidget->addGraph(); graph->addData(qVec, rVec); - if (!drVec.empty()) { + if (!eVec.empty()) { auto errorBars = new QCPErrorBars(plotWidget->xAxis, plotWidget->yAxis); - errorBars->setData(drVec); + errorBars->setData(eVec); errorBars->setDataPlottable(graph); } plotWidget->rescaleAxes(); plotWidget->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); - plotWidget->xAxis->setLabel("Q [" + m_columnDefinitions[DataType::Q].unit + "]"); + plotWidget->xAxis->setLabel("Q [1/nm]"); plotWidget->yAxis->setLabel("R"); - return "<p>" + bold("<h>Information: </h>") + info() + "</p><p>" + s + "</p>"; - */ - return "not implemented"; + return ""; } void QREDataLoader::populatePropertiesWidget(QWidget* parent) @@ -318,6 +213,7 @@ void QREDataLoader::initWithDefaultProperties() QByteArray QREDataLoader::serialize() const { + // #badataloader ++ add version QByteArray a; QDataStream s(&a, QIODevice::WriteOnly); s << m_separator; @@ -366,6 +262,325 @@ AbstractDataLoader* QREDataLoader::clone() const return loader; } +void QREDataLoader::importFile(const QString& filename, RealDataItem* item, QStringList* errors, + QStringList* warnings) const +{ + ASSERT(item != nullptr); + ASSERT(item->isSpecularData()); + + m_parsingResult.clear(); + + if (!parseFile(filename, errors, warnings)) { + // #baimport ++ reset item! + return; + } + + // -- create OutputData + std::vector<double> qVec; + std::vector<double> rVec; + + for (const auto d : m_parsingResult.qValues) + qVec.push_back(d.second); + + for (const auto d : m_parsingResult.rValues) + rVec.push_back(d.second); + + // -- make a few checks (mainly for fulfilling PointwiseAxis::sanityCheck()) + if (qVec.size() < 2) { + *errors << "At least two Q coordinates must exist"; + return; + } + if (!std::is_sorted(qVec.begin(), qVec.end())) { + *errors << "Q coordinates must be sorted in ascending order"; + return; + } + if (std::adjacent_find(qVec.begin(), qVec.end()) != qVec.end()) { + *errors << "Q coordinates must not contain duplicate values"; + return; + } + + OutputData<double>* oData = new OutputData<double>(); + oData->addAxis(PointwiseAxis("qVector", qVec)); + oData->setRawDataVector(rVec); + + try { + // -- Replacement of item->setImportData(std::move(data)); + item->initNativeData(); + + QString units_name = JobItemUtils::nameFromAxesUnits(Axes::Units::QSPACE); + + // -- Replacement of specularItem->reset(std::move(data)); + SpecularDataItem* specularItem = item->specularDataItem(); + ComboProperty combo = ComboProperty() << units_name; + + specularItem->setItemValue(SpecularDataItem::P_AXES_UNITS, combo.variant()); + specularItem->getItem(SpecularDataItem::P_AXES_UNITS)->setVisible(true); + + auto label_map = AxisNames::InitSpecAxis(); + const auto xAxisTitle = QString::fromStdString(label_map[Axes::Units::QSPACE]); + const auto yAxisTitle = "Signal [a.u.]"; // taken from ImportDataInfo::axisLabel + + specularItem->setXaxisTitle(xAxisTitle); + specularItem->setYaxisTitle(yAxisTitle); + specularItem->setOutputData(oData); // takes ownership of odata + specularItem->setAxesRangeToData(); + + item->setNativeDataUnits(units_name); + item->nativeData()->setOutputData(oData->clone()); // takes ownership of odata + } catch (...) { + *errors << "Import not successful - caught an exception."; + // #baimport ++ reset item! + } +} + +bool QREDataLoader::fillImportDetailsTable(QTableWidget* table, bool fileContent, bool rawContent, + bool processedContent) const +{ + bool showErrorColumn = m_columnDefinitions[QREDataLoader::DataType::dR].enabled; + QString qUnit = " [1/nm]"; + + auto t = table; + t->clear(); + + int colCount = 0; + if (fileContent) + colCount++; + + if (rawContent) + colCount += m_parsingResult.columnCount; + + if (processedContent) + colCount += showErrorColumn ? 3 : 2; + + if (!fileContent && !rawContent && !processedContent) { + t->setRowCount(0); + return true; + } + + t->setColumnCount(colCount); + t->setRowCount(fileContent ? m_parsingResult.lines.size() + : m_parsingResult.originalEntriesAsDouble.size()); + + const auto cell = [t](int row, int col, double s, const QColor& backColor) { + auto tableItem = new QTableWidgetItem(QString::number(s)); + tableItem->setBackgroundColor(backColor); + tableItem->setFlags(tableItem->flags() & ~Qt::ItemIsEditable); + t->setItem(row, col, tableItem); + }; + + QColor backgroundColorFileContent(239, 237, 248); + QColor backgroundColorRawContent(247, 240, 210); + QColor backgroundColorProcessedContent(191, 232, 242); + + int dataCol = 0; + if (fileContent) { + auto headerItem = new QTableWidgetItem("File content (text)"); + headerItem->setBackgroundColor(backgroundColorFileContent.darker(150)); + t->setHorizontalHeaderItem(0, headerItem); + int row = 0; + for (auto line : m_parsingResult.lines) { + const bool skipped = line.first; + QString lineContent = line.second; + lineContent.replace("\t", " --> "); + auto tableItem = new QTableWidgetItem(lineContent); + if (skipped) + tableItem->setTextColor(Qt::lightGray); // #baimport review + tableItem->setBackgroundColor(backgroundColorFileContent); + tableItem->setFlags(tableItem->flags() & ~Qt::ItemIsEditable); + t->setItem(row++, 0, tableItem); + } + dataCol = 1; + } + + if (rawContent) { + for (int col = 0; col < m_parsingResult.columnCount; col++) { + t->setHorizontalHeaderItem(dataCol + col, + new QTableWidgetItem(QString("Column %1 raw").arg(col + 1))); + } + + for (auto rowContent : m_parsingResult.originalEntriesAsDouble) { + int lineNr = rowContent.first; + int dataRow = lineNr - 1; // lineNr is 1-based + + for (auto v : rowContent.second) + cell(dataRow, dataCol++, v, backgroundColorRawContent); + dataCol -= rowContent.second.size(); + } + dataCol += m_parsingResult.columnCount; + } + + if (processedContent) { + t->setHorizontalHeaderItem(dataCol, new QTableWidgetItem("Q" + qUnit)); + t->setHorizontalHeaderItem(dataCol + 1, new QTableWidgetItem("R")); + if (showErrorColumn) + t->setHorizontalHeaderItem(dataCol + 2, new QTableWidgetItem("E")); + + for (auto rowContent : m_parsingResult.qValues) { + int lineNr = rowContent.first; + int dataRow = lineNr - 1; // lineNr is 1-based + auto v = rowContent.second; + cell(dataRow, dataCol + 0, v, backgroundColorProcessedContent); + } + + for (auto rowContent : m_parsingResult.rValues) { + int lineNr = rowContent.first; + int dataRow = lineNr - 1; // lineNr is 1-based + auto v = rowContent.second; + cell(dataRow, dataCol + 1, v, backgroundColorProcessedContent); + } + + if (showErrorColumn) { + for (auto rowContent : m_parsingResult.eValues) { + int lineNr = rowContent.first; + int dataRow = lineNr - 1; // lineNr is 1-based + auto v = rowContent.second; + cell(dataRow, dataCol + 2, v, backgroundColorProcessedContent); + } + } + } + + t->resizeColumnsToContents(); + + return true; +} + +bool QREDataLoader::parseFile(const QString& filename, QStringList* errors, + QStringList* warnings) const +{ + // #baimport optimize: check which import settings changed. E.g. if only q-factor changed, only + // regenerate qValues + + QFile file(filename); + if (!file.open(QFile::ReadOnly)) { + *errors << "File '" + filename + "' could not be opened"; + return false; + } + + const QStringList headerPrefixes = + (m_headerPrefix.trimmed().isEmpty()) ? QStringList() : m_headerPrefix.split(","); + + const auto lineIsHeader = [headerPrefixes](const QString& line) { + for (auto prefix : headerPrefixes) { + if (line.startsWith(prefix.trimmed())) + return true; + } + + return false; + }; + + const auto skippedLines = expandLineNumberPattern(m_linesToSkip); + const auto lineShouldBeSkipped = [skippedLines](int lineNr) { + for (auto pair : skippedLines) { + if (lineNr >= pair.first && lineNr <= pair.second) + return true; + } + return false; + }; + + QTextStream in(&file); + int lineNr = 0; + int lastColumnCount = -1; + // if separator is SPACE: e.g. three consecutive SPACEs do not represent 3 columns => delete + // empty parts + QString::SplitBehavior splitBehavior = + m_separator == " " ? QString::SkipEmptyParts : QString::KeepEmptyParts; + while (!in.atEnd()) { + lineNr++; + + QString line = in.readLine(); + + const bool skip = + lineIsHeader(line) || lineShouldBeSkipped(lineNr) || line.trimmed().isEmpty(); + + m_parsingResult.lines << qMakePair(skip, line); + if (skip) + continue; + + QStringList lineEntries = line.split(m_separator, splitBehavior); + + if (lastColumnCount == -1) + lastColumnCount = lineEntries.count(); + else if (lastColumnCount != lineEntries.count()) { + *errors << QString( + "Number of columns is not constant over all lines (found in line %1)") + .arg(lineNr); + return false; + } + + QVector<double> rowEntriesAsDouble; + + for (int col = 0; col < lineEntries.count(); col++) { + bool ok = false; + double val = lineEntries[col].toDouble(&ok); + if (!ok) + val = std::numeric_limits<double>::quiet_NaN(); + + rowEntriesAsDouble << val; + } + + m_parsingResult.originalEntriesAsDouble << qMakePair(lineNr, rowEntriesAsDouble); + } + + // validate - There is at least one row and at least two columns + size_t nrows = m_parsingResult.originalEntriesAsDouble.size(); + if (nrows < 1) { + *errors << "No numerical values found"; + return false; + } + + m_parsingResult.columnCount = m_parsingResult.originalEntriesAsDouble[0].second.size(); + if (m_parsingResult.columnCount < 2) { + *errors << "Minimum 2 columns required"; + return false; + } + + // -- calculate the Q/R/E values (take from specified column, use factor) + const bool errorColumnIsEnabled = m_columnDefinitions[DataType::dR].enabled; + const double unitFac = + (m_columnDefinitions[DataType::Q].unit == UnitInFile::perAngstrom) ? 10.0 : 1.0; + const double qFactor = m_columnDefinitions[DataType::Q].factor * unitFac; + const double rFactor = m_columnDefinitions[DataType::R].factor; + const double eFactor = m_columnDefinitions[DataType::dR].factor; + + const int qCol = m_columnDefinitions[DataType::Q].column; + const int rCol = m_columnDefinitions[DataType::R].column; + const int eCol = m_columnDefinitions[DataType::dR].column; + + const bool qColIsValid = qCol >= 0 && qCol < m_parsingResult.columnCount; + const bool rColIsValid = rCol >= 0 && rCol < m_parsingResult.columnCount; + const bool eColIsValid = eCol >= 0 && eCol < m_parsingResult.columnCount; + + QSet<double> foundQValues; + + for (auto rowContent : m_parsingResult.originalEntriesAsDouble) { + int lineNr = rowContent.first; + const auto& rawValues = rowContent.second; + + const double q = + qColIsValid ? rawValues[qCol] * qFactor : std::numeric_limits<double>::quiet_NaN(); + const double r = + rColIsValid ? rawValues[rCol] * rFactor : std::numeric_limits<double>::quiet_NaN(); + const double e = + eColIsValid ? rawValues[eCol] * eFactor : std::numeric_limits<double>::quiet_NaN(); + + const bool containsNaN = + (std::isnan(q) || std::isnan(r) || (std::isnan(e) && errorColumnIsEnabled)); + + const bool isDuplicateQ = foundQValues.contains(q); + + // ignore lines when a resulting value would be NAN or in case of duplicate Q values + // #baimport make this dependent from a UI checkbox? + if (!containsNaN && !isDuplicateQ) { + m_parsingResult.qValues << qMakePair(lineNr, q); + m_parsingResult.rValues << qMakePair(lineNr, r); + m_parsingResult.eValues << qMakePair(lineNr, e); + foundQValues << q; + } + } + + return true; +} + void QREDataLoader::applyProperties() { if (!m_propertiesWidget) @@ -396,3 +611,13 @@ void QREDataLoader::applyProperties() m_columnDefinitions[DataType::dR].enabled = m_propertiesWidget->m_ui->enableErrorCheckBox->isChecked(); } + +void QREDataLoader::ParsingResult::clear() +{ + lines.clear(); + originalEntriesAsDouble.clear(); + qValues.clear(); + rValues.clear(); + eValues.clear(); + columnCount = 0; +} diff --git a/GUI/coregui/DataLoaders/QREDataLoader.h b/GUI/coregui/DataLoaders/QREDataLoader.h index a347c74bbe0b93a220bca3727679f2bfee808b6f..9b175efd1b9a95f3fb4f61e23bced72b45505868 100644 --- a/GUI/coregui/DataLoaders/QREDataLoader.h +++ b/GUI/coregui/DataLoaders/QREDataLoader.h @@ -1,63 +1,76 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/coregui/DataLoaders/QREDataLoader.h -//! @brief Defines class QREDataLoader -//! -//! @homepage http://www.bornagainproject.org -//! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2021 -//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) -// -// ************************************************************************************************ - -#ifndef GUI_COREGUI_DATALOADERS_QREDATALOADER_H -#define GUI_COREGUI_DATALOADERS_QREDATALOADER_H - -#include "GUI/coregui/DataLoaders/AbstractDataLoader1D.h" -#include <QVector> - -class QString; -class QREDataLoaderProperties; - -class QREDataLoader : public AbstractDataLoader1D { -public: - QREDataLoader(); - virtual QString name() const override; - virtual QString info() const override; - virtual QString persistentClassName() const override; - virtual QString preview(const QString& filepath, QCustomPlot* plotWidget) const override; - virtual void populatePropertiesWidget(QWidget* parent) override; - virtual void initWithDefaultProperties() override; - virtual void applyProperties() override; - virtual QByteArray serialize() const override; - virtual void deserialize(const QByteArray& data) override; - virtual AbstractDataLoader* clone() const override; - +// ************************************************************************************************ +// +// BornAgain: simulate and fit reflection and scattering +// +//! @file GUI/coregui/DataLoaders/QREDataLoader.h +//! @brief Defines class QREDataLoader +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2021 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************************************ + +#ifndef GUI_COREGUI_DATALOADERS_QREDATALOADER_H +#define GUI_COREGUI_DATALOADERS_QREDATALOADER_H + +#include "GUI/coregui/DataLoaders/AbstractDataLoader1D.h" +#include <QVector> + +class QString; +class QREDataLoaderProperties; + +class QREDataLoader : public AbstractDataLoader1D { +public: + QREDataLoader(); + virtual QString name() const override; + virtual QString info() const override; + virtual QString persistentClassName() const override; + virtual QString preview(const QString& filepath, QCustomPlot* plotWidget) const override; + virtual void populatePropertiesWidget(QWidget* parent) override; + virtual void initWithDefaultProperties() override; + virtual void applyProperties() override; + virtual QByteArray serialize() const override; + virtual void deserialize(const QByteArray& data) override; + virtual AbstractDataLoader* clone() const override; + virtual void importFile(const QString& filename, RealDataItem* item, QStringList* errors, + QStringList* warnings) const override; + virtual bool fillImportDetailsTable(QTableWidget* table, bool fileContent, bool rawContent, + bool processedContent) const override; + private: - enum class UnitInFile { - none, - perNanoMeter, - perAngstrom, - other + bool parseFile(const QString& filename, QStringList* errors, QStringList* warnings) const; + +private: + enum class UnitInFile { none, perNanoMeter, perAngstrom, other }; + + struct ColumnDefinition { + bool enabled; + int column; + UnitInFile unit; + double factor; + }; + + enum class DataType { Q, R, dR }; + + QString m_separator; //!< column separator + QString m_headerPrefix; //!< prefix denoting header line + QString m_linesToSkip; //!< pattern denoting line to skip (i.e. '1,10-12,42') + QPointer<QREDataLoaderProperties> m_propertiesWidget; + + QMap<DataType, ColumnDefinition> m_columnDefinitions; + + struct ParsingResult { + void clear(); + QVector<QPair<bool, QString>> lines; // bool describes whether line is skipped + QVector<QPair<int, QVector<double>>> originalEntriesAsDouble; + QVector<QPair<int, double>> qValues; + QVector<QPair<int, double>> rValues; + QVector<QPair<int, double>> eValues; + int columnCount; }; - - struct ColumnDefinition { - bool enabled; - int column; - UnitInFile unit; - double factor; - }; - - enum class DataType { Q, R, dR }; - - QString m_separator; //!< column separator - QString m_headerPrefix; //!< prefix denoting header line - QString m_linesToSkip; //!< pattern denoting line to skip (i.e. '1,10-12,42') - QPointer<QREDataLoaderProperties> m_propertiesWidget; - - QMap<DataType, ColumnDefinition> m_columnDefinitions; -}; - -#endif // GUI_COREGUI_DATALOADERS_QREDATALOADER_H + mutable ParsingResult m_parsingResult; +}; + +#endif // GUI_COREGUI_DATALOADERS_QREDATALOADER_H diff --git a/GUI/coregui/DataLoaders/QREDataLoaderProperties.cpp b/GUI/coregui/DataLoaders/QREDataLoaderProperties.cpp index de70d251fc3312ed905189ef67a9b1e7ca04fb4a..8c1f43236fdada85538cfb45a13df1d46afcd215 100644 --- a/GUI/coregui/DataLoaders/QREDataLoaderProperties.cpp +++ b/GUI/coregui/DataLoaders/QREDataLoaderProperties.cpp @@ -62,27 +62,11 @@ double QREDataLoaderProperties::factor(int dataType) const return spinBox->isVisible() ? spinBox->value() : 1.0; } -void QREDataLoaderProperties::updateEnabling(int dataType, bool enabled) -{ - const int lineInLayout = dataType; - for (int col = 1; col < m_ui->gridLayout->columnCount(); col++) { - auto layoutItem = m_ui->gridLayout->itemAtPosition(lineInLayout, col); - if (layoutItem) { - QWidget* w = layoutItem->widget(); - if (w) { - const bool belongsToUnusedCombo = - (dataType != 0) && (col >= 5); // no unit except for Q - w->setVisible(enabled && !belongsToUnusedCombo); - } - } - } -} - void QREDataLoaderProperties::updateErrorEnabling(bool enabled) { const int lineInLayout = 2; - for (int col = 1; col < m_ui->gridLayout->columnCount(); col++) { + for (int col = 2; col < m_ui->gridLayout->columnCount(); col++) { auto layoutItem = m_ui->gridLayout->itemAtPosition(lineInLayout, col); if (layoutItem) { QWidget* w = layoutItem->widget(); @@ -106,7 +90,8 @@ void QREDataLoaderProperties::onErrorEnablingChanged() QSpinBox* QREDataLoaderProperties::columnSpinBox(int dataType) const { const int lineInLayout = dataType; - return dynamic_cast<QSpinBox*>(m_ui->gridLayout->itemAtPosition(lineInLayout, 2)->widget()); + return dynamic_cast<QSpinBox*>( + m_ui->gridLayout->itemAtPosition(lineInLayout, columnColumn)->widget()); } QDoubleSpinBox* QREDataLoaderProperties::factorSpinBox(int dataType) const diff --git a/GUI/coregui/DataLoaders/QREDataLoaderProperties.h b/GUI/coregui/DataLoaders/QREDataLoaderProperties.h index 471560a466bf03dac8eec21f26875e08e76d613c..68dd2256d1a5ea0b9326e90dae30f7ce187c26b3 100644 --- a/GUI/coregui/DataLoaders/QREDataLoaderProperties.h +++ b/GUI/coregui/DataLoaders/QREDataLoaderProperties.h @@ -45,7 +45,6 @@ signals: void propertiesChanged(); private: - void updateEnabling(int dataType, bool enabled); void updateErrorEnabling(bool enabled); void onErrorEnablingChanged(); QLabel* factorLabel(int dataType) const; @@ -54,7 +53,8 @@ private: // they are only deactivated. Call allowFactors(true) to enable them. bool m_allowFactors; - static const int factorLabelColumn = 3; + static const int columnColumn = 3; + static const int factorLabelColumn = 4; static const int factorColumn = factorLabelColumn + 1; }; diff --git a/GUI/coregui/DataLoaders/QREDataLoaderProperties.ui b/GUI/coregui/DataLoaders/QREDataLoaderProperties.ui index 68f0538cba995cc733ca7bd15f2adb6fd7ad101c..881462bb4eb1b0af578cf550a020f3115cbe95b1 100644 --- a/GUI/coregui/DataLoaders/QREDataLoaderProperties.ui +++ b/GUI/coregui/DataLoaders/QREDataLoaderProperties.ui @@ -150,70 +150,62 @@ </item> <item> <layout class="QGridLayout" name="gridLayout"> - <item row="2" column="0"> - <widget class="QCheckBox" name="enableErrorCheckBox"> - <property name="text"> - <string>Read error</string> - </property> - </widget> - </item> - <item row="1" column="4"> - <widget class="QDoubleSpinBox" name="factorSpinBox4_5"> - <property name="value"> - <double>1.000000000000000</double> - </property> - </widget> - </item> - <item row="0" column="1"> - <widget class="QLabel" name="label_5"> - <property name="text"> - <string>from column</string> - </property> - </widget> - </item> - <item row="1" column="2"> - <widget class="QSpinBox" name="spinBox_2"> + <item row="0" column="3"> + <widget class="QSpinBox" name="spinBox"> <property name="minimum"> <number>1</number> </property> </widget> </item> - <item row="0" column="2"> - <widget class="QSpinBox" name="spinBox"> + <item row="2" column="3"> + <widget class="QSpinBox" name="spinBox_3"> <property name="minimum"> <number>1</number> </property> </widget> </item> - <item row="2" column="1"> - <widget class="QLabel" name="label_10"> + <item row="0" column="10"> + <spacer name="horizontalSpacer_2"> + <property name="orientation"> + <enum>Qt::Horizontal</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>40</width> + <height>20</height> + </size> + </property> + </spacer> + </item> + <item row="0" column="1"> + <widget class="QLabel" name="label"> <property name="text"> - <string>from column</string> + <string>Read Q</string> </property> </widget> </item> - <item row="0" column="5"> - <widget class="QLabel" name="unitLabel4_2"> + <item row="0" column="2"> + <widget class="QLabel" name="label_5"> <property name="text"> - <string>given values are in</string> + <string>from column</string> </property> </widget> </item> - <item row="2" column="3"> + <item row="2" column="4"> <widget class="QLabel" name="factorLabel4_8"> <property name="text"> <string>multiplied by</string> </property> </widget> </item> - <item row="1" column="1"> - <widget class="QLabel" name="label_6"> - <property name="text"> - <string>from column</string> + <item row="0" column="5"> + <widget class="QDoubleSpinBox" name="factorSpinBox4_2"> + <property name="value"> + <double>1.000000000000000</double> </property> </widget> </item> - <item row="0" column="6"> + <item row="0" column="7"> <widget class="QComboBox" name="qUnitCombo"> <item> <property name="text"> @@ -227,65 +219,106 @@ </item> </widget> </item> - <item row="1" column="3"> - <widget class="QLabel" name="factorLabel4_7"> - <property name="text"> - <string>multiplied by</string> + <item row="1" column="5"> + <widget class="QDoubleSpinBox" name="factorSpinBox4_5"> + <property name="value"> + <double>1.000000000000000</double> </property> </widget> </item> - <item row="0" column="9"> - <spacer name="horizontalSpacer_2"> - <property name="orientation"> - <enum>Qt::Horizontal</enum> - </property> - <property name="sizeHint" stdset="0"> - <size> - <width>40</width> - <height>20</height> - </size> + <item row="2" column="2"> + <widget class="QLabel" name="label_10"> + <property name="text"> + <string>from column</string> </property> - </spacer> + </widget> </item> - <item row="0" column="3"> - <widget class="QLabel" name="factorLabel4_2"> + <item row="1" column="4"> + <widget class="QLabel" name="factorLabel4_7"> <property name="text"> <string>multiplied by</string> </property> </widget> </item> - <item row="0" column="4"> - <widget class="QDoubleSpinBox" name="factorSpinBox4_2"> + <item row="2" column="5"> + <widget class="QDoubleSpinBox" name="factorSpinBox4_6"> <property name="value"> <double>1.000000000000000</double> </property> </widget> </item> - <item row="2" column="2"> - <widget class="QSpinBox" name="spinBox_3"> + <item row="1" column="3"> + <widget class="QSpinBox" name="spinBox_2"> <property name="minimum"> <number>1</number> </property> </widget> </item> - <item row="2" column="4"> - <widget class="QDoubleSpinBox" name="factorSpinBox4_6"> - <property name="value"> - <double>1.000000000000000</double> + <item row="0" column="6"> + <widget class="QLabel" name="unitLabel4_2"> + <property name="text"> + <string>given values are in</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Read R</string> + </property> + </widget> + </item> + <item row="0" column="4"> + <widget class="QLabel" name="factorLabel4_2"> + <property name="text"> + <string>multiplied by</string> + </property> + </widget> + </item> + <item row="1" column="2"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>from column</string> </property> </widget> </item> <item row="0" column="0"> - <widget class="QLabel" name="label"> + <widget class="QCheckBox" name="checkBox"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> - <string>Read Q</string> + <string/> + </property> + <property name="checked"> + <bool>true</bool> </property> </widget> </item> <item row="1" column="0"> - <widget class="QLabel" name="label_7"> + <widget class="QCheckBox" name="checkBox_2"> + <property name="enabled"> + <bool>false</bool> + </property> <property name="text"> - <string>Read R</string> + <string/> + </property> + <property name="checked"> + <bool>true</bool> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QCheckBox" name="enableErrorCheckBox"> + <property name="text"> + <string/> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Read error</string> </property> </widget> </item> diff --git a/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.cpp b/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.cpp index 465365c68c435f2bfd2719672473a875bb78b176..06cfba4109d64e29ad4be13ba5ca6e523cc25c21 100644 --- a/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.cpp +++ b/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.cpp @@ -69,3 +69,9 @@ AbstractDataLoader* UserDefinedDataLoader1D::clone() const auto loader = new UserDefinedDataLoader1D(cloned, m_name, m_defaultProperties); return loader; } + +void UserDefinedDataLoader1D::importFile(const QString& filename, RealDataItem* item, + QStringList* errors, QStringList* warnings) const +{ + m_wrappedLoader->importFile(filename, item, errors, warnings); +} diff --git a/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.h b/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.h index 5a79013497b3199ca347807186bbd047a24be9ec..ec4f4459b5c9cd8c0007bbec9d32e726a5342af1 100644 --- a/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.h +++ b/GUI/coregui/DataLoaders/UserDefinedDataLoader1D.h @@ -31,6 +31,8 @@ public: virtual void deserialize(const QByteArray& data) override; virtual QString preview(const QString& filepath, QCustomPlot* plotWidget) const override; virtual AbstractDataLoader* clone() const override; + virtual void importFile(const QString& filename, RealDataItem* item, QStringList* errors, + QStringList* warnings) const override; private: QString m_name;