diff --git a/GUI/Model/Item/RectangularDetectorItem.cpp b/GUI/Model/Item/RectangularDetectorItem.cpp index d1c458c9b41f0eee5db7686c8e0102bef2adcbab..42f7cb9e4845508ac6405cbd464e9f964bf0370c 100644 --- a/GUI/Model/Item/RectangularDetectorItem.cpp +++ b/GUI/Model/Item/RectangularDetectorItem.cpp @@ -63,7 +63,7 @@ QString alignmentTitle(RectangularDetector::EDetectorArrangement a) case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM: return "Perpendicular to reflected beam"; case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM_DPOS: - return "Perpendicular to reflected beam (dpos)"; + return "Perpendicular to reflected beam (intersection unknown)"; default: ASSERT(false); } diff --git a/GUI/View/Instrument/DetectorAlignmentEditor.cpp b/GUI/View/Instrument/DetectorAlignmentEditor.cpp index 8cf2ee19fd503a3f2249cd3b536d18db43dbccd2..86f0d9e403a5bf05f8e9bbcfdb44a4177aaa1840 100644 --- a/GUI/View/Instrument/DetectorAlignmentEditor.cpp +++ b/GUI/View/Instrument/DetectorAlignmentEditor.cpp @@ -15,6 +15,7 @@ #include "GUI/View/Instrument/DetectorAlignmentEditor.h" #include "GUI/Model/Item/RectangularDetectorItem.h" #include "GUI/Util/ComboProperty.h" +#include "GUI/View/Edit/DoubleSpinBox.h" #include "GUI/View/Instrument/VectorEditor.h" #include "GUI/View/Tool/WidgetUtils.h" #include <QComboBox> @@ -23,356 +24,26 @@ #include <QGroupBox> #include <QStackedLayout> -//================================================================================================== -// DetectorAlignmentForm -//================================================================================================== - -/// abstract interface of all detector alignment forms -class DetectorAlignmentForm : public QWidget { -public: - /// Set type if not already the matching one and display parameters of distribution - virtual void setItem(RectangularDetectorItem* item) = 0; - -protected: - DetectorAlignmentForm(QWidget* parent, std::function<void()> dataChangedSlot) - : QWidget(parent) - , dataChanged(dataChangedSlot) - { - } - - std::function<void()> dataChanged; -}; - -//================================================================================================== -// GenericDetectorAlignmentForm -//================================================================================================== - -/// Alignment form for the generic case -class GenericDetectorAlignmentForm : public DetectorAlignmentForm { -public: - GenericDetectorAlignmentForm(QWidget* parent, std::function<void()> dataChangedSlot); - void setItem(RectangularDetectorItem* item) override; - -private slots: - void onU0ValueChanged(double u0); - void onV0ValueChanged(double v0); - -private: - QDoubleSpinBox* m_u0SpinBox; - QDoubleSpinBox* m_v0SpinBox; - VectorEditor* m_normalEditor; - VectorEditor* m_directionEditor; - RectangularDetectorItem* m_item; -}; - -GenericDetectorAlignmentForm::GenericDetectorAlignmentForm(QWidget* parent, - std::function<void()> dataChangedSlot) - : DetectorAlignmentForm(parent, dataChangedSlot) - , m_item(nullptr) -{ - auto* layout = new QHBoxLayout(this); - - auto* gbox = new QGroupBox("Intersection of normal and detector", this); - auto* form = new QFormLayout(gbox); - layout->addWidget(gbox); - - m_u0SpinBox = new QDoubleSpinBox(gbox); - m_u0SpinBox->setDecimals(3); - m_u0SpinBox->setRange(std::numeric_limits<double>::lowest(), - std::numeric_limits<double>::max()); - m_u0SpinBox->setSingleStep(0.01); - m_u0SpinBox->setToolTip("u-coordinate of intersection of normal vector " - "and detector plane, \n in local detector coordinates"); - connect(m_u0SpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, - &GenericDetectorAlignmentForm::onU0ValueChanged); - form->addRow("u0 [mm]:", m_u0SpinBox); - - m_v0SpinBox = new QDoubleSpinBox(gbox); - m_v0SpinBox->setDecimals(3); - m_v0SpinBox->setRange(std::numeric_limits<double>::lowest(), - std::numeric_limits<double>::max()); - m_v0SpinBox->setSingleStep(0.01); - m_v0SpinBox->setToolTip("v-coordinate of intersection of normal vector " - "and detector plane, \n in local detector coordinates"); - connect(m_v0SpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, - &GenericDetectorAlignmentForm::onV0ValueChanged); - form->addRow("v0 [mm]:", m_v0SpinBox); - - m_normalEditor = new VectorEditor( - "Normal vector", - "Normal of the detector plane with length equal to the sample detector distance (%1)", - this); - layout->addWidget(m_normalEditor); - connect(m_normalEditor, &VectorEditor::vectorChanged, [=]() { dataChanged(); }); - - m_directionEditor = new VectorEditor( - "Detector axis direction vector", - "Detector axis direction vector w.r.t. the sample coordinate system (%1)", this); - layout->addWidget(m_directionEditor); - connect(m_directionEditor, &VectorEditor::vectorChanged, [=]() { dataChanged(); }); -} - -void GenericDetectorAlignmentForm::setItem(RectangularDetectorItem* item) -{ - m_item = item; - m_u0SpinBox->setValue(m_item->u0()); - m_v0SpinBox->setValue(m_item->v0()); - m_normalEditor->setVector(m_item->normalVector()); - m_directionEditor->setVector(m_item->directionVector()); -} - -void GenericDetectorAlignmentForm::onU0ValueChanged(double u0) -{ - if (m_item && m_item->u0() != u0) { - m_item->setU0(u0); - dataChanged(); - } -} - -void GenericDetectorAlignmentForm::onV0ValueChanged(double v0) -{ - if (m_item && m_item->v0() != v0) { - - m_item->setV0(v0); - dataChanged(); - } -} - -//================================================================================================== -// PerpendicularDetectorAlignmentForm -//================================================================================================== - -/// Abstract alignment form for the perpendicular case -class PerpendicularDetectorAlignmentForm : public DetectorAlignmentForm { -public: - void setItem(RectangularDetectorItem* item) override; - -protected: - PerpendicularDetectorAlignmentForm(const QString& title, const QString& tooltip, - QWidget* parent, std::function<void()> dataChangedSlot); - RectangularDetectorItem* item() const; - -private slots: - void onU0ValueChanged(double u0); - void onV0ValueChanged(double v0); - void onDistanceValueChanged(double distance); - -private: - virtual double getU0() const = 0; - virtual double getV0() const = 0; - virtual void setU0(double u0) = 0; - virtual void setV0(double v0) = 0; - - QDoubleSpinBox* m_u0SpinBox; - QDoubleSpinBox* m_v0SpinBox; - QDoubleSpinBox* m_distanceSpinBox; - RectangularDetectorItem* m_item; -}; - -//-------------------------------------------------------------------------------------------------- -// public member functions -//-------------------------------------------------------------------------------------------------- - -void PerpendicularDetectorAlignmentForm::setItem(RectangularDetectorItem* item) -{ - m_item = item; - m_u0SpinBox->setValue(getU0()); - m_v0SpinBox->setValue(getV0()); - m_distanceSpinBox->setValue(m_item->distance()); -} - -//-------------------------------------------------------------------------------------------------- -// protected member functions -//-------------------------------------------------------------------------------------------------- - -PerpendicularDetectorAlignmentForm::PerpendicularDetectorAlignmentForm( - const QString& title, const QString& tooltip, QWidget* parent, - std::function<void()> dataChangedSlot) - : DetectorAlignmentForm(parent, dataChangedSlot) - , m_item(nullptr) -{ - auto* layout = new QHBoxLayout(this); - - auto* gbox = new QGroupBox(title, this); - auto* form = new QFormLayout(gbox); - layout->addWidget(gbox); - - m_u0SpinBox = new QDoubleSpinBox(gbox); - m_u0SpinBox->setDecimals(3); - m_u0SpinBox->setRange(std::numeric_limits<double>::lowest(), - std::numeric_limits<double>::max()); - m_u0SpinBox->setSingleStep(0.01); - m_u0SpinBox->setToolTip(tooltip.arg("u-coordinate")); - connect(m_u0SpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, - &PerpendicularDetectorAlignmentForm::onU0ValueChanged); - form->addRow("u0 [mm]:", m_u0SpinBox); - - m_v0SpinBox = new QDoubleSpinBox(gbox); - m_v0SpinBox->setDecimals(3); - m_v0SpinBox->setRange(std::numeric_limits<double>::lowest(), - std::numeric_limits<double>::max()); - m_v0SpinBox->setSingleStep(0.01); - m_v0SpinBox->setToolTip(tooltip.arg("v-coordinate")); - connect(m_v0SpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, - &PerpendicularDetectorAlignmentForm::onV0ValueChanged); - form->addRow("v0 [mm]:", m_v0SpinBox); - - m_distanceSpinBox = new QDoubleSpinBox(gbox); - m_distanceSpinBox->setDecimals(3); - m_distanceSpinBox->setRange(0, std::numeric_limits<double>::max()); - m_distanceSpinBox->setSingleStep(0.01); - m_distanceSpinBox->setToolTip("Distance in [mm] from the sample origin to the detector plane"); - connect(m_distanceSpinBox, qOverload<double>(&QDoubleSpinBox::valueChanged), this, - &PerpendicularDetectorAlignmentForm::onDistanceValueChanged); - form->addRow("Distance [mm]:", m_distanceSpinBox); -} - -RectangularDetectorItem* PerpendicularDetectorAlignmentForm::item() const -{ - return m_item; -} - -//-------------------------------------------------------------------------------------------------- -// private slots -//-------------------------------------------------------------------------------------------------- - -void PerpendicularDetectorAlignmentForm::onU0ValueChanged(double u0) -{ - setU0(u0); -} - -void PerpendicularDetectorAlignmentForm::onV0ValueChanged(double v0) -{ - setV0(v0); -} - -void PerpendicularDetectorAlignmentForm::onDistanceValueChanged(double distance) -{ - if (m_item && m_item->distance() != distance) { - m_item->setDistance(distance); - dataChanged(); - } -} - -//================================================================================================== -// ReflectedBeamDetectorAlignmentForm -//================================================================================================== - -/// detector alignment form for the perpendicular case where the reflected beam is used -class ReflectedBeamDetectorAlignmentForm : public PerpendicularDetectorAlignmentForm { -public: - ReflectedBeamDetectorAlignmentForm(const QString& title, const QString& tooltip, - QWidget* parent, std::function<void()> dataChangedSlot); - -private: - double getU0() const override; - double getV0() const override; - void setU0(double u0) override; - void setV0(double v0) override; -}; - -//-------------------------------------------------------------------------------------------------- -// public member functions -//-------------------------------------------------------------------------------------------------- - -ReflectedBeamDetectorAlignmentForm::ReflectedBeamDetectorAlignmentForm( - const QString& title, const QString& tooltip, QWidget* parent, - std::function<void()> dataChangedSlot) - : PerpendicularDetectorAlignmentForm(title, tooltip, parent, dataChangedSlot) -{ -} - -//-------------------------------------------------------------------------------------------------- -// private slots -//-------------------------------------------------------------------------------------------------- - -double ReflectedBeamDetectorAlignmentForm::getU0() const -{ - return item()->u0(); -} - -double ReflectedBeamDetectorAlignmentForm::getV0() const -{ - return item()->v0(); -} - -void ReflectedBeamDetectorAlignmentForm::setU0(double u0) -{ - if (item() && item()->u0() != u0) { - item()->setU0(u0); - dataChanged(); - } -} - -void ReflectedBeamDetectorAlignmentForm::setV0(double v0) -{ - if (item() && item()->v0() != v0) { - item()->setV0(v0); - dataChanged(); - } -} - -//================================================================================================== -// DirectBeamDetectorAlignmentForm -//================================================================================================== - -/// detector alignment form for the perpendicular case where the direct beam is used -class DirectBeamDetectorAlignmentForm : public PerpendicularDetectorAlignmentForm { -public: - DirectBeamDetectorAlignmentForm(const QString& title, const QString& tooltip, QWidget* parent, - std::function<void()> dataChangedSlot); - -private: - double getU0() const override; - double getV0() const override; - void setU0(double u0) override; - void setV0(double v0) override; -}; - -//-------------------------------------------------------------------------------------------------- -// public member functions -//-------------------------------------------------------------------------------------------------- - -DirectBeamDetectorAlignmentForm::DirectBeamDetectorAlignmentForm( - const QString& title, const QString& tooltip, QWidget* parent, - std::function<void()> dataChangedSlot) - : PerpendicularDetectorAlignmentForm(title, tooltip, parent, dataChangedSlot) +namespace { +QString alignmentDescription(RectangularDetector::EDetectorArrangement a) { -} - -//-------------------------------------------------------------------------------------------------- -// private slots -//-------------------------------------------------------------------------------------------------- - -double DirectBeamDetectorAlignmentForm::getU0() const -{ - return item()->directBeamU0(); -} - -double DirectBeamDetectorAlignmentForm::getV0() const -{ - return item()->directBeamV0(); -} - -void DirectBeamDetectorAlignmentForm::setU0(double u0) -{ - if (item() && item()->directBeamU0() != u0) { - item()->setDirectBeamU0(u0); - dataChanged(); - } -} - -void DirectBeamDetectorAlignmentForm::setV0(double v0) -{ - if (item() && item()->directBeamV0() != v0) { - item()->setDirectBeamV0(v0); - dataChanged(); + switch (a) { + case RectangularDetector::GENERIC: + return "Intersection of normal and detector"; + case RectangularDetector::PERPENDICULAR_TO_SAMPLE: + return "Intersection of sample x-axis and detector"; + case RectangularDetector::PERPENDICULAR_TO_DIRECT_BEAM: + return "Intersection of direct beam and detector"; + case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM: + return "Intersection of reflected beam and detector"; + case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM_DPOS: + return "Intersection of direct beam and detector"; + default: + ASSERT(false); } } -//================================================================================================== -// DetectorAlignmentEditor -//================================================================================================== +} // namespace DetectorAlignmentEditor::DetectorAlignmentEditor(QWidget* parent, RectangularDetectorItem* item) : QWidget(parent) @@ -380,48 +51,85 @@ DetectorAlignmentEditor::DetectorAlignmentEditor(QWidget* parent, RectangularDet { ASSERT(m_item); m_formLayout = new QFormLayout(this); - - auto* m_combo = GUI::Util::createSelectionCombo(this, item->detectorAlignmentSelection(), - [=](int) { createAligmentWidgets(); }); + m_formLayout->setContentsMargins(0, 15, 0, 0); + m_formLayout->setSpacing(8); + + auto* m_combo = + GUI::Util::createSelectionCombo(this, item->detectorAlignmentSelection(), [=](int) { + createAligmentWidgets(); + emit dataChanged(); + }); m_formLayout->addRow("Alignment:", m_combo); createAligmentWidgets(); } +DoubleSpinBox* DetectorAlignmentEditor::createSpinBox(QFormLayout* parentFormLayout, + const DoubleDescriptor& d) +{ + auto* sb = GUI::Util::createSpinBox(parentFormLayout, d); + connect(sb, qOverload<double>(&DoubleSpinBox::baseValueChanged), [=](double v) { + if (d.get() != v) { + d.set(v); + emit dataChanged(); + } + }); + return sb; +} + void DetectorAlignmentEditor::createAligmentWidgets() { while (m_formLayout->rowCount() > 1) m_formLayout->removeRow(1); + const QString descr = alignmentDescription(m_item->detectorAlignment()); switch (m_item->detectorAlignment()) { - case RectangularDetector::GENERIC: - m_formLayout->addRow(new GenericDetectorAlignmentForm(this, [=]() { dataChanged(); })); - break; - case RectangularDetector::PERPENDICULAR_TO_SAMPLE: - m_formLayout->addRow(new ReflectedBeamDetectorAlignmentForm( - "Intersection of sample x-axis and detector", - "Point where the sample x-axis intersects with the detector plane (%1)", this, - [=]() { dataChanged(); })); - break; - case RectangularDetector::PERPENDICULAR_TO_DIRECT_BEAM: - m_formLayout->addRow(new DirectBeamDetectorAlignmentForm( - "Intersection of direct beam and detector", - "Point where the direct beam hits the detector plane (%1)", this, - [=]() { dataChanged(); })); - break; - case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM: - m_formLayout->addRow(new ReflectedBeamDetectorAlignmentForm( - "Intersection of reflected beam and detector", - "Point where the reflected beam hits the detector plane (%1)", this, - [=]() { dataChanged(); })); - break; - case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM_DPOS: - m_formLayout->addRow(new DirectBeamDetectorAlignmentForm( - "Intersection of direct beam and detector", - "Point where the direct beam hits the detector plane (%1)", this, - [=]() { dataChanged(); })); - break; + case RectangularDetector::GENERIC: { + auto* layout = new QHBoxLayout(this); + auto* gbox = new QGroupBox(descr, this); + auto* form = new QFormLayout(gbox); + + createSpinBox(form, m_item->u0()); + createSpinBox(form, m_item->v0()); + + auto* normalEditor = new VectorEditor( + "Normal vector", + "Normal of the detector plane with length equal to the sample detector distance (%1)", + this); + normalEditor->setVector(m_item->normalVector()); + connect(normalEditor, &VectorEditor::vectorChanged, [=]() { dataChanged(); }); + + auto* directionEditor = new VectorEditor( + "Detector axis direction vector", + "Detector axis direction vector w.r.t. the sample coordinate system (%1)", this); + directionEditor->setVector(m_item->directionVector()); + connect(directionEditor, &VectorEditor::vectorChanged, [=]() { dataChanged(); }); + + layout->addWidget(gbox); + layout->addWidget(normalEditor); + layout->addWidget(directionEditor); + + m_formLayout->addRow(layout); + } break; + case RectangularDetector::PERPENDICULAR_TO_SAMPLE: // fall-through! + case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM: { + auto* gbox = new QGroupBox(descr, this); + auto* form = new QFormLayout(gbox); + createSpinBox(form, m_item->u0()); + createSpinBox(form, m_item->v0()); + createSpinBox(form, m_item->distance()); + m_formLayout->addRow(gbox); + } break; + case RectangularDetector::PERPENDICULAR_TO_DIRECT_BEAM: // fall-through! + case RectangularDetector::PERPENDICULAR_TO_REFLECTED_BEAM_DPOS: { + auto* gbox = new QGroupBox(descr, this); + auto* form = new QFormLayout(gbox); + createSpinBox(form, m_item->directBeamU0()); + createSpinBox(form, m_item->directBeamV0()); + createSpinBox(form, m_item->distance()); + m_formLayout->addRow(gbox); + } break; default: - break; + ASSERT(false); } } diff --git a/GUI/View/Instrument/DetectorAlignmentEditor.h b/GUI/View/Instrument/DetectorAlignmentEditor.h index 7285424b3df40de8ff93da663b2a762b23b0f63a..dd138d0e36503c0a3937c862a54727fc6f31711b 100644 --- a/GUI/View/Instrument/DetectorAlignmentEditor.h +++ b/GUI/View/Instrument/DetectorAlignmentEditor.h @@ -23,6 +23,8 @@ class QStackedLayout; class DetectorAlignmentForm; class RectangularDetectorItem; class QFormLayout; +class DoubleSpinBox; +class DoubleDescriptor; /// Widget for selecting the alignment of a detector (combo box) and input of the corresponding /// values @@ -37,6 +39,7 @@ signals: private: void createAligmentWidgets(); + DoubleSpinBox* createSpinBox(QFormLayout* parentFormLayout, const DoubleDescriptor& d); private: RectangularDetectorItem* m_item;