diff --git a/GUI/Model/Job/ParameterTreeUtils.cpp b/GUI/Model/Job/ParameterTreeUtils.cpp index 3dfaf7c2559bbe1a2bcc56d6dd56c7332f77309a..94c008431536ea8848ef81459960a888375f0ea7 100644 --- a/GUI/Model/Job/ParameterTreeUtils.cpp +++ b/GUI/Model/Job/ParameterTreeUtils.cpp @@ -19,6 +19,8 @@ #include "GUI/Model/Instrument/InstrumentItems.h" #include "GUI/Model/Job/JobItem.h" #include "GUI/Model/Material/MaterialItem.h" +#include "GUI/Model/Sample/InterferenceItems.h" +#include "GUI/Model/Sample/Lattice2DItems.h" #include "GUI/Model/Sample/MultiLayerItem.h" #include "GUI/Model/Sample/ParticleCoreShellItem.h" #include "GUI/Model/Sample/ParticleItem.h" @@ -60,6 +62,11 @@ void handleItem(ParameterContainerItem* container, SessionItem* tree, SessionIte if (c->shell()) c->shell()->positionItem()->setEnabled(false); + // later in handleItems the rotation of an interference may have to be ignored. + if (auto* c = dynamic_cast<Interference2DAbstractLatticeItem*>(child)) + if (c->latticeType().currentItem()) + c->latticeType().currentItem()->enableRotationAngle(!c->xiIntegration()); + if (child->isVisible() && child->isEnabled()) { if (child->hasModelType<PropertyItem>()) { if (child->value().type() == QVariant::Double) { diff --git a/GUI/Model/Sample/InterferenceItems.cpp b/GUI/Model/Sample/InterferenceItems.cpp index 9bf360c2a536ddba51ee1b37f37d807bcba5ffc7..54d34ea8fd90b0e4c86953cba72e89897f0d4118 100644 --- a/GUI/Model/Sample/InterferenceItems.cpp +++ b/GUI/Model/Sample/InterferenceItems.cpp @@ -119,27 +119,6 @@ Interference2DAbstractLatticeItem::Interference2DAbstractLatticeItem(const QStri addProperty(P_XI_INTEGRATION, xi_integration) ->setToolTip("Enables/disables averaging over the lattice rotation angle."); - - mapper()->setOnPropertyChange([this](const QString& name) { - if (name == P_XI_INTEGRATION && isTag(P_LATTICE_TYPE)) - update_rotation_availability(); - }); - - mapper()->setOnChildPropertyChange([this](SessionItem* item, const QString&) { - if (item->hasModelType<GroupItem>() && item->displayName() == P_LATTICE_TYPE) - update_rotation_availability(); - }); -} - -//! Sets rotation property of the lattice enabled/disabled depending on integration flag. - -void Interference2DAbstractLatticeItem::update_rotation_availability() -{ - auto* p_lattice_item = latticeType().currentItem(); - if (p_lattice_item) { - auto* angle_item = p_lattice_item->latticeRotationAngleItem(); - angle_item->setEnabled(!getItemValue(P_XI_INTEGRATION).toBool()); - } } // --------------------------------------------------------------------------------------------- // diff --git a/GUI/Model/Sample/InterferenceItems.h b/GUI/Model/Sample/InterferenceItems.h index e5914d400923b716532f13b0727cd1026e74718d..18ee21c7b056acc8decaf03e8288d536fedd8a34 100644 --- a/GUI/Model/Sample/InterferenceItems.h +++ b/GUI/Model/Sample/InterferenceItems.h @@ -74,7 +74,6 @@ public: protected: explicit Interference2DAbstractLatticeItem(const QString& modelType, bool xi_integration); - void update_rotation_availability(); }; class Interference2DLatticeItem : public Interference2DAbstractLatticeItem { diff --git a/GUI/Model/Sample/Lattice2DItems.cpp b/GUI/Model/Sample/Lattice2DItems.cpp index 87bd888c864854f4ceec60706ead8d8ee5d911b4..cabb75342442e497aae176f4fa4316a752b04169 100644 --- a/GUI/Model/Sample/Lattice2DItems.cpp +++ b/GUI/Model/Sample/Lattice2DItems.cpp @@ -51,6 +51,11 @@ void Lattice2DItem::setLatticeRotationAngle(const double angle) setItemValue(P_LATTICE_ROTATION_ANGLE, angle); } +void Lattice2DItem::enableRotationAngle(bool b) +{ + latticeRotationAngleItem()->setEnabled(b); +} + // --------------------------------------------------------------------------------------------- // BasicLattice2DItem::BasicLattice2DItem() : Lattice2DItem(M_TYPE) diff --git a/GUI/Model/Sample/Lattice2DItems.h b/GUI/Model/Sample/Lattice2DItems.h index b71d34d2cb03febffc453a1c3424aa3358e47d23..e13eff61e8c0b6b2b8373cd76e5737b4ffd71e5d 100644 --- a/GUI/Model/Sample/Lattice2DItems.h +++ b/GUI/Model/Sample/Lattice2DItems.h @@ -34,6 +34,12 @@ public: SessionItem* latticeRotationAngleItem() const; DoubleDescriptor latticeRotationAngle() const; void setLatticeRotationAngle(double angle); + + //! enable or disable rotation angle in ParameterTuningTree. + //! + //! #bamigration This is for migration only; after SessionModel migration, this has to be done + //! in the creation of the tuning tree + void enableRotationAngle(bool b); }; class BasicLattice2DItem : public Lattice2DItem { diff --git a/GUI/Model/Sample/LayerItem.cpp b/GUI/Model/Sample/LayerItem.cpp index ec5eeb300e6160122e0a39890cb59f1f3cb11962..e660e98ec69eddf7fbafc5031c1ea6e82f77ede5 100644 --- a/GUI/Model/Sample/LayerItem.cpp +++ b/GUI/Model/Sample/LayerItem.cpp @@ -28,10 +28,6 @@ namespace { -const QString layer_nslices_tooltip = "Number of horizontal slices.\n" - "Used for Average Layer Material calculations \n" - "when corresponding simulation option is set."; - QVector<ItemWithMaterial*> layoutItemsWithMaterial(ParticleLayoutItem* item) { QVector<ItemWithMaterial*> ret; @@ -63,15 +59,8 @@ QVector<ItemWithMaterial*> layoutItemsWithMaterial(ParticleLayoutItem* item) LayerItem::LayerItem() : SessionItem(M_TYPE), ItemWithMaterial(M_TYPE) { - setToolTip("A layer with thickness and material"); - addProperty(P_THICKNESS, 0.0) - ->setLimits(RealLimits::lowerLimited(0.0)) - .setToolTip("Thickness of a layer in nanometers"); - - addProperty(P_NSLICES, 1) - ->setLimits(RealLimits::lowerLimited(0.0)) - .setToolTip(layer_nslices_tooltip); - + addProperty(P_THICKNESS, 0.0)->setLimits(RealLimits::lowerLimited(0.0)); + addProperty(P_NSLICES, 1)->setLimits(RealLimits::lowerLimited(0.0)); addProperty(P_COLOR, QColor()); // initially no color GroupInfo info; @@ -82,8 +71,6 @@ LayerItem::LayerItem() : SessionItem(M_TYPE), ItemWithMaterial(M_TYPE) registerTag(T_LAYOUTS, 0, -1, {ParticleLayoutItem::M_TYPE}); setDefaultTag(T_LAYOUTS); - - mapper()->setOnParentChange([this](SessionItem* new_parent) { updateAppearance(new_parent); }); } QString LayerItem::layerName() const @@ -149,7 +136,11 @@ void LayerItem::setRoughnessEnabled(bool enabled) UIntDescriptor LayerItem::numSlices() const { - return UIntDescriptor(getItem(P_NSLICES), Unit::unitless); + UIntDescriptor d(getItem(P_NSLICES), Unit::unitless); + d.tooltip = "Number of horizontal slices.\n" + "Used for Average Layer Material calculations \n" + "when corresponding simulation option is set."; + return d; } QVector<ParticleLayoutItem*> LayerItem::layouts() const @@ -176,14 +167,3 @@ void LayerItem::setColor(const QColor& color) { setItemValue(P_COLOR, color); } - -void LayerItem::updateAppearance(SessionItem* new_parent) -{ - if (!new_parent) { - if (parent() && parent()->hasModelType<MultiLayerItem>()) { - // we are about to be removed from MultiLayer - getItem(LayerItem::P_ROUGHNESS)->setEnabled(true); - getItem(LayerItem::P_THICKNESS)->setEnabled(true); - } - } -} diff --git a/GUI/Model/Sample/LayerItem.h b/GUI/Model/Sample/LayerItem.h index 66b6e35dd753c990f5665f0735db03963946e972..3fb6eae06495aa13cceb37cd452a637119b6f569 100644 --- a/GUI/Model/Sample/LayerItem.h +++ b/GUI/Model/Sample/LayerItem.h @@ -43,7 +43,6 @@ public: QVector<ItemWithMaterial*> itemsWithMaterial(); DoubleDescriptor thickness() const; - static bool isThicknessPropertyName(const QString& name); void setThicknessEnabled(bool enabled); template <typename T> T* setRoughnessType(); @@ -61,15 +60,16 @@ public: void setColor(const QColor& color); private: - void updateAppearance(SessionItem* new_parent); - // -- Only for testing friend class TestMultiLayerItem_twoLayerSystem_Test; friend class TestMultiLayerItem_threeLayerSystem_Test; friend class TestMultiLayerItem_movingMiddleLayerOnTop_Test; friend class TestModelUtils_iterateIf_Test; + friend class TestMapperForItem_onPropertyChange_Test; + friend class TestMapperForItem_Subscription_Test; SessionItem* thicknessItem() const; SessionItem* roughnessItem() const; + static bool isThicknessPropertyName(const QString& name); }; template <typename T> T* LayerItem::setRoughnessType() diff --git a/GUI/View/Main/ActionManager.cpp b/GUI/View/Main/ActionManager.cpp index 1fe45c741966fe5f45a9ee71c0e9fce58dc5bb31..2adf9d2fbdbbeddd7699348c4d19104c007e5d30 100644 --- a/GUI/View/Main/ActionManager.cpp +++ b/GUI/View/Main/ActionManager.cpp @@ -42,6 +42,7 @@ ActionManager::ActionManager(MainWindow* parent) , m_saveAsAction(nullptr) , m_exitAction(nullptr) , m_aboutAction(nullptr) + , m_importFromPythonAction(nullptr) , m_menuBar(nullptr) , m_fileMenu(nullptr) , m_settingsMenu(nullptr) @@ -70,6 +71,10 @@ void ActionManager::updateActionEnabling() m_saveAction->setEnabled(documentExists); m_saveAsAction->setEnabled(documentExists); m_closeProjectAction->setEnabled(documentExists); + m_importFromPythonAction->setEnabled(documentExists); + if (GUI::Util::OS::HostOsInfo::isMacHost()) + if (BaseUtils::System::getenv("PYTHONHOME").empty()) + m_importFromPythonAction->setEnabled(false); } void ActionManager::createActions() @@ -152,18 +157,18 @@ void ActionManager::createMenus() m_fileMenu->addSeparator(); m_importMenu = m_fileMenu->addMenu("&Import"); m_importMenu->setToolTipsVisible(true); - QAction* action = m_importMenu->addAction("&Import from Python script (experimental)"); - action->setToolTip("Import sample from Python script.\n The script should contain a function " - "returning a valid multi-layer."); + m_importFromPythonAction = m_importMenu->addAction("&Import from Python script (experimental)"); + m_importFromPythonAction->setToolTip( + "Import sample from Python script.\n The script should contain a function " + "returning a valid multi-layer."); #ifdef BORNAGAIN_PYTHON - connect(action, &QAction::triggered, this, &ActionManager::onImportFromPythonScript); + connect(m_importFromPythonAction, &QAction::triggered, this, + &ActionManager::onImportFromPythonScript); if (GUI::Util::OS::HostOsInfo::isMacHost()) if (BaseUtils::System::getenv("PYTHONHOME").empty()) - action->setEnabled(false); + m_importFromPythonAction->setEnabled(false); #endif // BORNAGAIN_PYTHON - if (!gSessionData->projectDocument) - action->setEnabled(false); m_fileMenu->addSeparator(); m_fileMenu->addAction(m_exitAction); diff --git a/GUI/View/Main/ActionManager.h b/GUI/View/Main/ActionManager.h index 5511848037ebf76713086c72b661c0950c305667..3b9982bc88601f8b540db2c8fc1b41842c2df11e 100644 --- a/GUI/View/Main/ActionManager.h +++ b/GUI/View/Main/ActionManager.h @@ -55,6 +55,7 @@ private: QAction* m_exitAction; QAction* m_webdocAction; QAction* m_aboutAction; + QAction* m_importFromPythonAction; QMenuBar* m_menuBar; QMenu* m_fileMenu; diff --git a/GUI/View/SampleDesigner/RealSpacePanel.cpp b/GUI/View/SampleDesigner/RealSpacePanel.cpp index 320d3a4643c1da9c68ca357b9be7d1f48dc79568..602572372cdbf07854fd7e8b1c6479887e1249f7 100644 --- a/GUI/View/SampleDesigner/RealSpacePanel.cpp +++ b/GUI/View/SampleDesigner/RealSpacePanel.cpp @@ -22,16 +22,15 @@ RealSpacePanel::RealSpacePanel(const MaterialModel* materialModel, SampleModel* : QWidget(parent), m_canvas(new RealSpaceCanvas(materialModel, sampleModel)) { setWindowTitle("Real Space"); - setObjectName("Sample3DPanel"); - m_toolbar = new StyledToolBar(this); + auto* toolbar = new StyledToolBar(this); const auto createAction = [&](const QString& text, const QString& tooltip) -> QAction* { auto* action = new QAction(text, this); action->setToolTip(tooltip); - if (!m_toolbar->actions().empty()) - m_toolbar->addSeparator(); - m_toolbar->addAction(action); + if (!toolbar->actions().empty()) + toolbar->addSeparator(); + toolbar->addAction(action); return action; }; @@ -53,14 +52,11 @@ RealSpacePanel::RealSpacePanel(const MaterialModel* materialModel, SampleModel* action = createAction("Reduce", "Decrease layer size"); connect(action, &QAction::triggered, [this]() { m_canvas->changeLayerSize(0.8); }); - m_lockViewAction = createAction("Lock View", "Lock/unlock current sample selection"); - m_lockViewAction->setCheckable(true); - auto* mainLayout = new QVBoxLayout(this); mainLayout->setMargin(0); mainLayout->setSpacing(0); mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->addWidget(m_toolbar); + mainLayout->addWidget(toolbar); mainLayout->addWidget(m_canvas); } @@ -73,19 +69,3 @@ RealSpaceCanvas* RealSpacePanel::canvas() { return m_canvas; } - -bool RealSpacePanel::isViewLocked() const -{ - return m_lockViewAction->isChecked(); -} - -void RealSpacePanel::setLockViewActionVisible(bool visible) -{ - QAction* separatorBeforeLockViewAction = nullptr; - if (auto idx = m_toolbar->actions().indexOf(m_lockViewAction); idx > 0) - separatorBeforeLockViewAction = m_toolbar->actions()[idx - 1]; - - m_lockViewAction->setVisible(visible); - if (separatorBeforeLockViewAction) - separatorBeforeLockViewAction->setVisible(visible); -} diff --git a/GUI/View/SampleDesigner/RealSpacePanel.h b/GUI/View/SampleDesigner/RealSpacePanel.h index 72a6c92016c5b1c5781c8897266ca56089886de7..7cedc7f00f7e0c0894a28376c1c4fda0c05b6818 100644 --- a/GUI/View/SampleDesigner/RealSpacePanel.h +++ b/GUI/View/SampleDesigner/RealSpacePanel.h @@ -35,13 +35,8 @@ public: QSize sizeHint() const override; RealSpaceCanvas* canvas(); - bool isViewLocked() const; - void setLockViewActionVisible(bool visible); - private: RealSpaceCanvas* m_canvas; - QAction* m_lockViewAction; - QToolBar* m_toolbar; }; #endif // BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_REALSPACEPANEL_H diff --git a/GUI/View/SampleDesigner/SampleView.cpp b/GUI/View/SampleDesigner/SampleView.cpp index e59ac2d3c097f6e5924bd3d347b3991fd2df8a5c..7e27f6f3ad144eb8f96a79869cbf618257119a17 100644 --- a/GUI/View/SampleDesigner/SampleView.cpp +++ b/GUI/View/SampleDesigner/SampleView.cpp @@ -63,7 +63,6 @@ SampleView::SampleView(QWidget* parent, ProjectDocument* document) auto* scriptPanel = new ScriptPanel(m_document->sampleModel(), this); m_realSpacePanel = new RealSpacePanel(m_document->materialModel(), m_document->sampleModel(), this); - m_realSpacePanel->setLockViewActionVisible(false); // lock not used in layer oriented editor sampleSelectionPane->setWindowTitle("Samples"); @@ -77,6 +76,9 @@ SampleView::SampleView(QWidget* parent, ProjectDocument* document) connect(sampleSelectionView, &SampleListView::currentSampleChanged, m_realSpacePanel->canvas(), &RealSpaceCanvas::setCurrentItem); + connect(sampleSelectionView, &SampleListView::currentSampleChanged, scriptPanel, + &ScriptPanel::setCurrentSample); + connect(editor, &LayerOrientedSampleEditor::requestViewInRealSpace, this, &SampleView::onRequestViewInRealSpace); diff --git a/GUI/View/SampleDesigner/ScriptPanel.cpp b/GUI/View/SampleDesigner/ScriptPanel.cpp index 427eb3e3ae3f84681f5a972896acdc8cc6282338..8e3af3eb6b24a8724317f8566db280710dbb315b 100644 --- a/GUI/View/SampleDesigner/ScriptPanel.cpp +++ b/GUI/View/SampleDesigner/ScriptPanel.cpp @@ -40,6 +40,7 @@ ScriptPanel::ScriptPanel(SampleModel* sampleModel, QWidget* parent) , m_highlighter(nullptr) , m_updateTimer(new UpdateTimer(accumulateUpdatesDuringMsec, this)) , m_cautionSign(new CautionSign(m_textEdit)) + , m_currentMultiLayerItem(nullptr) { setWindowTitle("Python Script"); setObjectName("ScriptPanel"); @@ -56,6 +57,12 @@ ScriptPanel::ScriptPanel(SampleModel* sampleModel, QWidget* parent) m_textEdit->setFontPointSize(DesignerHelper::getPythonEditorFontSize()); } +void ScriptPanel::setCurrentSample(MultiLayerItem* multiLayerItem) +{ + m_currentMultiLayerItem = multiLayerItem; + updateEditor(); +} + void ScriptPanel::updateEditor() { if (!m_highlighter) { @@ -109,23 +116,23 @@ void ScriptPanel::hideEvent(QHideEvent*) QString ScriptPanel::generateCodeSnippet() { m_cautionSign->clear(); - QString result; - for (const MultiLayerItem* sampleItem : m_sampleModel->multiLayerItems()) { - try { - auto multilayer = GUI::Model::DomainObjectBuilder::buildMultiLayer(*sampleItem); - if (!result.isEmpty()) - result.append("\n"); - result.append(QString::fromStdString(Py::Export::sampleCode(*multilayer))); - } catch (const std::exception& ex) { - QString message = - QString("Generation of Python Script failed. Code is not complete.\n\n" - "It can happen if sample requires further assembling or some of sample " - "parameters are not valid. See details below.\n\n%1") - .arg(QString::fromStdString(ex.what())); - - m_cautionSign->setCautionMessage(message); - } + if (m_currentMultiLayerItem == nullptr) + return {}; + + QString result; + try { + auto multilayer = + GUI::Model::DomainObjectBuilder::buildMultiLayer(*m_currentMultiLayerItem); + result.append(QString::fromStdString(Py::Export::sampleCode(*multilayer))); + } catch (const std::exception& ex) { + QString message = + QString("Generation of Python Script failed. Code is not complete.\n\n" + "It can happen if sample requires further assembling or some of sample " + "parameters are not valid. See details below.\n\n%1") + .arg(QString::fromStdString(ex.what())); + + m_cautionSign->setCautionMessage(message); } return result; diff --git a/GUI/View/SampleDesigner/ScriptPanel.h b/GUI/View/SampleDesigner/ScriptPanel.h index bc8bd6c6c1a7b8a1fb1048c9d00168aa2fa292dc..dff8439d7f0f2a9b024e482f8435e02a6716f46e 100644 --- a/GUI/View/SampleDesigner/ScriptPanel.h +++ b/GUI/View/SampleDesigner/ScriptPanel.h @@ -25,6 +25,7 @@ class QShowEvent; class QHideEvent; class QTextEdit; class QModelIndex; +class MultiLayerItem; //! Resides at the bottom of SampleView and displays a Python script. @@ -34,6 +35,8 @@ class ScriptPanel : public QWidget { public: explicit ScriptPanel(SampleModel* sampleModel, QWidget* parent); + void setCurrentSample(MultiLayerItem* multiLayerItem); + protected: void showEvent(QShowEvent*) override; void hideEvent(QHideEvent*) override; @@ -52,6 +55,7 @@ private: PythonSyntaxHighlighter* m_highlighter; UpdateTimer* m_updateTimer; CautionSign* m_cautionSign; + MultiLayerItem* m_currentMultiLayerItem; }; #endif // BORNAGAIN_GUI_VIEW_SAMPLEDESIGNER_SCRIPTPANEL_H diff --git a/Tests/Unit/GUI/TestMapperForItem.cpp b/Tests/Unit/GUI/TestMapperForItem.cpp index ece294796a9eb669c9d6c09d4511dab227d5687d..e5178cd382c8a17f66b30c15e4d676fabaf457d1 100644 --- a/Tests/Unit/GUI/TestMapperForItem.cpp +++ b/Tests/Unit/GUI/TestMapperForItem.cpp @@ -176,24 +176,6 @@ TEST_F(TestMapperForItem, onPropertyChange) && MultiLayerItem::isCrossCorrLengthPropertyName(w.m_reported_names[0])); } -TEST_F(TestMapperForItem, onParentChange) -{ - Widget w; - SampleModel model; - auto* multilayer = model.insertItem<MultiLayerItem>(); - auto* layer = model.insertItem<LayerItem>(multilayer); - - // Mapper is looking on child; changing child's parent - setItem(layer, &w); - EXPECT_TRUE(m_mapped_item == layer); - delete multilayer->takeRow(ParentRow(*layer)); - - EXPECT_EQ(w.m_onPropertyChangeCount, 2); // Layer updates its property appearance, see LayerItem - EXPECT_EQ(w.m_onChildPropertyChangeCount, 0); - EXPECT_EQ(w.m_onParentChangeCount, 1); - EXPECT_EQ(w.m_onChildrenChangeCount, 0); -} - TEST_F(TestMapperForItem, onChildrenChange) { Widget w; diff --git a/Tests/Unit/GUI/TestParaCrystalItems.cpp b/Tests/Unit/GUI/TestParaCrystalItems.cpp index 3381ed241e5b8ec137e18753070548a985644757..b6e50c234cd1e59451383d86cf858a10f58d9202 100644 --- a/Tests/Unit/GUI/TestParaCrystalItems.cpp +++ b/Tests/Unit/GUI/TestParaCrystalItems.cpp @@ -69,27 +69,3 @@ TEST_F(TestParaCrystalItems, Para2DFromToDomain) EXPECT_EQ(domain->lattice().latticeAngle(), orig.lattice().latticeAngle()); EXPECT_EQ(domain->lattice().rotationAngle(), orig.lattice().rotationAngle()); } - -TEST_F(TestParaCrystalItems, Inference2DRotationAngleToggle) -{ - SampleModel model; - auto* multilayer = model.insertItem<MultiLayerItem>(); - auto* layer = multilayer->addLayer(); - auto* layout = model.insertItem<ParticleLayoutItem>(layer); - - auto* interference = layout->createInterference<Interference2DParaCrystalItem>(); - - // rotation (xi) should be disabled if integration is on - interference->setXiIntegration(true); - - SessionItem* angleItem = interference->latticeType().currentItem()->latticeRotationAngleItem(); - - EXPECT_FALSE(angleItem->isEnabled()); - - // rotation (xi) should be enabled if integration is off - interference->setXiIntegration(false); - - angleItem = interference->latticeType().currentItem()->latticeRotationAngleItem(); - - EXPECT_TRUE(angleItem->isEnabled()); -}