Newer
Older
#include "MantidQtCustomInterfaces/ReflMainViewPresenter.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/ITableWorkspace.h"
#include "MantidAPI/TableRow.h"
#include "MantidGeometry/Instrument/ParameterMap.h"
#include "MantidKernel/Strings.h"
#include "MantidKernel/TimeSeriesProperty.h"
#include "MantidKernel/Utils.h"
#include "MantidQtCustomInterfaces/ReflMainView.h"
#include "MantidQtCustomInterfaces/QtReflOptionsDialog.h"
#include "MantidQtMantidWidgets/AlgorithmHintStrategy.h"
#include <boost/tokenizer.hpp>
using namespace Mantid::API;
using namespace Mantid::Geometry;
const QString ReflSettingsGroup = "Mantid/CustomInterfaces/ISISReflectometry";
Harry Jeffery
committed
void validateModel(ITableWorkspace_sptr model)
Harry Jeffery
committed
if(!model)
throw std::runtime_error("Null pointer");
if(model->columnCount() != 9)
throw std::runtime_error("Selected table has the incorrect number of columns (9) to be used as a reflectometry table.");
try
model->String(0,0);
model->String(0,1);
model->String(0,2);
model->String(0,3);
model->String(0,4);
model->String(0,5);
model->Double(0,6);
model->Int(0,7);
model->String(0,8);
catch(const std::runtime_error&)
{
throw std::runtime_error("Selected table does not meet the specifications to become a model for this interface.");
}
}
Harry Jeffery
committed
bool isValidModel(Workspace_sptr model)
{
try
{
validateModel(boost::dynamic_pointer_cast<ITableWorkspace>(model));
}
catch(...)
{
return false;
}
return true;
}
ITableWorkspace_sptr createWorkspace()
{
ITableWorkspace_sptr ws = WorkspaceFactory::Instance().createTable();
auto colRuns = ws->addColumn("str","Run(s)");
auto colTheta = ws->addColumn("str","ThetaIn");
auto colTrans = ws->addColumn("str","TransRun(s)");
auto colQmin = ws->addColumn("str","Qmin");
auto colQmax = ws->addColumn("str","Qmax");
auto colDqq = ws->addColumn("str","dq/q");
auto colScale = ws->addColumn("double","Scale");
auto colStitch = ws->addColumn("int","StitchGroup");
auto colOptions = ws->addColumn("str","Options");
colRuns->setPlotType(0);
colTheta->setPlotType(0);
colTrans->setPlotType(0);
colQmin->setPlotType(0);
colQmax->setPlotType(0);
colDqq->setPlotType(0);
colScale->setPlotType(0);
colStitch->setPlotType(0);
colOptions->setPlotType(0);
return ws;
}
}
namespace MantidQt
{
namespace CustomInterfaces
{
Harry Jeffery
committed
ReflMainViewPresenter::ReflMainViewPresenter(ReflMainView* view):
m_view(view),
m_tableDirty(false),
Harry Jeffery
committed
m_addObserver(*this, &ReflMainViewPresenter::handleAddEvent),
m_remObserver(*this, &ReflMainViewPresenter::handleRemEvent),
m_clearObserver(*this, &ReflMainViewPresenter::handleClearEvent),
m_renameObserver(*this, &ReflMainViewPresenter::handleRenameEvent),
m_replaceObserver(*this, &ReflMainViewPresenter::handleReplaceEvent)
//Initialise options
initOptions();
//Set up the instrument selectors
std::vector<std::string> instruments;
instruments.push_back("INTER");
instruments.push_back("SURF");
instruments.push_back("CRISP");
instruments.push_back("POLREF");
//If the user's configured default instrument is in this list, set it as the default, otherwise use INTER
const std::string defaultInst = Mantid::Kernel::ConfigService::Instance().getString("default.instrument");
if(std::find(instruments.begin(), instruments.end(), defaultInst) != instruments.end())
m_view->setInstrumentList(instruments, defaultInst);
else
m_view->setInstrumentList(instruments, "INTER");
Harry Jeffery
committed
//Populate an initial list of valid tables to open, and subscribe to the ADS to keep it up to date
Mantid::API::AnalysisDataServiceImpl& ads = Mantid::API::AnalysisDataService::Instance();
std::set<std::string> items;
items = ads.getObjectNames();
for(auto it = items.begin(); it != items.end(); ++it )
{
const std::string name = *it;
Workspace_sptr ws = ads.retrieve(name);
if(isValidModel(ws))
m_workspaceList.insert(name);
}
ads.notificationCenter.addObserver(m_addObserver);
ads.notificationCenter.addObserver(m_remObserver);
ads.notificationCenter.addObserver(m_renameObserver);
ads.notificationCenter.addObserver(m_clearObserver);
ads.notificationCenter.addObserver(m_replaceObserver);
if(m_view)
m_view->setTableList(m_workspaceList);
//Provide autocompletion hints for the options column. We use the algorithm's properties minus
//those we blacklist. We blacklist any useless properties or ones we're handling that the user
//should'nt touch.
IAlgorithm_sptr alg = AlgorithmManager::Instance().create("ReflectometryReductionOneAuto");
std::set<std::string> blacklist;
blacklist.insert("ThetaIn");
blacklist.insert("ThetaOut");
blacklist.insert("InputWorkspace");
blacklist.insert("OutputWorkspace");
blacklist.insert("OutputWorkspaceWavelength");
blacklist.insert("FirstTransmissionRun");
blacklist.insert("SecondTransmissionRun");
m_view->setOptionsHintStrategy(new AlgorithmHintStrategy(alg, blacklist));
Harry Jeffery
committed
//Start with a blank table
newTable();
ReflMainViewPresenter::~ReflMainViewPresenter()
{
Harry Jeffery
committed
Mantid::API::AnalysisDataServiceImpl& ads = Mantid::API::AnalysisDataService::Instance();
ads.notificationCenter.removeObserver(m_addObserver);
ads.notificationCenter.removeObserver(m_remObserver);
ads.notificationCenter.removeObserver(m_clearObserver);
ads.notificationCenter.removeObserver(m_renameObserver);
ads.notificationCenter.removeObserver(m_replaceObserver);
/**
* Finds the first unused group id
*/
int ReflMainViewPresenter::getUnusedGroup(std::set<size_t> ignoredRows) const
std::set<int> usedGroups;
//Scan through all the rows, working out which group ids are used
for(size_t idx = 0; idx < m_model->rowCount(); ++idx)
{
if(ignoredRows.find(idx) != ignoredRows.end())
continue;
//This is an unselected row. Add it to the list of used group ids
usedGroups.insert(m_model->Int(idx, COL_GROUP));
}
int groupId = 0;
//While the group id is one of the used ones, increment it by 1
while(usedGroups.find(groupId) != usedGroups.end())
groupId++;
return groupId;
}
/**
Parses a string in the format `a = 1,b=2, c = "1,2,3,4", d = 5.0, e='a,b,c'` into a map of key/value pairs
@throws std::runtime_error on an invalid input string
std::map<std::string,std::string> ReflMainViewPresenter::parseKeyValueString(const std::string& str)
{
//Tokenise, using '\' as an escape character, ',' as a delimiter and " and ' as quote characters
boost::tokenizer<boost::escaped_list_separator<char> > tok(str, boost::escaped_list_separator<char>("\\", ",", "\"'"));
std::map<std::string,std::string> kvp;
for(auto it = tok.begin(); it != tok.end(); ++it)
{
std::vector<std::string> valVec;
boost::split(valVec, *it, boost::is_any_of("="));
if(valVec.size() > 1)
{
//We split on all '='s. The first delimits the key, the rest are assumed to be part of the value
std::string key = valVec[0];
//Drop the key from the values vector
valVec.erase(valVec.begin());
//Join the remaining sections,
std::string value = boost::algorithm::join(valVec, "=");
//Remove any unwanted whitespace
boost::trim(key);
boost::trim(value);
if(key.empty() || value.empty())
throw std::runtime_error("Invalid key value pair, '" + *it + "'");
else
{
throw std::runtime_error("Invalid key value pair, '" + *it + "'");
}
}
return kvp;
}
/**
Process selected rows
*/
void ReflMainViewPresenter::process()
{
if(m_model->rowCount() == 0)
{
m_view->giveUserWarning("Cannot process an empty Table","Warning");
return;
}
std::set<size_t> rows = m_view->getSelectedRows();
if(m_options["WarnProcessAll"].toBool())
{
//Does the user want to abort?
if(!m_view->askUserYesNo("This will process all rows in the table. Continue?","Process all rows?"))
return;
}
//They want to process all rows, so populate rows with every index in the model
for(size_t idx = 0; idx < m_model->rowCount(); ++idx)
//Map group numbers to the set of rows in that group we want to process
std::map<int,std::set<size_t> > groups;
for(auto it = rows.begin(); it != rows.end(); ++it)
groups[m_model->Int(*it, COL_GROUP)].insert(*it);
//Check each group and warn if we're only partially processing it
for(auto gIt = groups.begin(); gIt != groups.end(); ++gIt)
{
const int& groupId = gIt->first;
const std::set<size_t>& groupRows = gIt->second;
//Are we only partially processing a group?
if(groupRows.size() < numRowsInGroup(gIt->first))
{
std::stringstream err;
err << "You have only selected " << groupRows.size() << " of the ";
err << numRowsInGroup(groupId) << " rows in group " << groupId << ".";
err << " Are you sure you want to continue?";
if(!m_view->askUserYesNo(err.str(), "Continue Processing?"))
return;
}
}
//Validate the rows
for(auto it = rows.begin(); it != rows.end(); ++it)
}
catch(std::exception& ex)
{
const std::string rowNo = Mantid::Kernel::Strings::toString<size_t>(*it + 1);
m_view->giveUserCritical("Error found in row " + rowNo + ":\n" + ex.what(), "Error");
return;
}
//Each group and each row within count as a progress step.
const int maxProgress = (int)(rows.size() + groups.size());
m_view->setProgressRange(progress, maxProgress);
m_view->setProgress(progress);
for(auto gIt = groups.begin(); gIt != groups.end(); ++gIt)
const std::set<size_t> groupRows = gIt->second;
for(auto rIt = groupRows.begin(); rIt != groupRows.end(); ++rIt)
{
try
{
m_view->setProgress(++progress);
}
catch(std::exception& ex)
{
const std::string rowNo = Mantid::Kernel::Strings::toString<size_t>(*rIt + 1);
const std::string message = "Error encountered while processing row " + rowNo + ":\n";
m_view->giveUserCritical(message + ex.what(), "Error");
m_view->setProgress(0);
return;
}
}
stitchRows(groupRows);
m_view->setProgress(++progress);
}
catch(std::exception& ex)
{
const std::string groupNo = Mantid::Kernel::Strings::toString<int>(gIt->first);
const std::string message = "Error encountered while stitching group " + groupNo + ":\n";
m_view->giveUserCritical(message + ex.what(), "Error");
Validate a row.
If a row passes validation, it is ready to be autofilled, but
not necessarily ready for processing.
@param rowNo : The row in the model to validate
@throws std::invalid_argument if the row fails validation
*/
void ReflMainViewPresenter::validateRow(size_t rowNo) const
if(rowNo >= m_model->rowCount())
throw std::invalid_argument("Invalid row");
if(m_model->String(rowNo, COL_RUNS).empty())
throw std::invalid_argument("Run column may not be empty.");
}
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
/**
Autofill a row
@param rowNo : The row in the model to autofill
@throws std::runtime_error if the row could not be auto-filled
*/
void ReflMainViewPresenter::autofillRow(size_t rowNo)
{
if(rowNo >= m_model->rowCount())
throw std::runtime_error("Invalid row");
const std::string runStr = m_model->String(rowNo, COL_RUNS);
MatrixWorkspace_sptr run = boost::dynamic_pointer_cast<MatrixWorkspace>(loadRun(runStr, m_view->getProcessInstrument()));
//Fetch two theta from the log if needed
if(m_model->String(rowNo, COL_ANGLE).empty())
{
Property* logData = NULL;
//First try TwoTheta
try
{
logData = run->mutableRun().getLogData("Theta");
}
catch(std::exception&)
{
throw std::runtime_error("Value for two theta could not be found in log.");
}
auto logPWV = dynamic_cast<const PropertyWithValue<double>*>(logData);
auto logTSP = dynamic_cast<const TimeSeriesProperty<double>*>(logData);
double thetaVal;
if(logPWV)
thetaVal = *logPWV;
else if(logTSP && logTSP->realSize() > 0)
thetaVal = logTSP->lastValue();
else
throw std::runtime_error("Value for two theta could not be found in log.");
//Update the model
if(m_options["RoundAngle"].toBool())
thetaVal = Utils::roundToDP(thetaVal, m_options["RoundAnglePrecision"].toInt());
m_model->String(rowNo, COL_ANGLE) = Strings::toString<double>(thetaVal);
}
//If we need to calculate the resolution, do.
if(m_model->String(rowNo, COL_DQQ).empty())
{
IAlgorithm_sptr calcResAlg = AlgorithmManager::Instance().create("CalculateResolution");
calcResAlg->setProperty("Workspace", run);
calcResAlg->setProperty("TwoTheta", m_model->String(rowNo, COL_ANGLE));
calcResAlg->execute();
//Update the model
double dqqVal = calcResAlg->getProperty("Resolution");
if(m_options["RoundDQQ"].toBool())
dqqVal = Utils::roundToDP(dqqVal, m_options["RoundDQQPrecision"].toInt());
m_model->String(rowNo, COL_DQQ) = Strings::toString<double>(dqqVal);
}
//Make sure the view updates
m_view->showTable(m_model);
}
/**
Extracts the run number of a workspace
@param ws : The workspace to fetch the run number from
@returns The run number of the workspace
*/
std::string ReflMainViewPresenter::getRunNumber(const Workspace_sptr& ws)
{
//If we can, use the run number from the workspace's sample log
MatrixWorkspace_sptr mws = boost::dynamic_pointer_cast<MatrixWorkspace>(ws);
if(mws)
{
try
{
const Property* runProperty = mws->mutableRun().getLogData("run_number");
auto runNumber = dynamic_cast<const PropertyWithValue<std::string>*>(runProperty);
if(runNumber)
return *runNumber;
}
catch(Mantid::Kernel::Exception::NotFoundError&)
{
//We'll just fall back to looking at the workspace's name
}
}
//Okay, let's see what we can get from the workspace's name
const std::string wsName = ws->name();
//Matches TOF_13460 -> 13460
boost::regex outputRegex("(TOF|IvsQ|IvsLam)_([0-9]+)");
//Matches INTER13460 -> 13460
boost::regex instrumentRegex("[a-zA-Z]{3,}([0-9]{3,})");
boost::smatch matches;
if(boost::regex_match(wsName, matches, outputRegex))
{
return matches[2].str();
}
else if(boost::regex_match(wsName, matches, instrumentRegex))
{
return matches[1].str();
}
//Resort to using the workspace name
return wsName;
}
Loads a run from disk or fetches it from the AnalysisDataService
@param run : The name of the run
@param instrument : The instrument the run belongs to
@throws std::runtime_error if the run could not be loaded
@returns a shared pointer to the workspace
*/
Workspace_sptr ReflMainViewPresenter::loadRun(const std::string& run, const std::string& instrument = "")
{
//First, let's see if the run given is the name of a workspace in the ADS
if(AnalysisDataService::Instance().doesExist(run))
return AnalysisDataService::Instance().retrieveWS<Workspace>(run);
//Is the run string is numeric
if(boost::regex_match(run, boost::regex("\\d+")))
{
std::string wsName;
//Look "TOF_<run_number>" in the ADS
wsName = "TOF_" + run;
if(AnalysisDataService::Instance().doesExist(wsName))
return AnalysisDataService::Instance().retrieveWS<Workspace>(wsName);
//Look for "<instrument><run_number>" in the ADS
wsName = instrument + run;
if(AnalysisDataService::Instance().doesExist(wsName))
return AnalysisDataService::Instance().retrieveWS<Workspace>(wsName);
}
//We'll just have to load it ourselves
const std::string filename = instrument + run;
IAlgorithm_sptr algLoadRun = AlgorithmManager::Instance().create("Load");
algLoadRun->initialize();
algLoadRun->setProperty("Filename", filename);
algLoadRun->setProperty("OutputWorkspace", "TOF_" + run);
algLoadRun->execute();
if(!algLoadRun->isExecuted())
throw std::runtime_error("Could not open " + filename);
return AnalysisDataService::Instance().retrieveWS<Workspace>("TOF_" + run);
Reduce a row
@param rowNo : The row in the model to reduce
@throws std::runtime_error if reduction fails
void ReflMainViewPresenter::reduceRow(size_t rowNo)
const std::string run = m_model->String(rowNo, COL_RUNS);
const std::string transStr = m_model->String(rowNo, COL_TRANSMISSION);
const std::string options = m_model->String(rowNo, COL_OPTIONS);
double theta = 0;
const bool thetaGiven = !m_model->String(rowNo, COL_ANGLE).empty();
if(thetaGiven)
Mantid::Kernel::Strings::convert<double>(m_model->String(rowNo, COL_ANGLE), theta);
Workspace_sptr runWS = loadRun(run, m_view->getProcessInstrument());
const std::string runNo = getRunNumber(runWS);
MatrixWorkspace_sptr transWS;
if(!transStr.empty())
transWS = makeTransWS(transStr);
IAlgorithm_sptr algReflOne = AlgorithmManager::Instance().create("ReflectometryReductionOneAuto");
algReflOne->initialize();
algReflOne->setProperty("InputWorkspace", runWS);
if(transWS)
algReflOne->setProperty("FirstTransmissionRun", transWS);
algReflOne->setProperty("OutputWorkspace", "IvsQ_" + runNo);
algReflOne->setProperty("OutputWorkspaceWaveLength", "IvsLam_" + runNo);
algReflOne->setProperty("ThetaIn", theta);
//Parse and set any user-specified options
auto optionsMap = parseKeyValueString(options);
for(auto kvp = optionsMap.begin(); kvp != optionsMap.end(); ++kvp)
{
try
{
algReflOne->setProperty(kvp->first, kvp->second);
}
catch(Mantid::Kernel::Exception::NotFoundError&)
{
throw std::runtime_error("Invalid property in options column: " + kvp->first);
}
}
algReflOne->execute();
if(!algReflOne->isExecuted())
throw std::runtime_error("Failed to run ReflectometryReductionOneAuto.");
const double scale = m_model->Double(rowNo, COL_SCALE);
if(scale != 1.0)
{
IAlgorithm_sptr algScale = AlgorithmManager::Instance().create("Scale");
algScale->initialize();
algScale->setProperty("InputWorkspace", "IvsQ_" + runNo);
algScale->setProperty("OutputWorkspace", "IvsQ_" + runNo);
algScale->setProperty("Factor", 1.0 / scale);
algScale->execute();
if(!algScale->isExecuted())
throw std::runtime_error("Failed to run Scale algorithm");
}
//Reduction has completed. Put Qmin and Qmax into the table if needed, for stitching.
if(m_model->String(rowNo, COL_QMIN).empty() || m_model->String(rowNo, COL_QMAX).empty())
{
MatrixWorkspace_sptr ws = AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>("IvsQ_" + runNo);
std::vector<double> qrange = calcQRange(ws, theta);
if(m_model->String(rowNo, COL_QMIN).empty())
m_model->String(rowNo, COL_QMIN) = Strings::toString<double>(qrange[0]);
if(m_model->String(rowNo, COL_QMAX).empty())
m_model->String(rowNo, COL_QMAX) = Strings::toString<double>(qrange[1]);
m_view->showTable(m_model);
}
}
/**
Calculates the minimum and maximum values for Q
@param ws : The workspace to fetch the instrument values from
@param theta : The value of two theta to use in calculations
*/
std::vector<double> ReflMainViewPresenter::calcQRange(MatrixWorkspace_sptr ws, double theta)
{
double lmin, lmax;
try
{
const Instrument_const_sptr instrument = ws->getInstrument();
lmin = instrument->getNumberParameter("LambdaMin")[0];
lmax = instrument->getNumberParameter("LambdaMax")[0];
}
catch(std::exception&)
{
throw std::runtime_error("LambdaMin/LambdaMax instrument parameters are required to calculate qmin/qmax");
}
double qmin = 4 * M_PI / lmax * sin(theta * M_PI / 180.0);
double qmax = 4 * M_PI / lmin * sin(theta * M_PI / 180.0);
if(m_options["RoundQMin"].toBool())
qmin = Utils::roundToDP(qmin, m_options["RoundQMinPrecision"].toInt());
if(m_options["RoundQMax"].toBool())
qmax = Utils::roundToDP(qmax, m_options["RoundQMaxPrecision"].toInt());
std::vector<double> ret;
ret.push_back(qmin);
ret.push_back(qmax);
return ret;
/**
Stitches the workspaces created by the given rows together.
@param rows : the list of rows
*/
void ReflMainViewPresenter::stitchRows(std::set<size_t> rows)
{
//If we can get away with doing nothing, do.
if(rows.size() < 2)
return;
//Properties for Stitch1DMany
std::vector<std::string> workspaceNames;
std::vector<std::string> runs;
std::vector<double> params;
std::vector<double> startOverlaps;
std::vector<double> endOverlaps;
//Go through each row and prepare the properties
for(auto rowIt = rows.begin(); rowIt != rows.end(); ++rowIt)
{
const std::string runStr = m_model->String(*rowIt, COL_RUNS);
const std::string qMinStr = m_model->String(*rowIt, COL_QMIN);
const std::string qMaxStr = m_model->String(*rowIt, COL_QMAX);
double qmin, qmax;
Mantid::Kernel::Strings::convert<double>(qMinStr, qmin);
Mantid::Kernel::Strings::convert<double>(qMaxStr, qmax);
Workspace_sptr runWS = loadRun(runStr);
if(runWS)
{
const std::string runNo = getRunNumber(runWS);
if(AnalysisDataService::Instance().doesExist("IvsQ_" + runNo))
{
runs.push_back(runNo);
workspaceNames.push_back("IvsQ_" + runNo);
}
}
startOverlaps.push_back(qmin);
endOverlaps.push_back(qmax);
}
double dqq;
std::string dqqStr = m_model->String(*(rows.begin()), COL_DQQ);
Mantid::Kernel::Strings::convert<double>(dqqStr, dqq);
//params are qmin, -dqq, qmax for the final output
params.push_back(*std::min_element(startOverlaps.begin(), startOverlaps.end()));
params.push_back(-dqq);
params.push_back(*std::max_element(endOverlaps.begin(), endOverlaps.end()));
//startOverlaps and endOverlaps need to be slightly offset from each other
//See usage examples of Stitch1DMany to see why we discard first qmin and last qmax
startOverlaps.erase(startOverlaps.begin());
endOverlaps.pop_back();
std::string outputWSName = "IvsQ_" + boost::algorithm::join(runs, "_");
IAlgorithm_sptr algStitch = AlgorithmManager::Instance().create("Stitch1DMany");
algStitch->initialize();
algStitch->setProperty("InputWorkspaces", workspaceNames);
algStitch->setProperty("OutputWorkspace", outputWSName);
algStitch->setProperty("Params", params);
algStitch->setProperty("StartOverlaps", startOverlaps);
algStitch->setProperty("EndOverlaps", endOverlaps);
algStitch->execute();
if(!algStitch->isExecuted())
throw std::runtime_error("Failed to run Stitch1DMany on IvsQ workspaces.");
@param transString : the numbers of the transmission runs to use
*/
MatrixWorkspace_sptr ReflMainViewPresenter::makeTransWS(const std::string& transString)
const size_t maxTransWS = 2;
std::vector<std::string> transVec;
//Take the first two run numbers
boost::split(transVec, transString, boost::is_any_of(","));
if(transVec.size() > maxTransWS)
transVec.resize(maxTransWS);
if(transVec.size() == 0)
throw std::runtime_error("Failed to parse the transmission run list.");
for(auto it = transVec.begin(); it != transVec.end(); ++it)
transWSVec.push_back(loadRun(*it, m_view->getProcessInstrument()));
//If the transmission workspace is already in the ADS, re-use it
std::string lastName = "TRANS_" + boost::algorithm::join(transVec, "_");
if(AnalysisDataService::Instance().doesExist(lastName))
return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(lastName);
//We have the runs, so we can create a TransWS
IAlgorithm_sptr algCreateTrans = AlgorithmManager::Instance().create("CreateTransmissionWorkspaceAuto");
algCreateTrans->initialize();
algCreateTrans->setProperty("FirstTransmissionRun", transWSVec[0]->name());
if(transWSVec.size() > 1)
algCreateTrans->setProperty("SecondTransmissionRun", transWSVec[1]->name());
std::string wsName = "TRANS_" + getRunNumber(transWSVec[0]);
wsName += "_" + getRunNumber(transWSVec[1]);
algCreateTrans->setProperty("OutputWorkspace", wsName);
if(!algCreateTrans->isInitialized())
throw std::runtime_error("Could not initialize CreateTransmissionWorkspaceAuto");
algCreateTrans->execute();
if(!algCreateTrans->isExecuted())
throw std::runtime_error("CreateTransmissionWorkspaceAuto failed to execute");
return AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(wsName);
Inserts a new row in the specified location
@param before The index to insert the new row before
void ReflMainViewPresenter::insertRow(size_t before)
const int groupId = getUnusedGroup();
size_t row = m_model->insertRow(before);
//Set the default scale to 1.0
m_model->Double(row, COL_SCALE) = 1.0;
//Set the group id of the new row
m_model->Int(row, COL_GROUP) = groupId;
//Make sure the view updates
m_view->showTable(m_model);
}
Insert a row after the last selected row
void ReflMainViewPresenter::appendRow()
std::set<size_t> rows = m_view->getSelectedRows();
insertRow(m_model->rowCount());
else
insertRow(*rows.rbegin() + 1);
/**
Insert a row before the first selected row
*/
void ReflMainViewPresenter::prependRow()
{
std::set<size_t> rows = m_view->getSelectedRows();
if(rows.empty())
insertRow(0);
else
insertRow(*rows.begin());
m_tableDirty = true;
}
/**
Delete row(s) from the model
*/
void ReflMainViewPresenter::deleteRow()
{
std::set<size_t> rows = m_view->getSelectedRows();
for(auto row = rows.rbegin(); row != rows.rend(); ++row)
m_model->removeRow(*row);
/**
Group rows together
*/
void ReflMainViewPresenter::groupRows()
{
const std::set<size_t> rows = m_view->getSelectedRows();
//Find the first unused group id, ignoring the selected rows
const int groupId = getUnusedGroup(rows);
//Now we just have to set the group id on the selected rows
for(auto it = rows.begin(); it != rows.end(); ++it)
m_model->Int(*it, COL_GROUP) = groupId;
/**
Used by the view to tell the presenter something has changed
*/
void ReflMainViewPresenter::notify(int flag)
case ReflMainView::SaveAsFlag: saveTableAs(); break;
case ReflMainView::SaveFlag: saveTable(); break;
case ReflMainView::AppendRowFlag: appendRow(); break;
case ReflMainView::PrependRowFlag: prependRow(); break;
case ReflMainView::DeleteRowFlag: deleteRow(); break;
case ReflMainView::ProcessFlag: process(); break;
case ReflMainView::GroupRowsFlag: groupRows(); break;
case ReflMainView::OpenTableFlag: openTable(); break;
case ReflMainView::NewTableFlag: newTable(); break;
case ReflMainView::TableUpdatedFlag: m_tableDirty = true; break;
case ReflMainView::ExpandSelectionFlag: expandSelection(); break;
case ReflMainView::OptionsDialogFlag: showOptionsDialog(); break;
case ReflMainView::NoFlags: return;
//Not having a 'default' case is deliberate. gcc issues a warning if there's a flag we aren't handling.
Press changes to the same item in the ADS
*/
void ReflMainViewPresenter::saveTable()
{
if(!m_wsName.empty())
AnalysisDataService::Instance().addOrReplace(m_wsName,boost::shared_ptr<ITableWorkspace>(m_model->clone()));
m_tableDirty = false;
}
saveTableAs();
}
/**
Press changes to a new item in the ADS
void ReflMainViewPresenter::saveTableAs()
const std::string userString = m_view->askUserString("Save As", "Enter a workspace name:", "Workspace");
if(!userString.empty())
{
m_wsName = userString;
saveTable();
}
}
/**
Start a new, untitled table
*/
void ReflMainViewPresenter::newTable()
{
if(m_tableDirty && m_options["WarnDiscardChanges"].toBool())
if(!m_view->askUserYesNo("Your current table has unsaved changes. Are you sure you want to discard them?","Start New Table?"))
return;
m_model = createWorkspace();
m_wsName.clear();
m_view->showTable(m_model);
m_tableDirty = false;
}
/**
Open a table from the ADS
*/
void ReflMainViewPresenter::openTable()
{
if(m_tableDirty && m_options["WarnDiscardChanges"].toBool())
if(!m_view->askUserYesNo("Your current table has unsaved changes. Are you sure you want to discard them?","Open Table?"))
return;
auto& ads = AnalysisDataService::Instance();
const std::string toOpen = m_view->getWorkspaceToOpen();
if(toOpen.empty())
return;
if(!ads.isValid(toOpen).empty())
{
m_view->giveUserCritical("Could not open workspace: " + toOpen, "Error");
return;
}
Harry Jeffery
committed
ITableWorkspace_sptr origTable = AnalysisDataService::Instance().retrieveWS<ITableWorkspace>(toOpen);
//We create a clone of the table for live editing. The original is not updated unless we explicitly save.
ITableWorkspace_sptr newModel = boost::shared_ptr<ITableWorkspace>(origTable->clone());
Harry Jeffery
committed
validateModel(newModel);
m_model = newModel;
m_wsName = toOpen;
m_view->showTable(m_model);
m_tableDirty = false;
}
catch(std::runtime_error& e)
{
Harry Jeffery
committed
m_view->giveUserCritical("Could not open workspace: " + std::string(e.what()), "Error");
}
Harry Jeffery
committed
}
Harry Jeffery
committed
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
/**
Handle ADS add events
*/
void ReflMainViewPresenter::handleAddEvent(Mantid::API::WorkspaceAddNotification_ptr pNf)
{
const std::string name = pNf->objectName();
if(Mantid::API::AnalysisDataService::Instance().isHiddenDataServiceObject(name))
return;
if(!isValidModel(pNf->object()))
return;
m_workspaceList.insert(name);
if(m_view)
m_view->setTableList(m_workspaceList);
}
/**
Handle ADS remove events
*/
void ReflMainViewPresenter::handleRemEvent(Mantid::API::WorkspacePostDeleteNotification_ptr pNf)
{
const std::string name = pNf->objectName();
m_workspaceList.erase(name);
if(m_view)
m_view->setTableList(m_workspaceList);
}
/**
Handle ADS clear events
*/
void ReflMainViewPresenter::handleClearEvent(Mantid::API::ClearADSNotification_ptr)
{
m_workspaceList.clear();
if(m_view)
m_view->setTableList(m_workspaceList);
}
/**
Handle ADS rename events
*/
void ReflMainViewPresenter::handleRenameEvent(Mantid::API::WorkspaceRenameNotification_ptr pNf)
{
//If we have this workspace, rename it
const std::string name = pNf->objectName();