Mantid
Loading...
Searching...
No Matches
AlgorithmPropertiesWidget.cpp
Go to the documentation of this file.
1// Mantid Repository : https://github.com/mantidproject/mantid
2//
3// Copyright © 2018 ISIS Rutherford Appleton Laboratory UKRI,
4// NScD Oak Ridge National Laboratory, European Spallation Source,
5// Institut Laue - Langevin & CSNS, Institute of High Energy Physics, CAS
6// SPDX - License - Identifier: GPL - 3.0 +
8
18
19#include <QCoreApplication>
20#include <QGridLayout>
21#include <QGroupBox>
22#include <QScrollArea>
23
24#include <algorithm>
25#include <utility>
26
27#include <vector>
28
29using namespace Mantid::Kernel;
34
35namespace MantidQt::API {
36
37//----------------------------------------------------------------------------------------------
41 : QWidget(parent), m_algoName(""), m_algo(), m_inputHistory(nullptr) {
42 // Create the grid layout that will have all the widgets
43 m_inputGrid = new QGridLayout;
44
45 // Create the viewport that holds only the grid layout
46 m_viewport = new QWidget(this);
47
48 // Put everything in a vertical box and put it inside the m_scroll area
49 auto *mainLay = new QVBoxLayout();
50 m_viewport->setLayout(mainLay);
51 // The property boxes
52 mainLay->addLayout(m_inputGrid);
53 // Add a stretchy item to allow the properties grid to be top-aligned
54 mainLay->addStretch(1);
55
56 // Create a m_scroll area for the (rare) occasion when an algorithm has
57 // so many properties it won't fit on the screen
58 m_scroll = new QScrollArea(this);
59 m_scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
60 m_scroll->setVerticalScrollBarPolicy(Qt::ScrollBarAsNeeded);
61 m_scroll->setWidget(m_viewport);
62 m_scroll->setWidgetResizable(true);
63 m_scroll->setAlignment(Qt::Alignment(Qt::AlignLeft & Qt::AlignTop));
64
65 // Add a layout for the whole widget, containing just the m_scroll area
66 auto *dialog_layout = new QVBoxLayout();
67 dialog_layout->addWidget(m_scroll);
68 setLayout(dialog_layout);
69
70 this->initLayout();
71
72 // Wide widgets inside the QScrollArea do not cause the dialog to grow
73 // and so can be cut off. Force a minimum width
74 setObjectName("AlgorithmPropertiesWidget");
75 setStyleSheet("#AlgorithmPropertiesWidget {min-width: 25em;}");
76}
77
78//----------------------------------------------------------------------------------------------
82
83//----------------------------------------------------------------------------------------------
92
93//----------------------------------------------------------------------------------------------
96
97//----------------------------------------------------------------------------------------------
102 if (!algo)
103 return;
104 saveInput();
105 m_algo = algo;
106 m_algoName = QString::fromStdString(m_algo->name());
107 this->initLayout();
108}
109
110//----------------------------------------------------------------------------------------------
113
118 FrameworkManager::Instance();
119 m_algoName = std::move(name);
120 try {
121 Algorithm_sptr alg = AlgorithmManager::Instance().createUnmanaged(m_algoName.toStdString());
122 alg->initialize();
123
124 // Set the algorithm ptr. This will redo the layout
125 this->setAlgorithm(alg);
126 } catch (std::runtime_error &) {
127 }
128}
129
130//---------------------------------------------------------------------------------------------------------------
132void AlgorithmPropertiesWidget::addEnabledAndDisableLists(const QStringList &enabled, const QStringList &disabled) {
133 this->m_enabled = enabled;
134 this->m_disabled = disabled;
135}
136
137//---------------------------------------------------------------------------------------------------------------
139bool haveInputWS(const std::vector<Property *> &prop_list) {
140 // For the few algorithms (mainly loading) that do not have input workspaces,
141 // we do not
142 // want to render the 'replace input workspace button'. Do a quick scan to
143 // check.
144 // Also the ones that don't have a set of allowed values as input workspace
145 std::vector<Property *>::const_iterator pEnd = prop_list.end();
146 for (std::vector<Property *>::const_iterator pIter = prop_list.begin(); pIter != pEnd; ++pIter) {
147 Property *prop = *pIter;
148 if (prop->direction() == Direction::Input && dynamic_cast<IWorkspaceProperty *>(prop)) {
149 return true;
150 }
151 }
152 return false;
153}
154
155//---------------------------------------------------------------------------------------------------------------
160 if (!getAlgorithm())
161 return;
162
163 // Delete all widgets in the layout
164 QLayoutItem *child;
165 while ((child = m_inputGrid->takeAt(0)) != nullptr) {
166 if (child->widget())
167 child->widget()->deleteLater();
168
169 delete child;
170 }
171
172 // This also deletes the PropertyWidget, which does not actually
173 // contain the sub-widgets because they are shared in the grid layout
174 for (auto &propWidget : m_propWidgets)
175 propWidget->deleteLater();
176 QCoreApplication::processEvents();
177 m_propWidgets.clear();
178
179 // Create a grid of properties if there are any available
180 const std::vector<Property *> &prop_list = getAlgorithm()->getProperties();
181 bool hasInputWS = haveInputWS(prop_list);
182
183 if (!prop_list.empty()) {
184 // Put the property boxes in a grid
186
187 std::string group = "";
188
189 // Each property is on its own row
190 int row = 0;
191
192 for (auto prop : prop_list) {
193 QString propName = QString::fromStdString(prop->name());
194
195 // Are we entering a new group?
196 if (prop->getGroup() != group) {
197 group = prop->getGroup();
198
199 if (group == "") {
200 // Return to the original grid
202 } else {
203 // Make a groupbox with a border and a light background
204 QGroupBox *grpBox = new QGroupBox(QString::fromStdString(group));
205 grpBox->setAutoFillBackground(true);
206 grpBox->setStyleSheet("QGroupBox { border: 1px solid gray; border-radius: 4px; "
207 "font-weight: bold; margin-top: 4px; margin-bottom: 4px; "
208 "padding-top: 16px; }"
209 "QGroupBox::title { background-color: transparent; "
210 "subcontrol-position: top center; padding-top:4px; "
211 "padding-bottom:4px; } ");
212 QPalette pal = grpBox->palette();
213 pal.setColor(grpBox->backgroundRole(), pal.alternateBase().color());
214 grpBox->setPalette(pal);
215
216 // Put the frame in the main grid
217 m_inputGrid->addWidget(grpBox, row, 0, 1, 4);
218
219 m_groupWidgets[QString::fromStdString(group)] = grpBox;
220
221 // Make a layout in the grp box
222 m_currentGrid = new QGridLayout;
223 grpBox->setLayout(m_currentGrid);
224
225 row++;
226 }
227 }
228
229 // Only accept input for output properties or workspace properties
230 bool isWorkspaceProp(dynamic_cast<IWorkspaceProperty *>(prop));
231 if (prop->direction() == Direction::Output && !isWorkspaceProp)
232 continue;
233
234 // Create the appropriate widget at this row in the grid.
236
237 // Set the previous input value, if any
238 if (m_inputHistory) {
239 QString oldValue = m_inputHistory->previousInput(m_algoName, propName);
240 // Empty string if not found. This means use the default value.
241 if (!oldValue.isEmpty()) {
242 auto error = prop->setValue(oldValue.toStdString());
243 // TODO: [Known defect]: this does not match the initialization sequence
244 // in `AlgorithmDialog`. In the `AlgorithmDialog` case the 'valueChanged' SIGNAL
245 // will already have been connected at the point the previous values are set.
246 // By implication: this initialization will not initialize properties
247 // with dynamic-default values correctly.
248 // However, at present this clause is not actually executed anywhere in the codebase.
249 // WHEN this clause is used, this issue might need to be fixed!
250 widget->setError(QString::fromStdString(error));
251 widget->setPrevious_isDynamicDefault(false);
252 widget->setPreviousValue(oldValue);
253 }
254 }
255
256 m_propWidgets[propName] = widget;
257
258 // Whenever the value changes in the widget, this fires propertyChanged()
259 connect(widget, SIGNAL(valueChanged(const QString &)), this, SLOT(propertyChanged(const QString &)));
260
261 // For clicking the "Replace Workspace" button (if any)
262 connect(widget, SIGNAL(replaceWorkspaceName(const QString &)), this, SLOT(replaceWSClicked(const QString &)));
263
264 // Only show the "Replace Workspace" button if the algorithm has an input
265 // workspace.
266 if (hasInputWS && !prop->disableReplaceWSButton())
267 widget->addReplaceWSButton();
268
269 ++row;
270 } //(end for each property)
271
272 } // (there are properties)
273}
274
275//--------------------------------------------------------------------------------------
280void AlgorithmPropertiesWidget::propertyChanged(const QString &changedPropName) {
281 this->hideOrDisableProperties(changedPropName);
282}
283
285 Mantid::Kernel::Property const *const property = candidate->getProperty();
286 const std::string &propertyName = property->name();
287 return propertyName == "InputWorkspace" || propertyName == "LHSWorkspace";
288}
289
290//-------------------------------------------------------------------------------------------------
295void AlgorithmPropertiesWidget::replaceWSClicked(const QString &propName) {
296 if (m_propWidgets.contains(propName)) {
297 PropertyWidget *propWidget = m_propWidgets[propName];
298 if (propWidget) {
299 using CollectionOfPropertyWidget = std::vector<PropertyWidget *>;
300 CollectionOfPropertyWidget candidateReplacementSources;
301 // Find the name to put in the spot
302 for (auto it = m_propWidgets.begin(); it != m_propWidgets.end(); it++) {
303 // Only look at workspace properties
304 PropertyWidget *otherWidget = it.value();
305 Property *prop = it.value()->getProperty();
306 const IWorkspaceProperty *wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
307 if (otherWidget && wsProp) {
308 if (prop->direction() == Direction::Input) {
309 // Input workspace property. Get the text typed in.
310 QString wsName = otherWidget->getValue();
311 if (!wsName.isEmpty()) {
312 // Add the candidate to the list of candidates.
313 candidateReplacementSources.emplace_back(otherWidget);
314 }
315 }
316 }
317 }
318
319 // Choose from candidates, only do this if there are candidates to select
320 // from.
321 if (candidateReplacementSources.size() > 0) {
322 const auto selectedIt = std::find_if(candidateReplacementSources.cbegin(), candidateReplacementSources.cend(),
324 if (selectedIt != candidateReplacementSources.end()) {
325 // Use the InputWorkspace property called "InputWorkspace" or "LHSWorkspace" as the
326 // source for the OutputWorkspace.
327 propWidget->setValue((*selectedIt)->getValue());
328 } else {
329 // Take the first candidate if there are none called "InputWorkspace"
330 // as the source for the OutputWorkspace.
331 propWidget->setValue(candidateReplacementSources.front()->getValue());
332 }
333 propWidget->userEditedProperty();
334 }
335 }
336 }
337}
338
339//-------------------------------------------------------------------------------------------------
344bool AlgorithmPropertiesWidget::isWidgetEnabled(const Property *property, const QString &propName) const {
345 // To avoid errors
346 if (propName.isEmpty())
347 return true;
348 if (!property)
349 return true;
350
351 // Keep things enabled if requested
352 if (m_enabled.contains(propName))
353 return true;
354
359 if (m_disabled.contains(propName)) {
360 return false;
361 } else {
362 // Regular C++ algo. Let the property tell us,
363 // possibly using validators, if it is to be shown enabled
364 if (property->getSettings())
365 return property->getSettings()->isEnabled(m_algo.get());
366 else
367 return true;
368 }
369}
370
371//-------------------------------------------------------------------------------------------------
379void AlgorithmPropertiesWidget::hideOrDisableProperties(const QString &changedPropName) {
380 // Apply `SetValueWhenProperty` or other `IPropertySettings` as appropriate.
381 auto const *changedPropWidget = !changedPropName.isEmpty() ? m_propWidgets[changedPropName] : nullptr;
382 for (auto &widget : m_propWidgets) {
383 Mantid::Kernel::Property *prop = widget->getProperty();
384 const QString propName = QString::fromStdString(prop->name());
385
386 IPropertySettings *settings = prop->getSettings();
387 if (settings) {
388 // Dynamic PropertySettings objects allow a property to change
389 // validators. This removes the old widget and creates a new one
390 // instead.
391
392 if (settings->isConditionChanged(m_algo.get(), changedPropName.toStdString())) {
393 if (settings->applyChanges(m_algo.get(), prop->name())) {
394 // WARNING: allow for the possibility that the current property has been replaced inside of `applyChanges`!
395 prop = m_algo->getPointerToProperty(propName.toStdString());
396
397 widget->setVisible(false);
398
399 // Create a new widget at the same position in the layout grid:
400 // since widget is a reference, this also replaces the `widget*` in `m_propWidgets`.
401 auto *oldWidget = widget;
402 int row = widget->getGridRow();
403 QGridLayout *layout = widget->getGridLayout();
404 widget = PropertyWidgetFactory::createWidget(prop, this, layout, row);
405 widget->transferHistoryState(oldWidget, changedPropWidget);
406
407 // Delete the old widget
408 oldWidget->deleteLater();
409
410 // Whenever the value changes in the widget, this fires
411 // propertyChanged()
412 connect(widget, SIGNAL(valueChanged(const QString &)), this, SLOT(propertyChanged(const QString &)));
413 }
414 }
415 }
416 } // for each property
417
418 // set Visible and Enabled as appropriate
419 for (auto &widget : m_propWidgets) {
420 Mantid::Kernel::Property const *prop = widget->getProperty();
421 IPropertySettings const *settings = prop->getSettings();
422 auto const &propName = QString::fromStdString(prop->name());
423
424 // Set the enabled and visible flags based on what the validators say.
425 // Default is always true.
426 bool visible = true;
427 // Dynamically check if the widget should be enabled.
428 bool enabled = this->isWidgetEnabled(prop, propName);
429
430 // Do we have a custom IPropertySettings?
431 if (settings) {
432 // Set the visible flag
433 visible = settings->isVisible(m_algo.get());
434 }
435
436 // Show/hide the validator label (that red star)
437 QString error = "";
438 if (m_errors.contains(propName))
439 error = m_errors[propName];
440 // Always show controls that are in error
441 if (error.length() != 0)
442 visible = true;
443
444 // Hide/disable the widget
445 widget->setEnabled(enabled);
446 widget->setVisible(visible);
447 } // for each property
448
449 this->repaint();
450}
451
452//-------------------------------------------------------------------------------------------------
457 if (m_inputHistory) {
458 for (auto pitr = m_propWidgets.begin(); pitr != m_propWidgets.end(); ++pitr) {
459 PropertyWidget const *widget = pitr.value();
460 auto const *prop = widget->getProperty();
461 QString const &propName = pitr.key();
462
463 // Normalize default values to empty string.
464 QString value = (prop->isDefault() || prop->isDynamicDefault() ? "" : widget->getValue());
465
466 // save the value
467 m_inputHistory->storeNewValue(m_algoName, QPair<QString, QString>(propName, value));
468 }
469 }
470}
471
472} // namespace MantidQt::API
std::string name
Definition Run.cpp:60
double value
The value of the point.
Definition FitMW.cpp:51
double error
This abstract class deals with the loading and saving of previous algorithm property values to/from M...
QString previousInput(const QString &algName, const QString &propName) const
Retrieve an old parameter value.
void storeNewValue(const QString &algName, const QPair< QString, QString > &property)
Update the old values that are stored here.
QScrollArea * m_scroll
Scroll area containing the viewport.
void initLayout()
Create the layout for this dialog.
void replaceWSClicked(const QString &propName)
Replace WS button was clicked.
MantidQt::API::AbstractAlgorithmInputHistory * m_inputHistory
History of inputs to the algorithm.
Mantid::API::IAlgorithm_sptr m_algo
Pointer to the algorithm to view.
QStringList m_enabled
A list of property names that are FORCED to stay enabled.
QGridLayout * m_inputGrid
The grid widget containing the input boxes.
void setAlgorithmName(QString name)
Set the algorithm to view using its name.
QHash< QString, QString > m_errors
A map where key = property name; value = the error for this property (i.e.
QWidget * m_viewport
Viewport containing the grid of property widgets.
QHash< QString, QGroupBox * > m_groupWidgets
Mapping between group and it's dynamically created widget.
void propertyChanged(const QString &changedPropName)
Any property changed.
bool isWidgetEnabled(const Mantid::Kernel::Property *property, const QString &propName) const
Check if the control should be enabled for this property.
void setAlgorithm(const Mantid::API::IAlgorithm_sptr &algo)
Directly set the algorithm to view.
void hideOrDisableProperties(const QString &changedPropName="")
Go through all the properties, and check their validators to determine whether they should be made di...
void addEnabledAndDisableLists(const QStringList &enabled, const QStringList &disabled)
Sets the properties to force as enabled/disabled.
QHash< QString, PropertyWidget * > m_propWidgets
Each dynamically created PropertyWidget.
void saveInput()
When closing or changing algorithm, this saves the input history to QSettings.
QStringList m_disabled
A list of property names that are FORCED to stay disabled.
QGridLayout * m_currentGrid
The current grid widget for sub-boxes.
AlgorithmPropertiesWidget(QWidget *parent=nullptr)
Constructor.
~AlgorithmPropertiesWidget() override
Destructor.
void setInputHistory(MantidQt::API::AbstractAlgorithmInputHistory *inputHistory)
Sets the AlgorithmInputHistoryImpl object holding all histories.
static PropertyWidget * createWidget(Mantid::Kernel::Property *prop, QWidget *parent=nullptr, QGridLayout *layout=nullptr, int row=-1)
Create the appropriate PropertyWidget for the given Property.
Base class for widgets that will set Mantid::Kernel::Property* types.
void setValue(const QString &value)
Set the value of the property given into the GUI state.
virtual QString getValue() const =0
Return the value of the property given the GUI state.
void addReplaceWSButton()
Create and show the "Replace WS" button.
void setError(const QString &error)
Externally set an error string to display in the validator.
const Mantid::Kernel::Property * getProperty() const
void setPrevious_isDynamicDefault(bool flag)
Set the isDynamicDefault flag associated with the previously-entered value.
void userEditedProperty()
To be called when a user edits a property, as opposed to one being set programmatically.
void setPreviousValue(const QString &previousValue)
Set this widget's previously-entered value.
An interface that is implemented by WorkspaceProperty.
Interface for modifiers to Property's that specify if they should be enabled or visible in a GUI.
virtual bool applyChanges(const IPropertyManager *algo, const std::string &currentPropName)
Overload this virtual function in order to modify the current property based on changes to other prop...
virtual bool isConditionChanged(const IPropertyManager *algo, const std::string &changedPropName="") const
to verify if the properties, this one depends on have changed or other special condition occurs which...
virtual bool isVisible(const IPropertyManager *algo) const
Is the property to be shown in the GUI? Default true.
Base class for properties.
Definition Property.h:94
const IPropertySettings * getSettings() const
Definition Property.cpp:109
unsigned int direction() const
returns the direction of the property
Definition Property.h:177
const std::string & name() const
Get the property's name.
Definition Property.cpp:61
virtual std::string value() const =0
Returns the value of the property as a string.
bool isCalledInputWorkspaceOrLHSWorkspace(PropertyWidget *const candidate)
bool haveInputWS(const std::vector< Property * > &prop_list)
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
Mantid::Kernel::SingletonHolder< FrameworkManagerImpl > FrameworkManager
Mantid::Kernel::SingletonHolder< AlgorithmManagerImpl > AlgorithmManager
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition Algorithm.h:52
@ Input
An input workspace.
Definition Property.h:53
@ Output
An output workspace.
Definition Property.h:54