diff --git a/GUI/coregui/CMakeLists.txt b/GUI/coregui/CMakeLists.txt index c1383c8a3883ea07928be4ffb8ba321d09dddf71..5548ba346542671447cefc5a003a25d5347f566d 100644 --- a/GUI/coregui/CMakeLists.txt +++ b/GUI/coregui/CMakeLists.txt @@ -38,6 +38,7 @@ set(include_dirs ${CMAKE_CURRENT_SOURCE_DIR}/Views/AccordionWidget ${CMAKE_CURRENT_SOURCE_DIR}/Views/ImportDataWidgets ${CMAKE_CURRENT_SOURCE_DIR}/Views/CommonWidgets + ${CMAKE_CURRENT_SOURCE_DIR}/Views/SpecularDataWidgets ) if(BORNAGAIN_OPENGL) diff --git a/GUI/coregui/Models/ItemCatalogue.cpp b/GUI/coregui/Models/ItemCatalogue.cpp index bb6192b14242dc4c5e39e22681e6c28786baf839..efcaef3c9128410dbbbfcb95184a43d4b8fe2a5c 100644 --- a/GUI/coregui/Models/ItemCatalogue.cpp +++ b/GUI/coregui/Models/ItemCatalogue.cpp @@ -53,6 +53,7 @@ #include "ResolutionFunctionItems.h" #include "RotationItems.h" #include "SimulationOptionsItem.h" +#include "SpecularDataItem.h" #include "SphericalDetectorItem.h" #include "TransformationItem.h" #include "VectorItem.h" @@ -164,6 +165,7 @@ ItemCatalogue::ItemCatalogue() add(Constants::JobItemType, create_new<JobItem>); add(Constants::IntensityDataType, create_new<IntensityDataItem>); + add(Constants::SpecularDataType, create_new<SpecularDataItem>); add(Constants::BasicAxisType, create_new<BasicAxisItem>); add(Constants::AmplitudeAxisType, create_new<AmplitudeAxisItem>); diff --git a/GUI/coregui/Models/SpecularDataItem.cpp b/GUI/coregui/Models/SpecularDataItem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dc13a2a943cb511898dfaf82215aefe2b958739f --- /dev/null +++ b/GUI/coregui/Models/SpecularDataItem.cpp @@ -0,0 +1,295 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Models/SpecularDataItem.cpp +//! @brief Implements class SpecularDataItem +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#include "SpecularDataItem.h" +#include "AxesItems.h" +#include "BornAgainNamespace.h" +#include "ComboProperty.h" +#include "GUIHelpers.h" +#include "BornAgainNamespace.h" + +const QString SpecularDataItem::P_AXES_UNITS = "Axes Units"; +const QString SpecularDataItem::P_TITLE = "Title"; +const QString SpecularDataItem::P_XAXIS = "x-axis"; +const QString SpecularDataItem::P_YAXIS = "y-axis"; +const QString SpecularDataItem::P_FILE_NAME = "FileName"; + +SpecularDataItem::SpecularDataItem() : SessionItem(Constants::SpecularDataType) +{ + ComboProperty units = ComboProperty() << Constants::UnitsNbins; + addProperty(P_AXES_UNITS, units.variant()); + + addProperty(P_TITLE, QString())->setVisible(false); + + SessionItem* item = addGroupProperty(P_XAXIS, Constants::BasicAxisType); + item->getItem(BasicAxisItem::P_NBINS)->setVisible(false); + + item = addGroupProperty(P_YAXIS, Constants::AmplitudeAxisType); + item->getItem(BasicAxisItem::P_NBINS)->setVisible(false); + item->getItem(BasicAxisItem::P_TITLE)->setVisible(true); + + item = item->getItem(AmplitudeAxisItem::P_IS_VISIBLE); + item->setValue(true); + item->setVisible(false); + + setXaxisTitle("X [nbins]"); + setYaxisTitle("Signal [a.u.]"); + + // name of the file used to serialize given SpecularDataItem + addProperty(P_FILE_NAME, QStringLiteral("undefined"))->setVisible(false); + + mapper()->setOnPropertyChange([this](const QString& name) + { + if(name == P_FILE_NAME) + setLastModified(QDateTime::currentDateTime()); + }); + + mapper()->setOnValueChange([this]() + { + // OutputData was modified + setLastModified(QDateTime::currentDateTime()); + }); +} + +void SpecularDataItem::setOutputData(OutputData<double>* data) +{ + Q_ASSERT(data); + m_data.reset(data); + + updateAxesZoomLevel(); + updateAxesLabels(); + computeDataRange(); + + emitDataChanged(); +} + +//! Sets the raw data vector from external source + +void SpecularDataItem::setRawDataVector(const OutputData<double>* data) +{ + if (!m_data->hasSameDimensions(*data)) { + throw GUIHelpers::Error("SpecularDataItem::setRawDataVector() -> Error. " + "Different dimensions of data."); + } + m_data->setRawDataVector(data->getRawDataVector()); + emitDataChanged(); +} + +int SpecularDataItem::getNbins() const +{ + return xAxisItem()->getItemValue(BasicAxisItem::P_NBINS).toInt(); +} + +double SpecularDataItem::getLowerX() const +{ + return getItem(P_XAXIS)->getItemValue(BasicAxisItem::P_MIN).toDouble(); +} + +double SpecularDataItem::getUpperX() const +{ + return getItem(P_XAXIS)->getItemValue(BasicAxisItem::P_MAX).toDouble(); +} + +double SpecularDataItem::getXmin() const +{ + const double defaultXmin(0.0); + return m_data ? m_data->getAxis(BornAgain::X_AXIS_INDEX).getMin() : defaultXmin; +} + +double SpecularDataItem::getXmax() const +{ + const double defaultXmax(1.0); + return m_data ? m_data->getAxis(BornAgain::X_AXIS_INDEX).getMax() : defaultXmax; +} + +double SpecularDataItem::getLowerY() const +{ + return getItem(P_YAXIS)->getItemValue(BasicAxisItem::P_MIN).toDouble(); +} + +double SpecularDataItem::getUpperY() const +{ + return getItem(P_YAXIS)->getItemValue(BasicAxisItem::P_MAX).toDouble(); +} + +double SpecularDataItem::getYmin() const +{ + const double defaultYmin(0.0); + auto limits = dataRange(); + return m_data ? limits.first : defaultYmin; +} + +double SpecularDataItem::getYmax() const +{ + const double defaultYmax(1.0); + auto limits = dataRange(); + return m_data ? limits.second : defaultYmax; +} + +bool SpecularDataItem::isLog() const +{ + return getItem(P_YAXIS)->getItemValue(AmplitudeAxisItem::P_IS_LOGSCALE).toBool(); +} + +QString SpecularDataItem::getXaxisTitle() const +{ + return getItem(P_XAXIS)->getItemValue(BasicAxisItem::P_TITLE).toString(); +} + +QString SpecularDataItem::getYaxisTitle() const +{ + return getItem(P_YAXIS)->getItemValue(BasicAxisItem::P_TITLE).toString(); +} + +QString SpecularDataItem::selectedAxesUnits() const +{ + ComboProperty combo = getItemValue(SpecularDataItem::P_AXES_UNITS).value<ComboProperty>(); + return combo.getValue(); +} + +QString SpecularDataItem::fileName(const QString& projectDir) const +{ + QString filename = getItemValue(SpecularDataItem::P_FILE_NAME).toString(); + return projectDir.isEmpty() ? filename : projectDir + QStringLiteral("/") + filename; +} + +void SpecularDataItem::setLowerX(double xmin) +{ + getItem(P_XAXIS)->setItemValue(BasicAxisItem::P_MIN, xmin); +} + +void SpecularDataItem::setUpperX(double xmax) +{ + getItem(P_XAXIS)->setItemValue(BasicAxisItem::P_MAX, xmax); +} + +void SpecularDataItem::setLowerY(double ymin) +{ + getItem(P_YAXIS)->setItemValue(AmplitudeAxisItem::P_MIN, ymin); +} + +void SpecularDataItem::setUpperY(double ymax) +{ + getItem(P_YAXIS)->setItemValue(AmplitudeAxisItem::P_MAX, ymax); +} + +void SpecularDataItem::setLog(bool log_flag) +{ + getItem(P_YAXIS)->setItemValue(AmplitudeAxisItem::P_IS_LOGSCALE, log_flag); +} + +void SpecularDataItem::setXaxisTitle(QString xtitle) +{ + getItem(P_XAXIS)->setItemValue(BasicAxisItem::P_TITLE, xtitle); +} + +void SpecularDataItem::setYaxisTitle(QString ytitle) +{ + getItem(P_YAXIS)->setItemValue(AmplitudeAxisItem::P_TITLE, ytitle); +} + +//! set zoom range of x,y axes to axes of input data +void SpecularDataItem::setAxesRangeToData() +{ + setLowerX(getXmin()); + setUpperX(getXmax()); + setLowerY(getYmin()); + setUpperY(getYmax()); +} + +//! Sets zoom range of X,Y axes, if it was not yet defined. + +void SpecularDataItem::updateAxesZoomLevel() +{ + // set zoom range of x-axis to min, max values if it was not set already + if (getUpperX() < getLowerX()) { + setLowerX(getXmin()); + setUpperX(getXmax()); + } + + // set zoom range of y-axis to min, max values if it was not set already + if (getUpperY() < getLowerY()) { + setLowerY(getYmin()); + setUpperY(getYmax()); + } + + const int nx = static_cast<int>(m_data->getAxis(BornAgain::X_AXIS_INDEX).size()); + xAxisItem()->setItemValue(BasicAxisItem::P_NBINS, nx); +} + +//! Init axes labels, if it was not done already. + +void SpecularDataItem::updateAxesLabels() +{ + if (getXaxisTitle().isEmpty()) + setXaxisTitle(QString::fromStdString(m_data->getAxis(BornAgain::X_AXIS_INDEX).getName())); +} + +void SpecularDataItem::computeDataRange() +{ + QPair<double, double> minmax = dataRange(); + setLowerY(minmax.first); + setUpperY(minmax.second); +} + +//! Init ymin, ymax to match the intensity values range. +QPair<double, double> SpecularDataItem::dataRange() const +{ + const OutputData<double>* data = getOutputData(); + double min(*std::min_element(data->begin(), data->end())); + double max(*std::max_element(data->begin(), data->end())); + if (isLog()) { + min /= 2.0; + max *= 2.0; + } else { + max = max * 1.1; + } + + return QPair<double, double>(min, max); +} + +const BasicAxisItem* SpecularDataItem::xAxisItem() const +{ + return dynamic_cast<const BasicAxisItem*>(getItem(P_XAXIS)); +} + +BasicAxisItem* SpecularDataItem::xAxisItem() +{ + return const_cast<BasicAxisItem*>(static_cast<const SpecularDataItem*>(this)->xAxisItem()); +} + +const AmplitudeAxisItem* SpecularDataItem::yAxisItem() const +{ + auto result = dynamic_cast<const AmplitudeAxisItem*>(getItem(P_YAXIS)); + Q_ASSERT(result); + return result; +} + +//! Set axes viewport to original data. + +void SpecularDataItem::resetView() +{ + setAxesRangeToData(); + computeDataRange(); +} + +QDateTime SpecularDataItem::lastModified() const +{ + return m_last_modified; +} + +void SpecularDataItem::setLastModified(const QDateTime &dtime) +{ + m_last_modified = dtime; +} diff --git a/GUI/coregui/Models/SpecularDataItem.h b/GUI/coregui/Models/SpecularDataItem.h new file mode 100644 index 0000000000000000000000000000000000000000..2912ac3012e0a72e35373f660d93181cdcc9571c --- /dev/null +++ b/GUI/coregui/Models/SpecularDataItem.h @@ -0,0 +1,101 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Models/SpecularDataItem.h +//! @brief Defines class SpecularDataItem +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#ifndef SPECULARDATAITEM_H +#define SPECULARDATAITEM_H + +#include "SessionItem.h" +#include "OutputData.h" +#include <QDateTime> + +class AmplitudeAxisItem; +class BasicAxisItem; +class MaskContainerItem; +class ProjectionContainerItem; + +class BA_CORE_API_ SpecularDataItem : public SessionItem +{ +public: + static const QString P_TITLE; + static const QString P_AXES_UNITS; + static const QString P_XAXIS; + static const QString P_YAXIS; + static const QString P_FILE_NAME; + + SpecularDataItem(); + + OutputData<double>* getOutputData() { return m_data.get(); } + const OutputData<double>* getOutputData() const { return m_data.get(); } + void setOutputData(OutputData<double>* data); + void setRawDataVector(const OutputData<double>* data); + + //! Number of bins in data + int getNbins() const; + + //! returns lower and upper zoom ranges of x-axis + double getLowerX() const; + double getUpperX() const; + + //! returns min and max range of x-axis as given by IntensityData + double getXmin() const; + double getXmax() const; + + //! returns lower and upper zoom ranges of y-axis + double getLowerY() const; + double getUpperY() const; + + //! returns min and max range of y-axis as given by IntensityData + double getYmin() const; + double getYmax() const; + + bool isLog() const; + QString getXaxisTitle() const; + QString getYaxisTitle() const; + + QString selectedAxesUnits() const; + + QString fileName(const QString& projectDir = QString()) const; + + void updateDataRange(); + void computeDataRange(); + QPair<double, double> dataRange() const; + + const BasicAxisItem* xAxisItem() const; + BasicAxisItem* xAxisItem(); + const AmplitudeAxisItem* yAxisItem() const; + + void resetView(); + + QDateTime lastModified() const; + void setLastModified(const QDateTime& dtime); + +public slots: + void setLowerX(double xmin); + void setUpperX(double xmax); + void setLowerY(double ymin); + void setUpperY(double ymax); + void setLog(bool log_flag); + void setXaxisTitle(QString xtitle); + void setYaxisTitle(QString ytitle); + void setAxesRangeToData(); + +private: + void updateAxesZoomLevel(); + void updateAxesLabels(); + + std::unique_ptr<OutputData<double>> m_data; //!< simulation results + QDateTime m_last_modified; +}; + +#endif // SPECULARDATAITEM_H diff --git a/GUI/coregui/Models/item_constants.h b/GUI/coregui/Models/item_constants.h index 6fc68656a053b1c99df102eebcd922a126bf3f5d..6fae1f0802f3b29a0b9b47a8a0609f44c5e5d473 100644 --- a/GUI/coregui/Models/item_constants.h +++ b/GUI/coregui/Models/item_constants.h @@ -135,6 +135,7 @@ const ModelType FitSuiteType = "FitSuite"; const ModelType JobItemType = "JobItem"; const ModelType IntensityDataType = "IntensityData"; +const ModelType SpecularDataType = "SpecularData"; const ModelType BasicAxisType = "BasicAxis"; const ModelType AmplitudeAxisType = "AmplitudeAxis"; diff --git a/GUI/coregui/Views/IntensityDataWidgets/IntensityDataCanvas.cpp b/GUI/coregui/Views/IntensityDataWidgets/IntensityDataCanvas.cpp index c72a033346632b6c52d3990a04a3f0261315c45e..e0ad5b9fdb333fd7260f8750965dd5562309f360 100644 --- a/GUI/coregui/Views/IntensityDataWidgets/IntensityDataCanvas.cpp +++ b/GUI/coregui/Views/IntensityDataWidgets/IntensityDataCanvas.cpp @@ -77,7 +77,7 @@ void IntensityDataCanvas::onSavePlotAction() { QString dirname = AppSvc::projectManager()->userExportDir(); SavePlotAssistant saveAssistant; - saveAssistant.savePlot(dirname, m_colorMap->customPlot(), intensityDataItem()); + saveAssistant.savePlot(dirname, m_colorMap->customPlot(), intensityDataItem()->getOutputData()); } void IntensityDataCanvas::onMousePress(QMouseEvent* event) diff --git a/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.cpp b/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.cpp index 5a2b0734f9e825f15e60cb9ff68c15e400d8482f..4891648f2457ba4c3927704287d5a09907e102d1 100644 --- a/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.cpp +++ b/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.cpp @@ -15,7 +15,6 @@ #include "SavePlotAssistant.h" #include "ColorMap.h" #include "IntensityDataIOFactory.h" -#include "IntensityDataItem.h" #include <QFileDialog> #include <QMessageBox> @@ -51,8 +50,8 @@ SavePlotAssistant::Format::Format(const QString &file_extention, const QString & } -void SavePlotAssistant::savePlot(const QString &dirname, QCustomPlot *plot, - IntensityDataItem *item) +void SavePlotAssistant::savePlot(const QString& dirname, QCustomPlot* plot, + OutputData<double>* output_data) { QString selectedFilter("*.png"); @@ -64,7 +63,7 @@ void SavePlotAssistant::savePlot(const QString &dirname, QCustomPlot *plot, if(!nameToSave.isEmpty()) { try { - saveToFile(nameToSave, plot, item); + saveToFile(nameToSave, plot, output_data); } catch(const std::exception &ex) { QString message = "Attempt to save file with the name '"; message.append(nameToSave); @@ -76,7 +75,8 @@ void SavePlotAssistant::savePlot(const QString &dirname, QCustomPlot *plot, } -void SavePlotAssistant::saveToFile(const QString &fileName, QCustomPlot *plot, IntensityDataItem *item) +void SavePlotAssistant::saveToFile(const QString& fileName, QCustomPlot* plot, + OutputData<double>* output_data) { if(isPngFile(fileName)) { plot->savePng(fileName); @@ -91,8 +91,8 @@ void SavePlotAssistant::saveToFile(const QString &fileName, QCustomPlot *plot, I } else { - IntensityDataIOFactory::writeOutputData(*item->getOutputData(), - fileName.toStdString()); + Q_ASSERT(output_data); + IntensityDataIOFactory::writeOutputData(*output_data, fileName.toStdString()); } } diff --git a/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.h b/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.h index eb09ead99710df4ead533484604d0cf118d075ab..6504987bc2dfa4368de1a72915bdbb089f9ba6a5 100644 --- a/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.h +++ b/GUI/coregui/Views/IntensityDataWidgets/SavePlotAssistant.h @@ -20,7 +20,7 @@ #include <QVector> class QCustomPlot; -class IntensityDataItem; +template <class T> class OutputData; //! Assistant class which contains all logic for saving IntensityData to various formats //! from IntensityDataPlotWidget. @@ -36,10 +36,10 @@ public: QString m_filter; }; - void savePlot(const QString &dirname, QCustomPlot *plot, IntensityDataItem *item); + void savePlot(const QString& dirname, QCustomPlot* plot, OutputData<double>* output_data); private: - void saveToFile(const QString &dirname, QCustomPlot *plot, IntensityDataItem *item); + void saveToFile(const QString& dirname, QCustomPlot* plot, OutputData<double>* output_data); QString getFilterString() const; QString composeFileName(const QString &fileName, const QString &filterName) const; bool isValidExtension(const QString &fileName) const; diff --git a/GUI/coregui/Views/MaskWidgets/MaskEditorCanvas.cpp b/GUI/coregui/Views/MaskWidgets/MaskEditorCanvas.cpp index 58449ba4ad901e08f502db3a91366fd48656633b..2037688765985a2002bb2376879bd5a58d6853de 100644 --- a/GUI/coregui/Views/MaskWidgets/MaskEditorCanvas.cpp +++ b/GUI/coregui/Views/MaskWidgets/MaskEditorCanvas.cpp @@ -96,7 +96,8 @@ void MaskEditorCanvas::onSavePlotRequest() QString dirname = AppSvc::projectManager()->userExportDir(); SavePlotAssistant saveAssistant; - saveAssistant.savePlot(dirname, m_scene->colorMap()->customPlot(), m_intensityDataItem); + saveAssistant.savePlot(dirname, m_scene->colorMap()->customPlot(), + m_intensityDataItem->getOutputData()); } void MaskEditorCanvas::onResetViewRequest() diff --git a/GUI/coregui/Views/SessionModelView.cpp b/GUI/coregui/Views/SessionModelView.cpp index b3468e642872d057ab64bbc8a5153b4fadba27be..4663862a98fd44730d0459d27d50d302cdaded4e 100644 --- a/GUI/coregui/Views/SessionModelView.cpp +++ b/GUI/coregui/Views/SessionModelView.cpp @@ -27,7 +27,7 @@ #include <QVBoxLayout> namespace { -const bool show_test_view = false; +const bool show_test_view = true; } SessionModelView::SessionModelView(MainWindow *mainWindow) diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.cpp b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ed5261c21eb3844beabacb56e78bf5f01f1e7bdd --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.cpp @@ -0,0 +1,98 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.cpp +//! @brief Implements class SpecularDataCanvas +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#include "AppSvc.h" +#include "projectmanager.h" +#include "SavePlotAssistant.h" +#include "SpecularDataCanvas.h" +#include "SpecularDataItem.h" +#include "SpecularPlotCanvas.h" +#include "plot_constants.h" +#include "qcustomplot.h" + +SpecularDataCanvas::SpecularDataCanvas(QWidget* parent) + : SessionItemWidget(parent) + , m_plot_canvas(new SpecularPlotCanvas) + , m_reset_view_action(nullptr) + , m_save_plot_action(nullptr) + +{ + QVBoxLayout* vlayout = new QVBoxLayout(this); + vlayout->setMargin(0); + vlayout->setSpacing(0); + vlayout->setContentsMargins(0, 0, 0, 0); + vlayout->addWidget(m_plot_canvas); + setLayout(vlayout); + setStyleSheet("background-color:white;"); + + initActions(); + + connect(m_plot_canvas->customPlot(), &QCustomPlot::mousePress, this, + &SpecularDataCanvas::onMousePress, Qt::UniqueConnection); +} + +void SpecularDataCanvas::setItem(SessionItem* intensityItem) +{ + SessionItemWidget::setItem(intensityItem); + m_plot_canvas->setItem(intensityItem); +} + +QSize SpecularDataCanvas::sizeHint() const { return QSize(500, 400); } + +QSize SpecularDataCanvas::minimumSizeHint() const { return QSize(128, 128); } + +QList<QAction*> SpecularDataCanvas::actionList() +{ + return QList<QAction*>() << m_reset_view_action << m_save_plot_action; +} + +void SpecularDataCanvas::onResetViewAction() { specularDataItem()->resetView(); } + +void SpecularDataCanvas::onSavePlotAction() +{ + QString dirname = AppSvc::projectManager()->userExportDir(); + SavePlotAssistant saveAssistant; + saveAssistant.savePlot(dirname, m_plot_canvas->customPlot(), + specularDataItem()->getOutputData()); +} + +void SpecularDataCanvas::onMousePress(QMouseEvent* event) +{ + if (event->button() == Qt::RightButton) + emit customContextMenuRequested(event->globalPos()); +} + +SpecularDataItem* SpecularDataCanvas::specularDataItem() +{ + SpecularDataItem* result = dynamic_cast<SpecularDataItem*>(currentItem()); + Q_ASSERT(result); + return result; +} + +// TODO: try to reuse IntensityDataCanvas::initActions somehow +void SpecularDataCanvas::initActions() +{ + m_reset_view_action = new QAction(this); + m_reset_view_action->setText("Reset"); + m_reset_view_action->setIcon(QIcon(":/images/toolbar16light_refresh.svg")); + m_reset_view_action->setToolTip("Reset view\n" + "x,y axes range will be set to default"); + connect(m_reset_view_action, &QAction::triggered, this, &SpecularDataCanvas::onResetViewAction); + + m_save_plot_action = new QAction(this); + m_save_plot_action->setText("Save"); + m_save_plot_action->setIcon(QIcon(":/images/toolbar16light_save.svg")); + m_save_plot_action->setToolTip("Save plot"); + connect(m_save_plot_action, &QAction::triggered, this, &SpecularDataCanvas::onSavePlotAction); +} diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.h b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.h new file mode 100644 index 0000000000000000000000000000000000000000..31d963cb617b5d23fa3f40c68e22b05a8e2a28af --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.h @@ -0,0 +1,52 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularDataCanvas.h +//! @brief Defines class SpecularDataCanvas +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#ifndef SPECULARDATACANVAS_H +#define SPECULARDATACANVAS_H + +#include "SessionItemWidget.h" +#include "WinDllMacros.h" +#include <QWidget> + +class SpecularDataItem; +class SpecularPlotCanvas; + +class BA_CORE_API_ SpecularDataCanvas : public SessionItemWidget +{ + Q_OBJECT +public: + explicit SpecularDataCanvas(QWidget* parent = nullptr); + + void setItem(SessionItem* intensityItem) override; + + QSize sizeHint() const override; + QSize minimumSizeHint() const override; + + QList<QAction*> actionList() override; + +public slots: + void onResetViewAction(); + void onSavePlotAction(); + void onMousePress(QMouseEvent* event); + +private: + SpecularDataItem* specularDataItem(); + void initActions(); + + SpecularPlotCanvas* m_plot_canvas; + QAction* m_reset_view_action; + QAction* m_save_plot_action; +}; + +#endif // SPECULARDATACANVAS_H diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.cpp b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bf45393d6af01fddd9195077f860ffaf8cd84acb --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.cpp @@ -0,0 +1,80 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.cpp +//! @brief Implements class SpecularDataWidget +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#include "SpecularDataWidget.h" +#include "SpecularDataItem.h" +#include "SpecularDataCanvas.h" +#include "JobItem.h" +#include "IntensityDataItemUtils.h" +#include "IntensityDataPropertyWidget.h" +#include <QBoxLayout> +#include <QMenu> + +SpecularDataWidget::SpecularDataWidget(QWidget* parent) + : SessionItemWidget(parent) + , m_intensity_canvas(new SpecularDataCanvas) + , m_property_widget(new IntensityDataPropertyWidget) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + + auto hlayout = new QHBoxLayout; + hlayout->setMargin(0); + hlayout->setSpacing(0); + hlayout->addWidget(m_intensity_canvas); + hlayout->addWidget(m_property_widget); + + auto mainLayout = new QVBoxLayout; + mainLayout->setMargin(0); + mainLayout->setSpacing(0); + mainLayout->addLayout(hlayout); + + setLayout(mainLayout); + + connect(m_intensity_canvas, &SpecularDataCanvas::customContextMenuRequested, this, + &SpecularDataWidget::onContextMenuRequest); + + m_property_widget->setVisible(false); +} + +void SpecularDataWidget::setItem(SessionItem* jobItem) +{ + SessionItemWidget::setItem(jobItem); + m_intensity_canvas->setItem(specularDataItem()); + m_property_widget->setItem(specularDataItem()); +} + +QList<QAction*> SpecularDataWidget::actionList() +{ + return m_intensity_canvas->actionList() + m_property_widget->actionList(); +} + +void SpecularDataWidget::onContextMenuRequest(const QPoint& point) +{ + QMenu menu; + for (auto action : actionList()) + menu.addAction(action); + menu.exec(point); +} + +SpecularDataItem* SpecularDataWidget::specularDataItem() +{ + // temporarily commented out + // return IntensityDataItemUtils::intensityDataItem(currentItem()); + + // temporary solution, should be removed after starting to use + // JobItem + SpecularDataItem* result = dynamic_cast<SpecularDataItem*>(currentItem()); + Q_ASSERT(result); + return result; +} diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.h b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..7ee6a42ca64392f325ac10cc1b8d2a7cf3ee0aa0 --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.h @@ -0,0 +1,45 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularDataWidget.h +//! @brief Defines class SpecularDataWidget +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#ifndef SPECULARDATAWIDGET_H +#define SPECULARDATAWIDGET_H + +#include "SessionItemWidget.h" + +class SpecularDataCanvas; +class SpecularDataItem; +class IntensityDataPropertyWidget; + +class SpecularDataWidget : public SessionItemWidget +{ + Q_OBJECT + +public: + SpecularDataWidget(QWidget* parent = nullptr); + + void setItem(SessionItem* jobItem); + + QList<QAction*> actionList(); + +private slots: + void onContextMenuRequest(const QPoint& point); + +private: + SpecularDataItem* specularDataItem(); + + SpecularDataCanvas* m_intensity_canvas; + IntensityDataPropertyWidget* m_property_widget; +}; + +#endif // SPECULARDATAWIDGET_H diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.cpp b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5e8a4c928b7fccc574a85b22cc15f90a6eb71473 --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.cpp @@ -0,0 +1,275 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.cpp +//! @brief Implements class SpecularPlot +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#include "SpecularPlot.h" +#include "AxesItems.h" +#include "ColorMapUtils.h" +#include "SpecularPlotEvent.h" +#include "MathConstants.h" +#include "SpecularDataItem.h" +#include "UpdateTimer.h" +#include "plot_constants.h" + +namespace { +const int replot_update_interval = 10; +} + +SpecularPlot::SpecularPlot(QWidget* parent) + : SessionItemWidget(parent) + , m_custom_plot(new QCustomPlot) + , m_update_timer(new UpdateTimer(replot_update_interval, this)) + , m_plot_event(new SpecularPlotEvent(this)) + , m_block_update(true) +{ + initPlot(); + + QVBoxLayout* vlayout = new QVBoxLayout(this); + vlayout->setMargin(0); + vlayout->setSpacing(0); + vlayout->addWidget(m_custom_plot); + setLayout(vlayout); + + setMouseTrackingEnabled(true); +} + +void SpecularPlot::setMouseTrackingEnabled(bool enable) +{ + m_plot_event->setMouseTrackingEnabled(enable); +} + +void SpecularPlot::setLog(bool log) +{ + ColorMapUtils::setLogz(m_custom_plot->yAxis, log); +} + +void SpecularPlot::resetView() +{ + specularItem()->resetView(); +} + +void SpecularPlot::onPropertyChanged(const QString& property_name) +{ + if (m_block_update) + return; + + if (property_name == SpecularDataItem::P_AXES_UNITS) { + setAxesRangeFromItem(specularItem()); + replot(); + } +} + +void SpecularPlot::onXaxisRangeChanged(QCPRange newRange) +{ + m_block_update = true; + specularItem()->setLowerX(newRange.lower); + specularItem()->setUpperX(newRange.upper); + m_block_update = false; +} + +void SpecularPlot::onYaxisRangeChanged(QCPRange newRange) +{ + m_block_update = true; + specularItem()->setLowerY(newRange.lower); + specularItem()->setUpperY(newRange.upper); + m_block_update = false; +} + +void SpecularPlot::onTimeToReplot() +{ + m_custom_plot->replot(); +} + +void SpecularPlot::subscribeToItem() +{ + setPlotFromItem(specularItem()); + + specularItem()->mapper()->setOnPropertyChange( + [this](const QString& name) { onPropertyChanged(name); }, this); + + specularItem()->mapper()->setOnChildPropertyChange( + [this](SessionItem* item, const QString name) { + if(item->modelType() == Constants::BasicAxisType || + item->modelType() == Constants::AmplitudeAxisType) + modifyAxesProperties(item->itemName(), name); + }, + this); + + specularItem()->mapper()->setOnValueChange([this]() { setPlotFromItem(this->specularItem()); }, + this); + + setConnected(true); +} + +void SpecularPlot::unsubscribeFromItem() +{ + setConnected(false); +} + +void SpecularPlot::initPlot() +{ + m_custom_plot->addGraph(); + + QPen pen(QColor(0, 0, 255, 200)); + m_custom_plot->graph()->setLineStyle(QCPGraph::lsLine); + m_custom_plot->graph()->setPen(pen); + + m_custom_plot->xAxis->setTickLabelFont( + QFont(QFont().family(), Constants::plot_tick_label_size)); + m_custom_plot->yAxis->setTickLabelFont( + QFont(QFont().family(), Constants::plot_tick_label_size)); + + m_custom_plot->xAxis->setLabelFont(QFont(QFont().family(), Constants::plot_axes_label_size)); + m_custom_plot->yAxis->setLabelFont(QFont(QFont().family(), Constants::plot_axes_label_size)); + + ColorMapUtils::setDefaultMargins(m_custom_plot); +} + +void SpecularPlot::setConnected(bool isConnected) +{ + setAxesRangeConnected(isConnected); + setUpdateTimerConnected(isConnected); +} + +void SpecularPlot::setAxesRangeConnected(bool isConnected) +{ + if (isConnected) { + connect(m_custom_plot->xAxis, SIGNAL(rangeChanged(QCPRange)), this, + SLOT(onXaxisRangeChanged(QCPRange)), Qt::UniqueConnection); + + connect(m_custom_plot->yAxis, SIGNAL(rangeChanged(QCPRange)), this, + SLOT(onYaxisRangeChanged(QCPRange)), Qt::UniqueConnection); + + } else { + disconnect(m_custom_plot->xAxis, SIGNAL(rangeChanged(QCPRange)), this, + SLOT(onXaxisRangeChanged(QCPRange))); + + disconnect(m_custom_plot->yAxis, SIGNAL(rangeChanged(QCPRange)), this, + SLOT(onYaxisRangeChanged(QCPRange))); + } +} + +void SpecularPlot::setUpdateTimerConnected(bool isConnected) +{ + if (isConnected) + connect(m_update_timer, SIGNAL(timeToUpdate()), this, SLOT(onTimeToReplot()), + Qt::UniqueConnection); + else + disconnect(m_update_timer, SIGNAL(timeToUpdate()), this, SLOT(onTimeToReplot())); +} + +void SpecularPlot::setPlotFromItem(SpecularDataItem* specularItem) +{ + Q_ASSERT(specularItem); + + m_block_update = true; + + setAxesRangeFromItem(specularItem); + setAxesLabelsFromItem(specularItem); + setDataFromItem(specularItem); + + replot(); + + m_block_update = false; +} + +void SpecularPlot::setAxesRangeFromItem(SpecularDataItem* item) +{ + m_custom_plot->setInteractions(QCP::iRangeDrag | QCP::iRangeZoom); + m_custom_plot->axisRect()->setupFullAxesBox(true); + + setAxesRangeConnected(false); + m_custom_plot->xAxis->setRange(item->getLowerX(), item->getUpperX()); + m_custom_plot->yAxis->setRange(item->getLowerY(), item->getUpperY()); + setLog(item->isLog()); + setAxesRangeConnected(true); +} + +void SpecularPlot::setAxesLabelsFromItem(SpecularDataItem* item) +{ + setLabel(item->xAxisItem(), m_custom_plot->xAxis, item->getXaxisTitle()); + setLabel(item->yAxisItem(), m_custom_plot->yAxis, item->getYaxisTitle()); +} + +void SpecularPlot::setLabel(const BasicAxisItem* item, QCPAxis* axis, QString label) +{ + Q_ASSERT(item && axis); + if(item->getItemValue(BasicAxisItem::P_TITLE_IS_VISIBLE).toBool()) + axis->setLabel(std::move(label)); + else + axis->setLabel(QString()); +} + +void SpecularPlot::setDataFromItem(SpecularDataItem* item) +{ + Q_ASSERT(item); + auto data = item->getOutputData(); + Q_ASSERT(data); + if (!data) + return; + + for (size_t i = 0, size = data->getAllocatedSize(); i < size; ++i) { + double x = data->getAxisValue(i, 0); + double y = data->operator[](i); + m_custom_plot->graph()->addData(x, y); + } +} + +SpecularDataItem* SpecularPlot::specularItem() +{ + return const_cast<SpecularDataItem*>( + static_cast<const SpecularPlot*>(this)->specularItem()); +} + +const SpecularDataItem* SpecularPlot::specularItem() const +{ + const auto result = dynamic_cast<const SpecularDataItem*>(currentItem()); + Q_ASSERT(result); + return result; +} + +void SpecularPlot::modifyAxesProperties(const QString& axisName, const QString& propertyName) +{ + if (m_block_update) + return; + + if (propertyName == BasicAxisItem::P_TITLE || + propertyName == BasicAxisItem::P_TITLE_IS_VISIBLE) { + setAxesLabelsFromItem(specularItem()); + replot(); + } + + if (axisName == SpecularDataItem::P_XAXIS) { + if (propertyName == BasicAxisItem::P_MIN || propertyName == BasicAxisItem::P_MAX) { + setAxesRangeConnected(false); + m_custom_plot->xAxis->setRange(specularItem()->getLowerX(), specularItem()->getUpperX()); + setAxesRangeConnected(true); + replot(); + } + } else if (axisName == SpecularDataItem::P_YAXIS) { + if (propertyName == BasicAxisItem::P_MIN || propertyName == BasicAxisItem::P_MAX) { + setAxesRangeConnected(false); + m_custom_plot->yAxis->setRange(specularItem()->getLowerY(), specularItem()->getUpperY()); + setAxesRangeConnected(true); + replot(); + } else if (propertyName == AmplitudeAxisItem::P_IS_LOGSCALE) { + setLog(specularItem()->isLog()); + replot(); + } + } +} + +void SpecularPlot::replot() +{ + m_update_timer->scheduleUpdate(); +} diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.h b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.h new file mode 100644 index 0000000000000000000000000000000000000000..b6dd5a2851e1a831f34eaeafe6303d422cdca6d3 --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.h @@ -0,0 +1,114 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularPlot.h +//! @brief Defines class SpecularPlot +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#ifndef SPECULARPLOT_H +#define SPECULARPLOT_H + +#include "SessionItemWidget.h" +#include "ColorMapBin.h" +#include "qcustomplot.h" +#include <memory> + +class BasicAxisItem; +class SpecularDataItem; +class SpecularPlotEvent; +class UpdateTimer; + +//! The SpecularPlot class presents 1D intensity data from SpecularDataItem. + +//! Provides minimal functionality for data plotting and axes interaction. Should be a component +//! for more complicated plotting widgets. Corresponds to ColorMap for 2D intensity data. + +class BA_CORE_API_ SpecularPlot : public SessionItemWidget +{ + Q_OBJECT + +public: + explicit SpecularPlot(QWidget* parent = 0); + + QSize sizeHint() const override { return QSize(500, 400); } + QSize minimumSizeHint() const override { return QSize(128, 128); } + + QCustomPlot* customPlot() { return m_custom_plot; } + const QCustomPlot* customPlot() const { return m_custom_plot; } + + //! to track move events (used when showing profile histograms and printing status string) + void setMouseTrackingEnabled(bool enable); + + //! sets logarithmic scale + void setLog(bool log); + + //! reset all axes min,max to initial value + void resetView(); + +private slots: + //! updates plot depending on IntensityDataItem properties + void onPropertyChanged(const QString& property_name); + + //! Propagate xmin, xmax back to IntensityDataItem + void onXaxisRangeChanged(QCPRange newRange); + + //! Propagate ymin, ymax back to IntensityDataItem + void onYaxisRangeChanged(QCPRange newRange); + + //! Replots SpecularPlot. + void onTimeToReplot(); + +protected: + void subscribeToItem() override; + void unsubscribeFromItem() override; + +private: + //! creates and initializes the color map + void initPlot(); + + void setConnected(bool isConnected); + + //! Connects/disconnects signals related to SpecularPlot's X,Y axes rectangle change. + void setAxesRangeConnected(bool isConnected); + + void setUpdateTimerConnected(bool isConnected); + + //! Sets initial state of SpecularPlot to match given intensity item. + void setPlotFromItem(SpecularDataItem* intensityItem); + + //! Sets (xmin,xmax) and (ymin,ymax) of SpecularPlot from specular item. + //! Also sets logarithmic scale on y-axis if necessary. + void setAxesRangeFromItem(SpecularDataItem* item); + + //! Sets X,Y axes labels from item + void setAxesLabelsFromItem(SpecularDataItem* item); + + //! Sets label to axis + void setLabel(const BasicAxisItem* item, QCPAxis* axis, QString label); + + //! Sets the intensity values to SpecularPlot. + void setDataFromItem(SpecularDataItem* item); + + SpecularDataItem* specularItem(); + const SpecularDataItem* specularItem() const; + + void modifyAxesProperties(const QString& axisName, const QString& propertyName); + + //! Schedule replot for later execution by onTimeReplot() slot. + void replot(); + + QCustomPlot* m_custom_plot; + UpdateTimer* m_update_timer; + SpecularPlotEvent* m_plot_event; + + bool m_block_update; +}; + +#endif // SPECULARPLOT_H diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.cpp b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0af71f7600967d57fb76c5b78eb0d8fec4db9fd3 --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.cpp @@ -0,0 +1,47 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.cpp +//! @brief Declares class SpecularPlotCanvas +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#include "SpecularPlotCanvas.h" +#include "SpecularPlot.h" +#include "SpecularDataItem.h" +#include <QVBoxLayout> + +SpecularPlotCanvas::SpecularPlotCanvas(QWidget *parent) + : SessionItemWidget(parent) + , m_plot(new SpecularPlot) +{ + QVBoxLayout* layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + + layout->addWidget(m_plot); + + setLayout(layout); +} + +void SpecularPlotCanvas::setItem(SessionItem* specularDataItem) +{ + SessionItemWidget::setItem(specularDataItem); + m_plot->setItem(dynamic_cast<SpecularDataItem*>(specularDataItem)); +} + +SpecularPlot* SpecularPlotCanvas::specularPlot() +{ + return m_plot; +} + +QCustomPlot* SpecularPlotCanvas::customPlot() +{ + return m_plot->customPlot(); +} diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.h b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.h new file mode 100644 index 0000000000000000000000000000000000000000..e66606ad0c849e7189566a5195a1e39be6cb7eb8 --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.h @@ -0,0 +1,42 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularPlotCanvas.h +//! @brief Defines class ColorMapCanvas +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#ifndef SPECULARPLOTCANVAS_H +#define SPECULARPLOTCANVAS_H + +#include "SessionItemWidget.h" + +class QCustomPlot; +class SpecularPlot; + +//! The SpecularPlotCanvas class contains SpecularPlot for specular data presentation, and provides +//! status string appearance. + +class BA_CORE_API_ SpecularPlotCanvas : public SessionItemWidget +{ + Q_OBJECT + +public: + explicit SpecularPlotCanvas(QWidget* parent = 0); + + void setItem(SessionItem* specularDataItem) override; + + SpecularPlot* specularPlot(); + QCustomPlot* customPlot(); + +private: + SpecularPlot* m_plot; +}; + +#endif // SPECULARPLOTCANVAS_H diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.cpp b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9f6b2714a7e1f01e41df657eb756c4513da58eeb --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.cpp @@ -0,0 +1,56 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.cpp +//! @brief Implements class SpecularPlotEvent +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#include "SpecularPlotEvent.h" +#include "SpecularPlot.h" +#include <QMouseEvent> + +SpecularPlotEvent::SpecularPlotEvent(SpecularPlot* plot) : QObject(plot), m_plot(plot) {} + +//! Sets tracking of the mouse for parent COlorMap + +void SpecularPlotEvent::setMouseTrackingEnabled(bool enable) +{ + m_plot->setMouseTracking(enable); + customPlot()->setMouseTracking(enable); + + if (enable) + connect(customPlot(), &QCustomPlot::mouseMove, this, + &SpecularPlotEvent::onCustomMouseMove, Qt::UniqueConnection); + else + disconnect(customPlot(), &QCustomPlot::mouseMove, this, + &SpecularPlotEvent::onCustomMouseMove); +} + +void SpecularPlotEvent::onCustomMouseMove(QMouseEvent* event) +{ + Q_UNUSED(event); + // this method should track mouse position in plot + // left unimplemented for now +} + +SpecularPlot* SpecularPlotEvent::specularPlot() +{ + return m_plot; +} + +const SpecularPlot* SpecularPlotEvent::specularPlot() const +{ + return m_plot; +} + +QCustomPlot* SpecularPlotEvent::customPlot() +{ + return m_plot->customPlot(); +} diff --git a/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.h b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.h new file mode 100644 index 0000000000000000000000000000000000000000..c99a07582dd562682390de2e9a02c4ed76b639c9 --- /dev/null +++ b/GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.h @@ -0,0 +1,51 @@ +// ************************************************************************** // +// +// BornAgain: simulate and fit scattering at grazing incidence +// +//! @file GUI/coregui/Views/SpecularDataWidgets/SpecularPlotEvent.h +//! @brief Defines class SpecularPlotEvent +//! +//! @homepage http://www.bornagainproject.org +//! @license GNU General Public License v3 or higher (see COPYING) +//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) +// +// ************************************************************************** // + +#ifndef SPECULARPLOTEVENT_H +#define SPECULARPLOTEVENT_H + +#include "WinDllMacros.h" +#include <QObject> + +class SpecularPlot; +class QMouseEvent; +class QCustomPlot; + +//! Helps SpecularPlot to handle mouse events. Particularly, it constructs a valid status string. +//! Can be extended to play a role of event filter. + +class BA_CORE_API_ SpecularPlotEvent : public QObject +{ + Q_OBJECT + +public: + explicit SpecularPlotEvent(SpecularPlot* colorMap); + + void setMouseTrackingEnabled(bool enable); + +public slots: + //! Constructs status string on mouse move event coming from QCustomPlot. String is emitted + //! if mouse is in axes's viewport rectangle. Once mouse goes out of it, an + //! empty string is emitted once again. + void onCustomMouseMove(QMouseEvent* event); + +private: + SpecularPlot* specularPlot(); + const SpecularPlot* specularPlot() const; + QCustomPlot* customPlot(); + + SpecularPlot* m_plot; +}; + +#endif // SPECULARPLOTEVENT_H diff --git a/GUI/coregui/Views/TestView.cpp b/GUI/coregui/Views/TestView.cpp index fcdbc0990f968239392b5e8e92ab3d67b3ccf624..69f62308b9d239b284243ae9b67dc24e6f93387c 100644 --- a/GUI/coregui/Views/TestView.cpp +++ b/GUI/coregui/Views/TestView.cpp @@ -21,6 +21,8 @@ #include "MinimizerSettingsWidget.h" #include "ApplicationModels.h" #include "SampleModel.h" +#include "SpecularDataItem.h" +#include "SpecularDataWidget.h" #include "TestComponentView.h" #include "mainwindow.h" #include <QTreeView> @@ -32,16 +34,25 @@ #include <QCheckBox> #include <QLineEdit> +namespace { +// These functions are required for testing purposes only +// They must be removed after completion of +// SpecularDataWidget +double getTestValue(size_t bin); +SpecularDataItem* fillTestItem(SessionItem* item); +} + TestView::TestView(MainWindow *mainWindow) : QWidget(mainWindow) , m_mainWindow(mainWindow) { - test_ComponentProxyModel(); +// test_ComponentProxyModel(); // test_MaterialEditor(); // test_MinimizerSettings(); // test_AccordionWidget(); // test_RunFitWidget(); // test_ba3d(); + test_specular_data_widget(); } void TestView::test_ComponentProxyModel() @@ -169,3 +180,39 @@ void TestView::test_ba3d() setLayout(layout); } + +void TestView::test_specular_data_widget() +{ + SessionModel* tempModel = new SessionModel("Test", this); + auto data = fillTestItem(tempModel->insertNewItem(Constants::SpecularDataType)); + + QVBoxLayout *layout = new QVBoxLayout; + layout->setMargin(0); + layout->setSpacing(0); + auto widget = new SpecularDataWidget(this); + widget->setItem(data); + layout->addWidget(widget); + setLayout(layout); +} + +namespace { +double getTestValue(size_t bin) +{ + const double factor = M_PI / (180.0 * 100.0); + const double angle = bin * factor; + return (std::cos(angle * 1000.0) + 1.5) * std::exp(-(bin / 100.0)); +} + +SpecularDataItem* fillTestItem(SessionItem* item) +{ + SpecularDataItem* result = dynamic_cast<SpecularDataItem*>(item); + Q_ASSERT(result); + auto outputData = std::make_unique<OutputData<double>>(); + outputData->addAxis(FixedBinAxis("Angle [deg]", 1000, 0.0, 10.0)); + for (size_t i = 0; i < 1000; ++i) + outputData->operator[](i) = getTestValue(i); + + result->setOutputData(outputData.release()); + return result; +} +} diff --git a/GUI/coregui/Views/TestView.h b/GUI/coregui/Views/TestView.h index 41e5a044f50e481e605e9e552e89f13e437fbea2..47e24725f165a34485580f8ba3846846d3e837f1 100644 --- a/GUI/coregui/Views/TestView.h +++ b/GUI/coregui/Views/TestView.h @@ -32,6 +32,7 @@ private: void test_MinimizerSettings(); void test_AccordionWidget(); void test_ba3d(); + void test_specular_data_widget(); MainWindow* m_mainWindow; };