diff --git a/GUI/Models/ModelUtils.h b/GUI/Models/ModelUtils.h index c4ba8d1280b2b35c10efb01065247ae2a843efb8..41c79e93cb4eb6232455d31028b2bd1e4d1ad3cf 100644 --- a/GUI/Models/ModelUtils.h +++ b/GUI/Models/ModelUtils.h @@ -16,6 +16,7 @@ #define BORNAGAIN_GUI_MODELS_MODELUTILS_H #include <QString> +#include <QStringList> #include <functional> class QModelIndex; @@ -27,6 +28,9 @@ namespace GUI::Model::ItemUtils { //! Returns list of top item names. QStringList topItemNames(SessionModel* model, const QString& modelType = ""); +//! Returns list of names of given items. +template <typename T> QStringList itemNames(const QVector<T*> items); + //! Iterates through all model indices and calls user function. void iterate(const QModelIndex& index, const QAbstractItemModel* model, const std::function<void(const QModelIndex&)>& fun); @@ -36,6 +40,17 @@ void iterate(const QModelIndex& index, const QAbstractItemModel* model, void iterate_if(const QModelIndex& index, const QAbstractItemModel* model, const std::function<bool(const QModelIndex&)>& fun); + +template <typename T> QStringList itemNames(const QVector<T*> items) +{ + QStringList result; + + for (auto item : items) + result.append(item->itemName()); + + return result; +} + } // namespace GUI::Model::ItemUtils #endif // BORNAGAIN_GUI_MODELS_MODELUTILS_H diff --git a/GUI/Models/SimulationOptionsItem.cpp b/GUI/Models/SimulationOptionsItem.cpp index 40b3f9960f733f8efa9310b63376b30740a8ae55..07142970ac6e0c0df0cc825c54ffc0917ccfbe3d 100644 --- a/GUI/Models/SimulationOptionsItem.cpp +++ b/GUI/Models/SimulationOptionsItem.cpp @@ -155,6 +155,11 @@ bool SimulationOptionsItem::useMonteCarloIntegration() const return getComputationMethod() == MonteCarlo; } +bool SimulationOptionsItem::useAnalytical() const +{ + return getComputationMethod() == Analytical; +} + void SimulationOptionsItem::setComputationMethod(const QString& name) { ComboProperty combo = getItemValue(P_COMPUTATION_METHOD).value<ComboProperty>(); diff --git a/GUI/Models/SimulationOptionsItem.h b/GUI/Models/SimulationOptionsItem.h index 642c28863cc341392152b7a790c8877a5e3f25fd..9c2e88cc43d022082ca7f97ac172350fe3ce2540 100644 --- a/GUI/Models/SimulationOptionsItem.h +++ b/GUI/Models/SimulationOptionsItem.h @@ -46,6 +46,7 @@ public: void setUseMonteCarloIntegration(int numberOfPoints); void setUseAnalytical(); bool useMonteCarloIntegration() const; + bool useAnalytical() const; void setNumberOfMonteCarloPoints(int npoints); int numberOfMonteCarloPoints() const; diff --git a/GUI/Views/SimulationView.cpp b/GUI/Views/SimulationView.cpp index 673a5af5559c2b15ca7d79b75c55060958154757..d537081d3773a9e4ff340cd8ea384a3042be2b35 100644 --- a/GUI/Views/SimulationView.cpp +++ b/GUI/Views/SimulationView.cpp @@ -7,45 +7,198 @@ //! //! @homepage http://www.bornagainproject.org //! @license GNU General Public License v3 or higher (see COPYING) -//! @copyright Forschungszentrum Jülich GmbH 2018 +//! @copyright Forschungszentrum Jülich GmbH 2021 //! @authors Scientific Computing Group at MLZ (see CITATION, AUTHORS) // // ************************************************************************************************ #include "GUI/Views/SimulationView.h" -#include "GUI/Views/CommonWidgets/StyledToolBar.h" -#include "GUI/Views/SimulationWidgets/SimulationSetupWidget.h" +#include "GUI/Models/ApplicationModels.h" +#include "GUI/Models/DocumentModel.h" +#include "GUI/Models/InstrumentItems.h" +#include "GUI/Models/JobItem.h" +#include "GUI/Models/JobModel.h" +#include "GUI/Models/ModelUtils.h" +#include "GUI/Models/MultiLayerItem.h" +#include "GUI/Models/RealDataItem.h" +#include "GUI/Models/RealDataModel.h" +#include "GUI/Models/SampleModel.h" +#include "GUI/Models/SampleValidator.h" +#include "GUI/Models/SimulationOptionsItem.h" +#include "GUI/Views/SimulationWidgets/PythonScriptWidget.h" +#include "GUI/mainwindow/ProjectManagerStore.h" #include "GUI/mainwindow/mainwindow.h" -#include <QVBoxLayout> +#include "GUI/mainwindow/projectmanager.h" +#include "ui_SimulationView.h" +#include <QMessageBox> +#include <thread> SimulationView::SimulationView(MainWindow* mainWindow) - : QWidget(mainWindow) - , m_simulationSetupWidget(new SimulationSetupWidget) - , m_toolBar(new StyledToolBar) + : QWidget(mainWindow), m_ui(new Ui::SimulationView) { - m_toolBar->setFixedHeight(m_toolBar->minimumHeight()); - m_simulationSetupWidget->setApplicationModels(mainWindow->models()); + m_ui->setupUi(this); - auto* mainLayout = new QVBoxLayout; - mainLayout->setSizeConstraint(QLayout::SetNoConstraint); - mainLayout->addWidget(m_toolBar); - mainLayout->addWidget(m_simulationSetupWidget); - mainLayout->setMargin(0); - mainLayout->setSpacing(0); - setLayout(mainLayout); + // -- fill combo for "number of threads" + const int nthreads = static_cast<int>(std::thread::hardware_concurrency()); + m_ui->numberOfThreadsCombo->addItem(QString("Max (%1 threads)").arg(nthreads), nthreads); + for (int i = nthreads - 1; i > 1; i--) + m_ui->numberOfThreadsCombo->addItem(QString("%1 threads").arg(i), i); + m_ui->numberOfThreadsCombo->addItem("1 thread", 1); + + connect(m_ui->runSimulationButton, &QPushButton::clicked, this, &SimulationView::runSimulation); + connect(m_ui->exportToPyScriptButton, &QPushButton::clicked, this, + &SimulationView::exportPythonScript); + connect(m_ui->computationMethodCombo, QOverload<int>::of(&QComboBox::currentIndexChanged), this, + &SimulationView::updateEnabling); } -void SimulationView::onRunSimulationShortcut() +void SimulationView::showEvent(QShowEvent*) { - m_simulationSetupWidget->onRunSimulation(); + // -- selection group + using GUI::Model::ItemUtils::itemNames; + updateSelection(m_ui->instrumentCombo, itemNames(instrumentItems())); + updateSelection(m_ui->sampleCombo, itemNames(multiLayerItems())); + updateSelection(m_ui->realDataCombo, itemNames(realDataItems()), true); + + // -- options group + m_ui->runPolicyCombo->setCurrentIndex(optionsItem()->runImmediately() ? 0 : 1); + m_ui->numberOfThreadsCombo->setCurrentIndex( + m_ui->numberOfThreadsCombo->findData(optionsItem()->numberOfThreads())); + m_ui->computationMethodCombo->setCurrentIndex(optionsItem()->useAnalytical() ? 0 : 1); + m_ui->numberOfMonteCarloPoints->setValue(optionsItem()->numberOfMonteCarloPoints()); + m_ui->materialCombo->setCurrentIndex(optionsItem()->useAverageMaterials() ? 1 : 0); + m_ui->includeSpecularCombo->setCurrentIndex(optionsItem()->includeSpecularPeak() ? 1 : 0); + + updateEnabling(); } -void SimulationView::showEvent(QShowEvent*) +void SimulationView::hideEvent(QHideEvent*) +{ + // Storing the options has do be done here because on a showEvent the values are set again from + // the SimulationOptionsItem (=> keep values when switching views) + readOptionsFromUI(); +} + +void SimulationView::runSimulation() +{ + readOptionsFromUI(); + if (const QString msg = validateSimulationSetup(true); !msg.isEmpty()) { + QMessageBox::warning(this, "Can't run the job", + "Can't run the job with current settings\n\n" + msg); + return; + } + JobModel* jobModel = MainWindow::instance()->models()->jobModel(); + JobItem* jobItem = + jobModel->addJob(selectedSample(), selectedInstrument(), selectedRealData(), optionsItem()); + jobModel->runJob(jobItem->index()); +} + +void SimulationView::exportPythonScript() +{ + readOptionsFromUI(); + if (const QString msg = validateSimulationSetup(false); !msg.isEmpty()) { + QMessageBox::warning(this, "Can't export to Python", + "Can't export to Python with current settings\n\n" + msg); + return; + } + PythonScriptWidget* pythonWidget = new PythonScriptWidget(this); + pythonWidget->show(); + pythonWidget->raise(); + pythonWidget->generatePythonScript(selectedSample(), selectedInstrument(), optionsItem(), + GUI::Project::Manager::projectManager()->projectDir()); +} + +void SimulationView::readOptionsFromUI() +{ + optionsItem()->setRunImmediately(m_ui->runPolicyCombo->currentIndex() == 0); + optionsItem()->setNumberOfThreads(m_ui->numberOfThreadsCombo->currentData().toInt()); + if (m_ui->computationMethodCombo->currentIndex() == 0) + optionsItem()->setUseAnalytical(); + else + optionsItem()->setUseMonteCarloIntegration(m_ui->numberOfMonteCarloPoints->value()); + optionsItem()->setUseAverageMaterials(m_ui->materialCombo->currentIndex() == 1); + optionsItem()->setIncludeSpecularPeak(m_ui->includeSpecularCombo->currentIndex() == 1); +} + +void SimulationView::updateEnabling() +{ + m_ui->numberOfMonteCarloPoints->setEnabled(m_ui->computationMethodCombo->currentIndex() == 1); +} + +void SimulationView::updateSelection(QComboBox* comboBox, QStringList itemList, bool allowNone) +{ + const QString previousItem = comboBox->currentText(); + + comboBox->clear(); + comboBox->setEnabled(!itemList.isEmpty()); + + if (itemList.isEmpty()) { + comboBox->addItem("Not yet defined"); + return; + } + + if (allowNone) + itemList.insert(-1, "None"); + comboBox->addItems(itemList); + if (itemList.contains(previousItem)) + comboBox->setCurrentIndex(itemList.indexOf(previousItem)); +} + +QString SimulationView::validateSimulationSetup(bool validateRealData) const +{ + QString messages; + const auto append = [&](const QString& m) { messages.append("- " + m + "\n"); }; + + if (!selectedSample()) + append("No sample selected"); + else { + SampleValidator sampleValidator; + if (!sampleValidator.isValidMultiLayer(selectedSample())) + append(sampleValidator.getValidationMessage()); + } + + if (!selectedInstrument()) + append("No instrument selected"); + + if (validateRealData && selectedRealData() && selectedInstrument() + && !selectedInstrument()->alignedWith(selectedRealData())) + append("The experimental data does not fit in the selected instrument. Try linking " + "them in Import Tab."); + + return messages; +} + +QVector<MultiLayerItem*> SimulationView::multiLayerItems() const +{ + return MainWindow::instance()->models()->sampleModel()->topItems<MultiLayerItem>(); +} + +QVector<InstrumentItem*> SimulationView::instrumentItems() const +{ + return MainWindow::instance()->models()->instrumentModel()->instrumentItems(); +} + +QVector<RealDataItem*> SimulationView::realDataItems() const +{ + return MainWindow::instance()->models()->realDataModel()->realDataItems(); +} + +SimulationOptionsItem* SimulationView::optionsItem() const +{ + return MainWindow::instance()->models()->documentModel()->simulationOptionsItem(); +} + +const MultiLayerItem* SimulationView::selectedSample() const +{ + return multiLayerItems().value(m_ui->sampleCombo->currentIndex(), nullptr); +} + +const InstrumentItem* SimulationView::selectedInstrument() const { - updateSimulationViewElements(); + return instrumentItems().value(m_ui->instrumentCombo->currentIndex(), nullptr); } -void SimulationView::updateSimulationViewElements() +const RealDataItem* SimulationView::selectedRealData() const { - m_simulationSetupWidget->updateViewElements(); + return realDataItems().value(m_ui->realDataCombo->currentIndex() - 1, nullptr); // -1: "None" } diff --git a/GUI/Views/SimulationView.h b/GUI/Views/SimulationView.h index 1c9f00981db2d9126cd9c4b611c5572c859134ac..3de160d8eebdc6e3f0bf4761d997663633dea2b5 100644 --- a/GUI/Views/SimulationView.h +++ b/GUI/Views/SimulationView.h @@ -18,26 +18,69 @@ #include <QWidget> class MainWindow; -class SimulationSetupWidget; -class StyledToolBar; +class MultiLayerItem; +class InstrumentItem; +class RealDataItem; +class QComboBox; +class SimulationOptionsItem; +namespace Ui { +class SimulationView; +} + +//! Widget to define a simulation. +//! Contains: +//! * Elements to select instrument, sample and real data. +//! * Elements to set options like number of threads. +//! * Buttons to run simulation or to export as a Python script class SimulationView : public QWidget { Q_OBJECT public: SimulationView(MainWindow* mainWindow); -protected: - void showEvent(QShowEvent*); + void runSimulation(); + void exportPythonScript(); -public slots: - void onRunSimulationShortcut(); +protected: + virtual void showEvent(QShowEvent*) override; + virtual void hideEvent(QHideEvent*) override; private: - void updateSimulationViewElements(); + //! Read the entries in the UI and store in OptionsItem. + void readOptionsFromUI(); + + //! Update enabling of elements depending on other elements. + void updateEnabling(); + + //! Returns selected sample (MultiLayerItem) taking into account that there might be several + //! samples with the same name. + const MultiLayerItem* selectedSample() const; + + //! Returns selected InstrumentItem taking into account that there might be several + //! instruments with the same name. + const InstrumentItem* selectedInstrument() const; - SimulationSetupWidget* m_simulationSetupWidget; - StyledToolBar* m_toolBar; + //! Returns selected real data item taking into account that there might be several + //! items with the same name. + const RealDataItem* selectedRealData() const; + + //! Updates selection combo with string list while preserving previous selection. + //! If allowNone == true, additional "None" item will be added to the combo. + void updateSelection(QComboBox* comboBox, QStringList itemList, bool allowNone = false); + + //! Checks whether selection is correct for running a simulation. + //! Returns empty string if valid, otherwise the error text. + QString validateSimulationSetup(bool validateRealData) const; + + // Convenience methods for easier access + QVector<MultiLayerItem*> multiLayerItems() const; + QVector<InstrumentItem*> instrumentItems() const; + QVector<RealDataItem*> realDataItems() const; + SimulationOptionsItem* optionsItem() const; + +private: + Ui::SimulationView* m_ui; }; #endif // BORNAGAIN_GUI_VIEWS_SIMULATIONVIEW_H diff --git a/GUI/Views/SimulationView.ui b/GUI/Views/SimulationView.ui new file mode 100644 index 0000000000000000000000000000000000000000..fabf9505e84962c85e3269a98deac693f5016de1 --- /dev/null +++ b/GUI/Views/SimulationView.ui @@ -0,0 +1,298 @@ +<?xml version="1.0" encoding="UTF-8"?> +<ui version="4.0"> + <class>SimulationView</class> + <widget class="QWidget" name="SimulationView"> + <property name="geometry"> + <rect> + <x>0</x> + <y>0</y> + <width>1239</width> + <height>835</height> + </rect> + </property> + <property name="windowTitle"> + <string>Form</string> + </property> + <layout class="QVBoxLayout" name="mainLayout"> + <property name="spacing"> + <number>15</number> + </property> + <property name="sizeConstraint"> + <enum>QLayout::SetNoConstraint</enum> + </property> + <property name="leftMargin"> + <number>9</number> + </property> + <property name="topMargin"> + <number>9</number> + </property> + <property name="rightMargin"> + <number>9</number> + </property> + <property name="bottomMargin"> + <number>9</number> + </property> + <item> + <widget class="QGroupBox" name="groupBox"> + <property name="title"> + <string>Data selection</string> + </property> + <layout class="QFormLayout" name="formLayout_2"> + <item row="0" column="0"> + <widget class="QLabel" name="label"> + <property name="text"> + <string>Instrument:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="instrumentCombo"> + <property name="toolTip"> + <string>Select Instrument to simulate from those defined in Instrument View</string> + </property> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_8"> + <property name="text"> + <string>Sample:</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_9"> + <property name="text"> + <string>Real data:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="sampleCombo"> + <property name="toolTip"> + <string>Select Sample to simulate from those defined in Sample View</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="realDataCombo"> + <property name="toolTip"> + <string><html><head/><body><p>Select real data to use during the fitting.</p><p>If None is selected, the standard simulation will be run.</p></body></html></string> + </property> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <widget class="QGroupBox" name="groupBox_2"> + <property name="title"> + <string>Simulation parameters</string> + </property> + <layout class="QFormLayout" name="formLayout"> + <item row="0" column="0"> + <widget class="QLabel" name="label_2"> + <property name="text"> + <string>Run policy:</string> + </property> + </widget> + </item> + <item row="0" column="1"> + <widget class="QComboBox" name="runPolicyCombo"> + <property name="toolTip"> + <string>Defines run policy for the simulation</string> + </property> + <item> + <property name="text"> + <string>Immediately</string> + </property> + </item> + <item> + <property name="text"> + <string>In background</string> + </property> + </item> + </widget> + </item> + <item row="1" column="0"> + <widget class="QLabel" name="label_3"> + <property name="text"> + <string>Number of Threads:</string> + </property> + </widget> + </item> + <item row="1" column="1"> + <widget class="QComboBox" name="numberOfThreadsCombo"> + <property name="toolTip"> + <string>Defines number of threads to use for the simulation.</string> + </property> + </widget> + </item> + <item row="2" column="0"> + <widget class="QLabel" name="label_4"> + <property name="text"> + <string>Computation method:</string> + </property> + </widget> + </item> + <item row="2" column="1"> + <widget class="QComboBox" name="computationMethodCombo"> + <property name="toolTip"> + <string>Defines computation method (analytical or Monte-Carlo integration)</string> + </property> + <item> + <property name="text"> + <string>Analytical</string> + </property> + </item> + <item> + <property name="text"> + <string>Monte-Carlo Integration</string> + </property> + </item> + </widget> + </item> + <item row="3" column="0"> + <widget class="QLabel" name="label_5"> + <property name="text"> + <string>Number of MC points:</string> + </property> + </widget> + </item> + <item row="3" column="1"> + <widget class="QSpinBox" name="numberOfMonteCarloPoints"> + <property name="maximum"> + <number>10000</number> + </property> + </widget> + </item> + <item row="4" column="0"> + <widget class="QLabel" name="label_6"> + <property name="text"> + <string>Material for Fresnel calculations:</string> + </property> + </widget> + </item> + <item row="4" column="1"> + <widget class="QComboBox" name="materialCombo"> + <property name="toolTip"> + <string><html><head/><body><p>Define if the material used for Fresnel calculations should be the ambient layer material or the average material of the layer and the particles it contains.</p></body></html></string> + </property> + <item> + <property name="text"> + <string>Ambient Layer Material</string> + </property> + </item> + <item> + <property name="text"> + <string>Average Layer Material</string> + </property> + </item> + </widget> + </item> + <item row="5" column="0"> + <widget class="QLabel" name="label_7"> + <property name="text"> + <string>Include specular peak:</string> + </property> + </widget> + </item> + <item row="5" column="1"> + <widget class="QComboBox" name="includeSpecularCombo"> + <property name="toolTip"> + <string>Defines if the specular peak should be included in the simulation result</string> + </property> + <item> + <property name="text"> + <string>No</string> + </property> + </item> + <item> + <property name="text"> + <string>Yes</string> + </property> + </item> + </widget> + </item> + </layout> + </widget> + </item> + <item> + <layout class="QHBoxLayout" name="horizontalLayout"> + <item> + <spacer name="horizontalSpacer"> + <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> + <widget class="QPushButton" name="runSimulationButton"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>50</height> + </size> + </property> + <property name="toolTip"> + <string><html><head/><body><p>Run the simulation using settings above.</p><p>Global shortcut ctrl-r can be used to run from sample view.</p></body></html></string> + </property> + <property name="text"> + <string>Run simulation</string> + </property> + </widget> + </item> + <item> + <widget class="QPushButton" name="exportToPyScriptButton"> + <property name="minimumSize"> + <size> + <width>100</width> + <height>50</height> + </size> + </property> + <property name="toolTip"> + <string>Export the simulation using settings above to a python script.</string> + </property> + <property name="text"> + <string>Export to Python Script</string> + </property> + </widget> + </item> + <item> + <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> + </layout> + </item> + <item> + <spacer name="verticalSpacer"> + <property name="orientation"> + <enum>Qt::Vertical</enum> + </property> + <property name="sizeHint" stdset="0"> + <size> + <width>20</width> + <height>488</height> + </size> + </property> + </spacer> + </item> + </layout> + </widget> + <resources/> + <connections/> +</ui> diff --git a/GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.cpp b/GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.cpp deleted file mode 100644 index 20d5b6e5a938efe16f5c80e720484b7442fa8f94..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.cpp +++ /dev/null @@ -1,169 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.cpp -//! @brief Implements class SimulationDataSelectorWidget -//! -//! @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 "GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.h" -#include "GUI/Models/ApplicationModels.h" -#include "GUI/Models/InstrumentItems.h" -#include "GUI/Models/ModelUtils.h" -#include "GUI/Models/MultiLayerItem.h" -#include "GUI/Models/RealDataModel.h" -#include "GUI/Models/SampleModel.h" -#include <QComboBox> -#include <QFileDialog> -#include <QGroupBox> -#include <QLabel> -#include <QVBoxLayout> - -namespace { -const QString select_instrument_tooltip = - "Select Instrument to simulate from those defined in Instrument View"; -const QString select_sample_tooltip = "Select Sample to simulate from those defined in Sample View"; -const QString select_realdata_tooltip = "Select real data to use during the fitting. \n" - "If None is selected, the standard simulation will be run."; -} // namespace - -SimulationDataSelectorWidget::SimulationDataSelectorWidget(QWidget* parent) - : QWidget(parent) - , m_instrumentCombo(new QComboBox) - , m_sampleCombo(new QComboBox) - , m_realDataCombo(new QComboBox) - , m_applicationModels(0) -{ - QVBoxLayout* mainLayout = new QVBoxLayout; - mainLayout->setMargin(0); - mainLayout->setSpacing(0); - - // selection of input parameters - QGroupBox* groupBox = new QGroupBox("Data selection"); - - QLabel* instrumentSelectionLabel = new QLabel("Instrument:"); - instrumentSelectionLabel->setToolTip(select_instrument_tooltip); - m_instrumentCombo->setToolTip(select_instrument_tooltip); - m_instrumentCombo->setAttribute(Qt::WA_MacShowFocusRect, false); - m_instrumentCombo->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::Fixed); - - QLabel* sampleSelectionLabel = new QLabel("Sample:"); - sampleSelectionLabel->setToolTip(select_sample_tooltip); - m_sampleCombo->setToolTip(select_sample_tooltip); - - QLabel* readDataSelectionLabel = new QLabel("Real Data:"); - readDataSelectionLabel->setToolTip(select_realdata_tooltip); - m_realDataCombo->setToolTip(select_realdata_tooltip); - - // layout - QGridLayout* dataSelectionLayout = new QGridLayout; - dataSelectionLayout->setMargin(12); // to match margin in SimulationOptionsWidget - - dataSelectionLayout->addWidget(instrumentSelectionLabel, 0, 0); - dataSelectionLayout->addWidget(m_instrumentCombo, 0, 1); - dataSelectionLayout->addWidget(sampleSelectionLabel, 1, 0); - dataSelectionLayout->addWidget(m_sampleCombo, 1, 1); - dataSelectionLayout->addWidget(readDataSelectionLabel, 2, 0); - dataSelectionLayout->addWidget(m_realDataCombo, 2, 1); - groupBox->setLayout(dataSelectionLayout); - - mainLayout->addWidget(groupBox); - setLayout(mainLayout); -} - -void SimulationDataSelectorWidget::setApplicationModels(ApplicationModels* applicationModels) -{ - m_applicationModels = applicationModels; - updateViewElements(); -} - -//! Returns selected MultiLayerItem taking into account that there might be several -//! multilayers with same name. - -const MultiLayerItem* SimulationDataSelectorWidget::selectedMultiLayerItem() const -{ - auto items = m_applicationModels->sampleModel()->topItems<MultiLayerItem>(); - if (items.isEmpty()) - return nullptr; - return dynamic_cast<const MultiLayerItem*>(items.at(selectedSampleIndex())); -} - -//! Returns selected InstrumentItem taking into account that there might be several -//! instruments with same name. - -const InstrumentItem* SimulationDataSelectorWidget::selectedInstrumentItem() const -{ - auto items = m_applicationModels->instrumentModel()->topItems<InstrumentItem>(); - return items.isEmpty() ? nullptr : items.at(selectedInstrumentIndex()); -} - -//! Returns selected real data item taking into account that there might be several -//! items with same name. - -const RealDataItem* SimulationDataSelectorWidget::selectedRealDataItem() const -{ - auto items = m_applicationModels->realDataModel()->realDataItems(); - if (items.isEmpty()) - return nullptr; - if (selectedRealDataIndex() >= 0 && selectedRealDataIndex() < items.size()) - return items.at(selectedRealDataIndex()); - - return nullptr; -} - -void SimulationDataSelectorWidget::updateViewElements() -{ - ASSERT(m_applicationModels); - updateSelection(m_instrumentCombo, - GUI::Model::ItemUtils::topItemNames(m_applicationModels->instrumentModel())); - updateSelection(m_sampleCombo, GUI::Model::ItemUtils::topItemNames( - m_applicationModels->sampleModel(), MultiLayerItem::M_TYPE)); - updateSelection(m_realDataCombo, - GUI::Model::ItemUtils::topItemNames(m_applicationModels->realDataModel()), - true); -} - -int SimulationDataSelectorWidget::selectedInstrumentIndex() const -{ - return m_instrumentCombo->currentIndex(); -} - -int SimulationDataSelectorWidget::selectedSampleIndex() const -{ - return m_sampleCombo->currentIndex(); -} - -int SimulationDataSelectorWidget::selectedRealDataIndex() const -{ - // -1 because m_realDataCombo always contains "None" as a first entry - return m_realDataCombo->currentIndex() - 1; -} - -//! Updates selection combo with string list while preserving previous selection. -//! If allow_none == true, additional "None" item will be added to the combo. - -void SimulationDataSelectorWidget::updateSelection(QComboBox* comboBox, QStringList itemList, - bool allow_none) -{ - QString previousItem = comboBox->currentText(); - - comboBox->clear(); - if (itemList.isEmpty()) { - comboBox->setEnabled(false); - comboBox->addItem("Not yet defined"); - } else { - comboBox->setEnabled(true); - // qSort(itemList.begin(), itemList.end()); // uncomment, if we want alphabetical order - if (allow_none) - itemList.insert(-1, "None"); - comboBox->addItems(itemList); - if (itemList.contains(previousItem)) - comboBox->setCurrentIndex(itemList.indexOf(previousItem)); - } -} diff --git a/GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.h b/GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.h deleted file mode 100644 index f70bca270613411065808448239dbda1a3bcd5ea..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.h +++ /dev/null @@ -1,55 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.h -//! @brief Defines class SimulationDataSelectorWidget -//! -//! @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 BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONDATASELECTORWIDGET_H -#define BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONDATASELECTORWIDGET_H - -#include <QWidget> - -class ApplicationModels; -class QComboBox; -class MultiLayerItem; -class InstrumentItem; -class RealDataItem; - -//! The SimulationDataSelectorWidget class represents widget to select instrument, sample and -//! real data. Located at the top of SimulationView. - -class SimulationDataSelectorWidget : public QWidget { - Q_OBJECT - -public: - SimulationDataSelectorWidget(QWidget* parent = 0); - - void setApplicationModels(ApplicationModels* applicationModels); - - const MultiLayerItem* selectedMultiLayerItem() const; - const InstrumentItem* selectedInstrumentItem() const; - const RealDataItem* selectedRealDataItem() const; - - void updateViewElements(); - -private: - int selectedInstrumentIndex() const; - int selectedSampleIndex() const; - int selectedRealDataIndex() const; - void updateSelection(QComboBox* comboBox, QStringList itemList, bool allow_none = false); - - QComboBox* m_instrumentCombo; - QComboBox* m_sampleCombo; - QComboBox* m_realDataCombo; - ApplicationModels* m_applicationModels; -}; - -#endif // BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONDATASELECTORWIDGET_H diff --git a/GUI/Views/SimulationWidgets/SimulationOptionsWidget.cpp b/GUI/Views/SimulationWidgets/SimulationOptionsWidget.cpp deleted file mode 100644 index 45609f12fce57453d2f06387202a584bcbeeba83..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationOptionsWidget.cpp +++ /dev/null @@ -1,42 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationOptionsWidget.cpp -//! @brief Implements class SimulationOptionsWidget -//! -//! @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 "GUI/Views/SimulationWidgets/SimulationOptionsWidget.h" -#include "GUI/Models/SimulationOptionsItem.h" -#include "GUI/Views/PropertyEditor/ComponentFlatView.h" -#include <QGroupBox> -#include <QVBoxLayout> - -SimulationOptionsWidget::SimulationOptionsWidget(QWidget* parent) - : QWidget(parent), m_boxEditor(new ComponentFlatView) -{ - auto groupBox = new QGroupBox("Simulation parameters"); - - auto groupLayout = new QVBoxLayout; - groupBox->setLayout(groupLayout); - - groupLayout->addWidget(m_boxEditor); - - auto mainLayout = new QVBoxLayout; - mainLayout->setContentsMargins(0, 0, 0, 0); - mainLayout->addWidget(groupBox); - mainLayout->addStretch(); - setLayout(mainLayout); -} - -void SimulationOptionsWidget::setItem(SimulationOptionsItem* item) -{ - m_boxEditor->clearEditor(); - m_boxEditor->setItem(item); -} diff --git a/GUI/Views/SimulationWidgets/SimulationOptionsWidget.h b/GUI/Views/SimulationWidgets/SimulationOptionsWidget.h deleted file mode 100644 index ed121df9d737c17dc2e6c425565ecde1d7d11b11..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationOptionsWidget.h +++ /dev/null @@ -1,38 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationOptionsWidget.h -//! @brief Defines class SimulationOptionsWidget -//! -//! @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 BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONOPTIONSWIDGET_H -#define BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONOPTIONSWIDGET_H - -#include <QWidget> - -class SimulationOptionsItem; -class ComponentFlatView; - -//! Holds widgets related to the setup of simulation/job options (nthreads, run policy, -//! computation method). Part of SimulationView/SimulationSetupWidet - -class SimulationOptionsWidget : public QWidget { - Q_OBJECT - -public: - SimulationOptionsWidget(QWidget* parent = nullptr); - - void setItem(SimulationOptionsItem* item); - -private: - ComponentFlatView* m_boxEditor; -}; - -#endif // BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONOPTIONSWIDGET_H diff --git a/GUI/Views/SimulationWidgets/SimulationSetupAssistant.cpp b/GUI/Views/SimulationWidgets/SimulationSetupAssistant.cpp deleted file mode 100644 index 643afbeb27973c53f98aa5a01e8c5af9ec7bc3bc..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationSetupAssistant.cpp +++ /dev/null @@ -1,93 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationSetupAssistant.cpp -//! @brief Implements class SimulationSetupAssistant -//! -//! @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 "GUI/Views/SimulationWidgets/SimulationSetupAssistant.h" -#include "GUI/Models/InstrumentItems.h" -#include "GUI/Models/SampleValidator.h" -#include <QMessageBox> - -//! Returns true if given setup is valid for submitting the job - -SimulationSetupAssistant::SimulationSetupAssistant() : m_isValid(false) {} - -bool SimulationSetupAssistant::isValidSimulationSetup(const MultiLayerItem* multiLayerItem, - const InstrumentItem* instrumentItem, - const RealDataItem* realData) -{ - clear(); - - checkMultiLayerItem(multiLayerItem); - checkInstrumentItem(instrumentItem); - checkFittingSetup(instrumentItem, realData); - - if (!m_isValid) - QMessageBox::warning(nullptr, "Can't run the job", - composeMessage()); // #baTODO fix modality - - return m_isValid; -} - -void SimulationSetupAssistant::clear() -{ - m_isValid = true; - m_messages.clear(); -} - -void SimulationSetupAssistant::checkMultiLayerItem(const MultiLayerItem* multiLayerItem) -{ - if (!multiLayerItem) { - m_messages.append("No sample selected"); - m_isValid = false; - } else { - SampleValidator sampleValidator; - if (!sampleValidator.isValidMultiLayer(multiLayerItem)) { - m_isValid = false; - m_messages.append(sampleValidator.getValidationMessage()); - } - } -} - -void SimulationSetupAssistant::checkInstrumentItem(const InstrumentItem* instrumentItem) -{ - if (!instrumentItem) { - m_messages.append("No instrument selected"); - m_isValid = false; - } -} - -//! Check if setup is suitable for fitting. In the case when there is a realData defined, -//! its axes will be compared with current detector item. - -void SimulationSetupAssistant::checkFittingSetup(const InstrumentItem* instrumentItem, - const RealDataItem* realData) -{ - if (!realData || !instrumentItem || instrumentItem->alignedWith(realData)) - return; - - m_isValid = false; - m_messages.append("The experimental data does not fit in the selected instrument. Try linking " - "them in Import Tab."); -} - -//! Composes the error message for message box. - -QString SimulationSetupAssistant::composeMessage() -{ - QString result("Can't run the job with current settings\n\n"); - for (auto message : m_messages) { - QString text = QString("- %1 \n").arg(message); - result.append(text); - } - return result; -} diff --git a/GUI/Views/SimulationWidgets/SimulationSetupAssistant.h b/GUI/Views/SimulationWidgets/SimulationSetupAssistant.h deleted file mode 100644 index d7cdbe63d1a44b216648371ed3ea945391cfcf33..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationSetupAssistant.h +++ /dev/null @@ -1,46 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationSetupAssistant.h -//! @brief Defines class SimulationSetupAssistant -//! -//! @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 BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONSETUPASSISTANT_H -#define BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONSETUPASSISTANT_H - -#include <QStringList> - -class MultiLayerItem; -class InstrumentItem; -class RealDataItem; - -//! The SimulationSetupAssistant class provides sample, instrument and real data validation before -//! submitting the job. - -class SimulationSetupAssistant { -public: - SimulationSetupAssistant(); - - bool isValidSimulationSetup(const MultiLayerItem* multiLayerItem, - const InstrumentItem* instrumentItem, - const RealDataItem* realData = 0); - -private: - void clear(); - void checkMultiLayerItem(const MultiLayerItem* multiLayerItem); - void checkInstrumentItem(const InstrumentItem* instrumentItem); - void checkFittingSetup(const InstrumentItem* instrumentItem, const RealDataItem* realData); - QString composeMessage(); - - bool m_isValid; - QStringList m_messages; -}; - -#endif // BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONSETUPASSISTANT_H diff --git a/GUI/Views/SimulationWidgets/SimulationSetupWidget.cpp b/GUI/Views/SimulationWidgets/SimulationSetupWidget.cpp deleted file mode 100644 index e1e748da7eba684c44431b9853ae4cc72b4ddea5..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationSetupWidget.cpp +++ /dev/null @@ -1,138 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationSetupWidget.cpp -//! @brief Implements class SimulationSetupWidget -//! -//! @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 "GUI/Views/SimulationWidgets/SimulationSetupWidget.h" -#include "GUI/Models/ApplicationModels.h" -#include "GUI/Models/DocumentModel.h" -#include "GUI/Models/JobItem.h" -#include "GUI/Models/JobModel.h" -#include "GUI/Views/SimulationWidgets/PythonScriptWidget.h" -#include "GUI/Views/SimulationWidgets/SimulationDataSelectorWidget.h" -#include "GUI/Views/SimulationWidgets/SimulationOptionsWidget.h" -#include "GUI/Views/SimulationWidgets/SimulationSetupAssistant.h" -#include "GUI/mainwindow/ProjectManagerStore.h" -#include "GUI/mainwindow/projectmanager.h" -#include <QMessageBox> -#include <QPushButton> -#include <QVBoxLayout> - -SimulationSetupWidget::SimulationSetupWidget(QWidget* parent) - : QWidget(parent) - , m_applicationModels(0) - , runSimulationButton(0) - , exportToPyScriptButton(0) - , m_simDataSelectorWidget(new SimulationDataSelectorWidget(this)) - , m_simOptionsWidget(new SimulationOptionsWidget(this)) -{ - QVBoxLayout* mainLayout = new QVBoxLayout; - mainLayout->addWidget(m_simDataSelectorWidget); - mainLayout->addWidget(m_simOptionsWidget); - mainLayout->addWidget(createButtonWidget()); - mainLayout->addStretch(10); - setLayout(mainLayout); - - // signal and slots - connect(runSimulationButton, &QPushButton::clicked, this, - &SimulationSetupWidget::onRunSimulation); - connect(exportToPyScriptButton, &QPushButton::clicked, this, - &SimulationSetupWidget::onExportToPythonScript); -} - -void SimulationSetupWidget::setApplicationModels(ApplicationModels* model) -{ - ASSERT(model); - if (model != m_applicationModels) { - m_applicationModels = model; - m_simDataSelectorWidget->setApplicationModels(model); - updateViewElements(); - } -} - -void SimulationSetupWidget::updateViewElements() -{ - m_simDataSelectorWidget->updateViewElements(); - m_simOptionsWidget->setItem(m_applicationModels->documentModel()->simulationOptionsItem()); -} - -void SimulationSetupWidget::onRunSimulation() -{ - const MultiLayerItem* multiLayerItem = m_simDataSelectorWidget->selectedMultiLayerItem(); - const auto instrumentItem = m_simDataSelectorWidget->selectedInstrumentItem(); - const RealDataItem* realDataItem = m_simDataSelectorWidget->selectedRealDataItem(); - - SimulationSetupAssistant assistant; - if (!assistant.isValidSimulationSetup(multiLayerItem, instrumentItem, realDataItem)) - return; - - JobItem* jobItem = m_applicationModels->jobModel()->addJob( - multiLayerItem, instrumentItem, realDataItem, - m_applicationModels->documentModel()->simulationOptionsItem()); - - if (jobItem->runImmediately() || jobItem->runInBackground()) - m_applicationModels->jobModel()->runJob(jobItem->index()); -} - -void SimulationSetupWidget::onExportToPythonScript() -{ - const MultiLayerItem* multiLayerItem = m_simDataSelectorWidget->selectedMultiLayerItem(); - const auto instrumentItem = m_simDataSelectorWidget->selectedInstrumentItem(); - - SimulationSetupAssistant assistant; - if (!assistant.isValidSimulationSetup(multiLayerItem, instrumentItem)) - return; - - PythonScriptWidget* pythonWidget = new PythonScriptWidget(this); - pythonWidget->show(); - pythonWidget->raise(); - pythonWidget->generatePythonScript( - multiLayerItem, instrumentItem, - m_applicationModels->documentModel()->simulationOptionsItem(), - GUI::Project::Manager::projectManager()->projectDir()); -} - -QWidget* SimulationSetupWidget::createButtonWidget() -{ - QWidget* result = new QWidget; - - QHBoxLayout* simButtonLayout = new QHBoxLayout; - // run simulation button - runSimulationButton = new QPushButton("Run simulation"); - runSimulationButton->setIcon(QIcon(":/images/main_simulation.png")); - runSimulationButton->setMinimumWidth(100); - runSimulationButton->setMinimumHeight(50); - runSimulationButton->setToolTip("Run the simulation using settings above.\n" - " Global shortcut ctrl-r can be used to run from sample view."); - // QPalette palette = runSimulationButton->palette(); - // palette.setColor(QPalette::Button, QColor(GUI::Constants::BUTTON_COLOR)); - // palette.setColor(QPalette::ButtonText, QColor(GUI::Constants::BUTTON_TEXT_COLOR)); - // runSimulationButton->setPalette(palette); - - // export simulation to a python script - exportToPyScriptButton = new QPushButton("Export to Python Script"); - exportToPyScriptButton->setIcon(QIcon(":/images/mode_script.png")); - exportToPyScriptButton->setMinimumWidth(100); - exportToPyScriptButton->setMinimumHeight(50); - exportToPyScriptButton->setToolTip("Export the simulation using settings above to " - "a python script.\n"); - // exportToPyScriptButton->setPalette(palette); - - simButtonLayout->addStretch(); - simButtonLayout->addWidget(runSimulationButton); - simButtonLayout->addWidget(exportToPyScriptButton); - simButtonLayout->addStretch(); - - result->setLayout(simButtonLayout); - - return result; -} diff --git a/GUI/Views/SimulationWidgets/SimulationSetupWidget.h b/GUI/Views/SimulationWidgets/SimulationSetupWidget.h deleted file mode 100644 index f07a4099d13ae26af21c749b25314e81e8878a93..0000000000000000000000000000000000000000 --- a/GUI/Views/SimulationWidgets/SimulationSetupWidget.h +++ /dev/null @@ -1,54 +0,0 @@ -// ************************************************************************************************ -// -// BornAgain: simulate and fit reflection and scattering -// -//! @file GUI/Views/SimulationWidgets/SimulationSetupWidget.h -//! @brief Defines class SimulationSetupWidget -//! -//! @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 BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONSETUPWIDGET_H -#define BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONSETUPWIDGET_H - -#include <QWidget> - -class QPushButton; -class MultiLayerItem; -class SimulationDataSelectorWidget; -class SimulationOptionsWidget; -class ApplicationModels; - -//! The SimulationSetupWidget class represents a main widget to define simulation settings -//! and run the simulation. Belongs to the SimulationView. - -class SimulationSetupWidget : public QWidget { - Q_OBJECT - -public: - SimulationSetupWidget(QWidget* parent = 0); - - void setApplicationModels(ApplicationModels* model); - void updateViewElements(); - -public slots: - void onRunSimulation(); - void onExportToPythonScript(); - -private: - QWidget* createButtonWidget(); - - ApplicationModels* m_applicationModels; - - QPushButton* runSimulationButton; - QPushButton* exportToPyScriptButton; - - SimulationDataSelectorWidget* m_simDataSelectorWidget; - SimulationOptionsWidget* m_simOptionsWidget; -}; - -#endif // BORNAGAIN_GUI_VIEWS_SIMULATIONWIDGETS_SIMULATIONSETUPWIDGET_H diff --git a/GUI/mainwindow/mainwindow.cpp b/GUI/mainwindow/mainwindow.cpp index 5037d978012108370bdc8c10becc196fa9e4879f..cdcd51efe96807e307e4fa2162517d1dc681f8a2 100644 --- a/GUI/mainwindow/mainwindow.cpp +++ b/GUI/mainwindow/mainwindow.cpp @@ -211,7 +211,7 @@ void MainWindow::onRunSimulationShortcut() // since the runSimulation method will only change focus after finishing the simulation if (auto widget = QApplication::focusWidget()) widget->clearFocus(); - m_simulationView->onRunSimulationShortcut(); + m_simulationView->runSimulation(); } //! Inserts/removes developers SessionModelView on the left tabbar.