Newer
Older
Gigg, Martyn Anthony
committed
//----------------------------------
// Includes
//----------------------------------
#include "MantidQtAPI/AlgorithmDialog.h"
#include "MantidQtAPI/AlgorithmInputHistory.h"
Gigg, Martyn Anthony
committed
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/FileValidator.h"
Gigg, Martyn Anthony
committed
#include <QIcon>
#include <QLabel>
#include <QMessageBox>
Gigg, Martyn Anthony
committed
#include <QFileDialog>
#include <QFileInfo>
#include <QLineEdit>
Gigg, Martyn Anthony
committed
#include <QComboBox>
#include <QCheckBox>
#include <QPushButton>
#include <QDesktopServices>
#include <QUrl>
#include <QHBoxLayout>
Gigg, Martyn Anthony
committed
using namespace MantidQt::API;
//------------------------------------------------------
// Public member functions
//------------------------------------------------------
/**
* Default Constructor
*/
AlgorithmDialog::AlgorithmDialog(QWidget* parent) :
Gigg, Martyn Anthony
committed
QDialog(parent), m_algorithm(NULL), m_algName(""), m_propertyValueMap(), m_enabledNames(),
m_forScript(false), m_strMessage(""), m_msgAvailable(false), m_bIsInitialized(false), m_algProperties(),
m_validators()
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
// This disables the WhatsThis question mark button that appears next to the cross on windows
Qt::WindowFlags flags = windowFlags();
flags &= !Qt::WindowContextHelpButtonHint;
setWindowFlags(flags);
Gigg, Martyn Anthony
committed
}
/**
* Destructor
*/
AlgorithmDialog::~AlgorithmDialog()
{
}
/**
* Create the layout for this dialog.
*/
void AlgorithmDialog::initializeLayout()
{
if( isInitialized() ) return;
//Set a common title
setWindowTitle(QString::fromStdString(getAlgorithm()->name()) + " input dialog");
//Set the icon
setWindowIcon(QIcon(":/MantidPlot_Icon_32offset.png"));
createValidatorLabels();
Gigg, Martyn Anthony
committed
// This derived class function creates the layout of the widget. It can also add default input if the
// dialog has been written this way
Gigg, Martyn Anthony
committed
this->initLayout();
Gigg, Martyn Anthony
committed
// Check if there is any default input
Steve Williams
committed
this->parseInput();
Gigg, Martyn Anthony
committed
// Try to set these values. This will validate the defaults and mark those that are invalid, if any.
Steve Williams
committed
setPropertyValues();
Gigg, Martyn Anthony
committed
m_bIsInitialized = true;
}
/**
* Has this dialog been initialized yet
* @returns Whether initialzedLayout has been called yet
*/
bool AlgorithmDialog::isInitialized() const
{
return m_bIsInitialized;
}
//------------------------------------------------------
// Protected member functions
//------------------------------------------------------
/**
* Get the algorithm pointer
* @returns A pointer to the algorithm that is associated with the dialog
*/
Mantid::API::IAlgorithm* AlgorithmDialog::getAlgorithm() const
Gigg, Martyn Anthony
committed
{
return m_algorithm;
}
/**
Gigg, Martyn Anthony
committed
* Get a named property for this algorithm
* @param propName The name of the property
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
Mantid::Kernel::Property* AlgorithmDialog::getAlgorithmProperty(const QString & propName) const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
if( m_algProperties.contains(propName) ) return m_algProperties[propName];
else return NULL;
Gigg, Martyn Anthony
committed
}
/**
Gigg, Martyn Anthony
committed
* Get a property validator label
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
QLabel* AlgorithmDialog::getValidatorMarker(const QString & propname) const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
return m_validators.value(propname);
Gigg, Martyn Anthony
committed
}
/**
Gigg, Martyn Anthony
committed
* Return the message string
* @returns the message string
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
const QString & AlgorithmDialog::getOptionalMessage() const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
return m_strMessage;
Gigg, Martyn Anthony
committed
}
/**
Gigg, Martyn Anthony
committed
* Are we for a script or not
* @returns A boolean inidcating whether we are being called from a script
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
bool AlgorithmDialog::isForScript() const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
return m_forScript;
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
/*
* Is there a message string available
* @returns A boolean indicating whether the message string is empty
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
bool AlgorithmDialog::isMessageAvailable() const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
return !m_strMessage.isEmpty();
Gigg, Martyn Anthony
committed
}
/**
Gigg, Martyn Anthony
committed
* Check if the control should be enabled for this property
* @param propName The name of the property
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
bool AlgorithmDialog::isWidgetEnabled(const QString & propName) const
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
// If this dialog is not for a script then always enable
if( !isForScript() || propName.isEmpty() )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
return true;
}
Steve Williams
committed
Gigg, Martyn Anthony
committed
if( isInEnabledList(propName) ) return true;
Steve Williams
committed
Gigg, Martyn Anthony
committed
// Otherwise it must be disabled but only if it is valid
Mantid::Kernel::Property *property = getAlgorithmProperty(propName);
if( property->isValid().empty() )
{
return false;
}
else
{
return true;
Gigg, Martyn Anthony
committed
}
}
Gigg, Martyn Anthony
committed
/**
Gigg, Martyn Anthony
committed
* Adds a property (name,value) pair to the stored map
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
void AlgorithmDialog::storePropertyValue(const QString & name, const QString & value)
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
if( name.isEmpty() ) return;
m_propertyValueMap.insert(name, value);
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
/**
* Open a file selection box
* @param The property name that this is associated with
*/
QString AlgorithmDialog::openLoadFileDialog(const QString & propName)
{
if( propName.isEmpty() ) return "";
Gigg, Martyn Anthony
committed
Mantid::Kernel::PropertyWithValue<std::string>* prop =
dynamic_cast< Mantid::Kernel::PropertyWithValue<std::string>* >( getAlgorithmProperty(propName) );
Gigg, Martyn Anthony
committed
if( !prop ) return "";
//The allowed values in this context are file extensions
std::vector<std::string> exts = prop->allowedValues();
QString filter;
if( !exts.empty() )
{
filter = "Files (";
std::vector<std::string>::const_iterator iend = exts.end();
for( std::vector<std::string>::const_iterator itr = exts.begin(); itr != iend; ++itr)
{
filter.append("*." + QString::fromStdString(*itr) + " ");
}
filter.trimmed();
filter.append(QString::fromStdString(")"));
}
else
{
filter = "All Files (*.*)";
}
Gigg, Martyn Anthony
committed
const Mantid::Kernel::FileValidator *file_checker = dynamic_cast<const Mantid::Kernel::FileValidator*>(prop->getValidator());
Gigg, Martyn Anthony
committed
QFileDialog dialog(this);
dialog.setNameFilter(filter);
dialog.setDirectory(AlgorithmInputHistory::Instance().getPreviousDirectory());
Gigg, Martyn Anthony
committed
if( file_checker && !file_checker->fileMustExist() )
{
Gigg, Martyn Anthony
committed
dialog.setFileMode(QFileDialog::AnyFile);
Gigg, Martyn Anthony
committed
}
else
{
Gigg, Martyn Anthony
committed
dialog.setFileMode(QFileDialog::ExistingFile);
Gigg, Martyn Anthony
committed
}
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
QStringList file_names;
Gigg, Martyn Anthony
committed
if (dialog.exec()) file_names = dialog.selectedFiles();
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
if( !file_names.isEmpty() )
{
AlgorithmInputHistory::Instance().setPreviousDirectory(QFileInfo(file_names[0]).absoluteDir().path());
return file_names[0];
}
return QString();
Gigg, Martyn Anthony
committed
}
/**
Gigg, Martyn Anthony
committed
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
* Takes a combobox and adds the allowed values of the given property to its list.
* It also sets the displayed value to the correct one based on either the history
* or a script input value
* @param propName The name of the property
* @param optionsBox A pointer to a QComoboBox object
* @returns A newed QComboBox
*/
void AlgorithmDialog::fillAndSetComboBox(const QString & propName, QComboBox* optionsBox) const
{
if( !optionsBox ) return;
Mantid::Kernel::Property *property = getAlgorithmProperty(propName);
if( !property ) return;
std::vector<std::string> items = property->allowedValues();
std::vector<std::string>::const_iterator vend = items.end();
for(std::vector<std::string>::const_iterator vitr = items.begin(); vitr != vend;
++vitr)
{
optionsBox->addItem(QString::fromStdString(*vitr));
}
// Display the appropriate value
QString displayed("");
if( !isForScript() )
{
displayed = AlgorithmInputHistory::Instance().previousInput(m_algName, propName);
}
if( displayed.isEmpty() )
{
displayed = QString::fromStdString(property->value());
}
int index = optionsBox->findText(displayed);
if( index >= 0 )
{
optionsBox->setCurrentIndex(index);
}
}
/**
* Takes the given property and QCheckBox pointer and sets the state based on either
* the history or property value
* @param propName The name of the property
* @param
* @returns A newed QCheckBox
*/
void AlgorithmDialog::setCheckBoxState(const QString & propName, QCheckBox* checkBox) const
{
Mantid::Kernel::Property *property = getAlgorithmProperty(propName);
if( !property ) return;
//Check boxes are special in that if they have a default value we need to display it
QString displayed("");
if( !isForScript() )
{
displayed = AlgorithmInputHistory::Instance().previousInput(m_algName, propName);
}
if( displayed.isEmpty() )
{
displayed = QString::fromStdString(property->value());
}
if( displayed == "0" )
{
checkBox->setCheckState(Qt::Unchecked);
}
else
{
checkBox->setCheckState(Qt::Checked);
}
}
/**
* Set the input for a text box based on either the history or a script value
Gigg, Martyn Anthony
committed
* @param propName The name of the property
* @param field The QLineEdit field
*/
Gigg, Martyn Anthony
committed
void AlgorithmDialog::fillLineEdit(const QString & propName, QLineEdit* textField)
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
if( !isForScript() )
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
textField->setText(AlgorithmInputHistory::Instance().previousInput(m_algName, propName));
Gigg, Martyn Anthony
committed
}
else
{
Gigg, Martyn Anthony
committed
Mantid::Kernel::Property *property = getAlgorithmProperty(propName);
if( property && property->isValid().empty() && !property->isDefault() )
{
textField->setText(QString::fromStdString(property->value()));
}
Gigg, Martyn Anthony
committed
}
}
Gigg, Martyn Anthony
committed
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
QHBoxLayout *
AlgorithmDialog::createDefaultButtonLayout(const QString & helpText,
const QString & loadText,
const QString & cancelText)
{
QPushButton *okButton = new QPushButton(loadText);
connect(okButton, SIGNAL(clicked()), this, SLOT(accept()));
okButton->setDefault(true);
QPushButton *exitButton = new QPushButton(cancelText);
connect(exitButton, SIGNAL(clicked()), this, SLOT(close()));
QHBoxLayout *buttonRowLayout = new QHBoxLayout;
buttonRowLayout->addWidget(createHelpButton(helpText));
buttonRowLayout->addStretch();
buttonRowLayout->addWidget(okButton);
buttonRowLayout->addWidget(exitButton);
return buttonRowLayout;
}
/**
* Create a help button that, when clicked, will open a browser to the Mantid wiki page
* for that algorithm
*/
QPushButton* AlgorithmDialog::createHelpButton(const QString & helpText) const
{
QPushButton *help = new QPushButton(helpText);
help->setMaximumWidth(25);
connect(help, SIGNAL(clicked()), this, SLOT(helpClicked()));
return help;
}
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
/**
* A slot that can be used to connect a button that accepts the dialog if
* all of the properties are valid
*/
Gigg, Martyn Anthony
committed
void AlgorithmDialog::accept()
Gigg, Martyn Anthony
committed
{
parseInput();
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
if( setPropertyValues() )
{
saveInput();
QDialog::accept();
}
Gigg, Martyn Anthony
committed
else
{
QMessageBox::critical(this, "",
"One or more properties are invalid. The invalid properties are\n"
"marked with a *, hold your mouse over the * for more information." );
Gigg, Martyn Anthony
committed
}
}
Gigg, Martyn Anthony
committed
/**
* A slot to handle the help button click
*/
void AlgorithmDialog::helpClicked()
{
QDesktopServices::openUrl(QUrl(QString("http://www.mantidproject.org/") + m_algName));
}
Gigg, Martyn Anthony
committed
Gigg, Martyn Anthony
committed
//------------------------------------------------------
// Private member functions
//------------------------------------------------------
/**
* Set the algorithm pointer
* @param alg A pointer to the algorithm
*/
void AlgorithmDialog::setAlgorithm(Mantid::API::IAlgorithm* alg)
Gigg, Martyn Anthony
committed
{
m_algorithm = alg;
Gigg, Martyn Anthony
committed
m_algName = QString::fromStdString(alg->name());
Gigg, Martyn Anthony
committed
m_algProperties.clear();
std::vector<Mantid::Kernel::Property*>::const_iterator iend = alg->getProperties().end();
for( std::vector<Mantid::Kernel::Property*>::const_iterator itr = alg->getProperties().begin(); itr != iend;
++itr )
{
m_algProperties.insert(QString::fromStdString((*itr)->name()), *itr);
}
}
Gigg, Martyn Anthony
committed
/**
Gigg, Martyn Anthony
committed
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
* Set the properties that have been parsed from the dialog.
* @returns A boolean that indicates if the validation was successful.
*/
bool AlgorithmDialog::setPropertyValues()
{
QHash<QString, Mantid::Kernel::Property*>::const_iterator pend = m_algProperties.end();
bool allValid(true);
for( QHash<QString, Mantid::Kernel::Property*>::const_iterator pitr = m_algProperties.begin();
pitr != pend; ++pitr )
{
Mantid::Kernel::Property *prop = pitr.value();
QString pName = pitr.key();
QString value = m_propertyValueMap.value(pName);
QLabel *validator = getValidatorMarker(pitr.key());
std::string error = "";
if ( !value.isEmpty() )
{//if there something in the box then use it
error = prop->setValue(value.toStdString());
}
else
{//else use the default with may or may not be a valid property value
error = prop->setValue(prop->getDefault());
}
if( error.empty() )
{//no error
if( validator ) validator->hide();
//Store value for future input if it is not default
}
else
{//the property could not be set
allValid = false;
if( validator && validator->parent() )
{
//a description of the problem will be visible to users if they linger their mouse over validator star mark
validator->setToolTip( QString::fromStdString(error) );
validator->show();
}
}
}
return allValid;
}
/**
* Save the property values to the input history
*/
void AlgorithmDialog::saveInput()
{
AlgorithmInputHistory::Instance().clearAlgorithmInput(m_algName);
QHash<QString, Mantid::Kernel::Property*>::const_iterator pend = m_algProperties.end();
for( QHash<QString, Mantid::Kernel::Property*>::const_iterator pitr = m_algProperties.begin();
pitr != pend; ++pitr )
{
QString pName = pitr.key();
QString value = m_propertyValueMap.value(pName);
AlgorithmInputHistory::Instance().storeNewValue(m_algName, QPair<QString, QString>(pName, value));
}
}
/**
* Set a list of values for the properties
* @param presetValues A string containing a list of "name=value" pairs with each separated by an '|' character
Gigg, Martyn Anthony
committed
*/
Gigg, Martyn Anthony
committed
void AlgorithmDialog::setPresetValues(const QString & presetValues)
Gigg, Martyn Anthony
committed
{
Gigg, Martyn Anthony
committed
if( presetValues.isEmpty() ) return;
QStringList presets = presetValues.split('|', QString::SkipEmptyParts);
QStringListIterator itr(presets);
Gigg, Martyn Anthony
committed
while( itr.hasNext() )
{
QString namevalue = itr.next();
QString name = namevalue.section('=', 0, 0);
// Simplified removes trims from start and end and replaces all n counts of whitespace with a single whitespace
QString value = namevalue.section('=', 1, 1).simplified();
Gigg, Martyn Anthony
committed
storePropertyValue(name, value.trimmed());
Gigg, Martyn Anthony
committed
}
setPropertyValues();
m_propertyValueMap.clear();
}
Gigg, Martyn Anthony
committed
/**
* Set comma-separated list of enabled parameter names
* @param enabledNames A comma-separated list of parameter names to keep enabled
*/
void AlgorithmDialog::setEnabledNames(const QString & enabledNames)
{
if( enabledNames.isEmpty() ) return;
m_enabledNames = enabledNames.split(',', QString::SkipEmptyParts);
}
bool AlgorithmDialog::isInEnabledList(const QString& propName) const
{
return m_enabledNames.contains(propName);
}
Gigg, Martyn Anthony
committed
/**
* Set if we are for a script or not
* @param forScript A boolean inidcating whether we are being called from a script
*/
void AlgorithmDialog::isForScript(bool forScript)
{
m_forScript = forScript;
}
/**
* Set an optional message to be displayed at the top of the widget
* @param message The message string
*/
void AlgorithmDialog::setOptionalMessage(const QString & message)
{
m_strMessage = message;
if( message.isEmpty() ) m_msgAvailable = false;
}
/**
* This sets up the labels that are to be used to mark whether a property is valid. It has
* a default implmentation but can be overridden if some other marker is required
*/
void AlgorithmDialog::createValidatorLabels()
{
Gigg, Martyn Anthony
committed
QHash<QString, Mantid::Kernel::Property*>::const_iterator pend = m_algProperties.end();
for( QHash<QString, Mantid::Kernel::Property*>::const_iterator pitr = m_algProperties.begin();
Gigg, Martyn Anthony
committed
pitr != pend; ++pitr )
{
QLabel *validLbl = new QLabel("*");
QPalette pal = validLbl->palette();
pal.setColor(QPalette::WindowText, Qt::darkRed);
validLbl->setPalette(pal);
m_validators[pitr.key()] = validLbl;
}
}