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//----------------------------------------------------------------------------------------------
90 m_inputHistory = inputHistory;
91}
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
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.
241 if (!oldValue.isEmpty()) {
242 auto error = prop->setValue(oldValue.toStdString());
243 widget->setError(QString::fromStdString(error));
244 widget->setPreviousValue(oldValue);
245 }
246 }
247
248 m_propWidgets[propName] = widget;
249
250 // Whenever the value changes in the widget, this fires propertyChanged()
251 connect(widget, SIGNAL(valueChanged(const QString &)), this, SLOT(propertyChanged(const QString &)));
252
253 // For clicking the "Replace Workspace" button (if any)
254 connect(widget, SIGNAL(replaceWorkspaceName(const QString &)), this, SLOT(replaceWSClicked(const QString &)));
255
256 // Only show the "Replace Workspace" button if the algorithm has an input
257 // workspace.
258 if (hasInputWS)
259 widget->addReplaceWSButton();
260
261 ++row;
262 } //(end for each property)
263
264 } // (there are properties)
265}
266
267//--------------------------------------------------------------------------------------
272void AlgorithmPropertiesWidget::propertyChanged(const QString &changedPropName) {
273 this->hideOrDisableProperties(changedPropName);
274}
275
277 Mantid::Kernel::Property const *const property = candidate->getProperty();
278 const std::string &propertyName = property->name();
279 return propertyName == "InputWorkspace";
280}
281
282//-------------------------------------------------------------------------------------------------
287void AlgorithmPropertiesWidget::replaceWSClicked(const QString &propName) {
288 if (m_propWidgets.contains(propName)) {
289 using CollectionOfPropertyWidget = std::vector<PropertyWidget *>;
290 CollectionOfPropertyWidget candidateReplacementSources;
291 PropertyWidget *propWidget = m_propWidgets[propName];
292 if (propWidget) {
293 // Find the name to put in the spot
294 QString wsName("");
295 for (auto it = m_propWidgets.begin(); it != m_propWidgets.end(); it++) {
296 // Only look at workspace properties
297 PropertyWidget *otherWidget = it.value();
298 Property *prop = it.value()->getProperty();
299 IWorkspaceProperty *wsProp = dynamic_cast<IWorkspaceProperty *>(prop);
300 if (otherWidget && wsProp) {
301 if (prop->direction() == Direction::Input) {
302 // Input workspace property. Get the text typed in.
303 wsName = otherWidget->getValue();
304 if (!wsName.isEmpty()) {
305 // Add the candidate to the list of candidates.
306 candidateReplacementSources.emplace_back(otherWidget);
307 }
308 }
309 }
310 }
311
312 // Choose from candidates, only do this if there are candidates to select
313 // from.
314 if (candidateReplacementSources.size() > 0) {
315 CollectionOfPropertyWidget::iterator selectedIt = std::find_if(
316 candidateReplacementSources.begin(), candidateReplacementSources.end(), isCalledInputWorkspace);
317 if (selectedIt != candidateReplacementSources.end()) {
318 // Use the InputWorkspace property called "InputWorkspace" as the
319 // source for the OutputWorkspace.
320 propWidget->setValue((*selectedIt)->getValue());
321 } else {
322 // Take the first candidate if there are none called "InputWorkspace"
323 // as the source for the OutputWorkspace.
324 propWidget->setValue(candidateReplacementSources.front()->getValue());
325 }
326 propWidget->userEditedProperty();
327 }
328 }
329 }
330}
331
332//-------------------------------------------------------------------------------------------------
337bool AlgorithmPropertiesWidget::isWidgetEnabled(Property *property, const QString &propName) const {
338 // To avoid errors
339 if (propName.isEmpty())
340 return true;
341 if (!property)
342 return true;
343
344 // Keep things enabled if requested
345 if (m_enabled.contains(propName))
346 return true;
347
352 if (m_disabled.contains(propName)) {
353 return false;
354 } else {
355 // Regular C++ algo. Let the property tell us,
356 // possibly using validators, if it is to be shown enabled
357 if (property->getSettings())
358 return property->getSettings()->isEnabled(m_algo.get());
359 else
360 return true;
361 }
362}
363
364//-------------------------------------------------------------------------------------------------
372void AlgorithmPropertiesWidget::hideOrDisableProperties(const QString &changedPropName) {
373 // SetValueWhenProperty as appropriate
374 for (auto &widget : m_propWidgets) {
375 Mantid::Kernel::Property *prop = widget->getProperty();
376 IPropertySettings *settings = prop->getSettings();
377
378 if (settings) {
379 // Dynamic PropertySettings objects allow a property to change
380 // validators. This removes the old widget and creates a new one
381 // instead.
382 if (settings->isConditionChanged(m_algo.get(), changedPropName.toStdString())) {
383 settings->applyChanges(m_algo.get(), prop);
384
385 // Delete the old widget
386 int row = widget->getGridRow();
387 QGridLayout *layout = widget->getGridLayout();
388 widget->setVisible(false);
389 widget->deleteLater();
390
391 // Create the appropriate widget at this row in the grid.
392 widget = PropertyWidgetFactory::createWidget(prop, this, layout, row);
393
394 // Whenever the value changes in the widget, this fires
395 // propertyChanged()
396 connect(widget, SIGNAL(valueChanged(const QString &)), this, SLOT(propertyChanged(const QString &)));
397 }
398 }
399 } // for each property
400
401 // set Visible and Enabled as appropriate
402 for (auto &widget : m_propWidgets) {
403 Mantid::Kernel::Property *prop = widget->getProperty();
404 IPropertySettings *settings = prop->getSettings();
405 const auto &propName = QString::fromStdString(prop->name());
406
407 // Set the enabled and visible flags based on what the validators say.
408 // Default is always true.
409 bool visible = true;
410 // Dynamically check if the widget should be enabled.
411 bool enabled = this->isWidgetEnabled(prop, propName);
412
413 // Do we have a custom IPropertySettings?
414 if (settings) {
415 // Set the visible flag
416 visible = settings->isVisible(m_algo.get());
417 }
418
419 // Show/hide the validator label (that red star)
420 QString error = "";
421 if (m_errors.contains(propName))
422 error = m_errors[propName];
423 // Always show controls that are in error
424 if (error.length() != 0)
425 visible = true;
426
427 // Hide/disable the widget
428 widget->setEnabled(enabled);
429 widget->setVisible(visible);
430 } // for each property
431
432 this->repaint();
433}
434
435//-------------------------------------------------------------------------------------------------
440 if (m_inputHistory) {
441 for (auto pitr = m_propWidgets.begin(); pitr != m_propWidgets.end(); ++pitr) {
442 PropertyWidget *widget = pitr.value();
443 const QString &propName = pitr.key();
444 QString value = widget->getValue();
445 // Mantid::Kernel::Property *prop = widget->getProperty();
446 // if (!prop || prop->remember())
447 m_inputHistory->storeNewValue(m_algoName, QPair<QString, QString>(propName, value));
448 }
449 }
450}
451
452} // namespace MantidQt::API
double value
The value of the point.
Definition: FitMW.cpp:51
double error
Definition: IndexPeaks.cpp:133
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.
bool isWidgetEnabled(Mantid::Kernel::Property *property, const QString &propName) const
Check if the control should be enabled for this property.
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.
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.
Mantid::Kernel::Property * getProperty()
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.
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 void applyChanges(const IPropertyManager *, Property *const)
The function user have to overload it in their custom code to modify the property according to the ch...
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
IPropertySettings * getSettings()
Definition: Property.cpp:98
unsigned int direction() const
returns the direction of the property
Definition: Property.h:172
const std::string & name() const
Get the property's name.
Definition: Property.cpp:60
virtual std::string value() const =0
Returns the value of the property as a string.
Manage the lifetime of a class intended to be a singleton.
static T & Instance()
Return a reference to the Singleton instance, creating it if it does not already exist Creation is do...
bool isCalledInputWorkspace(PropertyWidget *const candidate)
bool haveInputWS(const std::vector< Property * > &prop_list)
std::shared_ptr< IAlgorithm > IAlgorithm_sptr
shared pointer to Mantid::API::IAlgorithm
std::shared_ptr< Algorithm > Algorithm_sptr
Typedef for a shared pointer to an Algorithm.
Definition: Algorithm.h:61
@ Input
An input workspace.
Definition: Property.h:53
@ Output
An output workspace.
Definition: Property.h:54