-
Doucet, Mathieu authoredDoucet, Mathieu authored
Code owners
Assign users and groups as approvers for specific file changes. Learn more.
SANSRunWindow.cpp 82.14 KiB
//----------------------
// Includes
//----------------------
#include "MantidQtCustomInterfaces/SANSRunWindow.h"
#include "MantidQtCustomInterfaces/SANSUtilityDialogs.h"
#include "MantidQtCustomInterfaces/SANSAddFiles.h"
#include "MantidKernel/ConfigService.h"
#include "MantidKernel/Logger.h"
#include "MantidKernel/Exception.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/IAlgorithm.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidAPI/AnalysisDataService.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidAPI/IInstrument.h"
#include "MantidAPI/SpectraDetectorMap.h"
#include "MantidGeometry/IComponent.h"
#include "MantidGeometry/V3D.h"
#include "MantidKernel/Exception.h"
#include <QLineEdit>
#include <QFileDialog>
#include <QHash>
#include <QTextStream>
#include <QTreeWidgetItem>
#include <QMessageBox>
#include <QInputDialog>
#include <QSignalMapper>
#include <QHeaderView>
#include <QApplication>
#include <QClipboard>
#include <QTemporaryFile>
#include <QDateTime>
#include "boost/lexical_cast.hpp"
//Add this class to the list of specialised dialogs in this namespace
namespace MantidQt
{
namespace CustomInterfaces
{
DECLARE_SUBWINDOW(SANSRunWindow);
}
}
using namespace MantidQt::MantidWidgets;
using namespace MantidQt::CustomInterfaces;
using namespace Mantid::Kernel;
using namespace Mantid::API;
// Initialize the logger
Logger& SANSRunWindow::g_log = Logger::get("SANSRunWindow");
//----------------------------------------------
// Public member functions
//----------------------------------------------
///Constructor
SANSRunWindow::SANSRunWindow(QWidget *parent) :
UserSubWindow(parent), m_addFilesTab(NULL), m_saveWorkspaces(NULL),
m_data_dir(""), m_ins_defdir(""), m_last_dir(""),
m_cfg_loaded(true), m_userFname(false), m_sample_no(), m_run_no_boxes(),
m_period_lbls(), m_warnings_issued(false), m_force_reload(false),
m_log_warnings(false),
m_delete_observer(*this, &SANSRunWindow::handleMantidDeleteWorkspace),
m_s2d_detlabels(), m_loq_detlabels(), m_allowed_batchtags(), m_lastreducetype(-1),
m_have_reducemodule(false), m_dirty_batch_grid(false), m_tmp_batchfile("")
{
}
///Destructor
SANSRunWindow::~SANSRunWindow()
{
if( isInitialized() )
{
// Seems to crash on destruction of if I don't do this
AnalysisDataService::Instance().notificationCenter.removeObserver(m_delete_observer);
saveSettings();
delete m_addFilesTab;
}
}
//--------------------------------------------
// Private member functions
//--------------------------------------------
/**
* Set up the dialog layout
*/
void SANSRunWindow::initLayout()
{
g_log.debug("Initializing interface layout");
m_uiForm.setupUi(this);
m_reducemapper = new QSignalMapper(this);
m_mode_mapper = new QSignalMapper(this);
//Set column stretch on the mask table
m_uiForm.mask_table->horizontalHeader()->setStretchLastSection(true);
setupSaveBox();
connectButtonSignals();
// Disable most things so that load is the only thing that can be done
m_uiForm.oneDBtn->setEnabled(false);
m_uiForm.twoDBtn->setEnabled(false);
m_uiForm.saveDefault_btn->setEnabled(false);
for( int i = 1; i < 4; ++i)
{
m_uiForm.tabWidget->setTabEnabled(i, false);
}
//Mode switches
connect(m_uiForm.single_mode_btn, SIGNAL(clicked()), m_mode_mapper, SLOT(map()));
m_mode_mapper->setMapping(m_uiForm.single_mode_btn, SANSRunWindow::SingleMode);
connect(m_uiForm.batch_mode_btn, SIGNAL(clicked()), m_mode_mapper, SLOT(map()));
m_mode_mapper->setMapping(m_uiForm.batch_mode_btn, SANSRunWindow::BatchMode);
connect(m_mode_mapper, SIGNAL(mapped(int)), this, SLOT(switchMode(int)));
//Set a custom context menu for the batch table
m_uiForm.batch_table->setContextMenuPolicy(Qt::ActionsContextMenu);
m_batch_paste = new QAction(tr("&Paste"),m_uiForm.batch_table);
m_batch_paste->setShortcut(tr("Ctrl+P"));
connect(m_batch_paste, SIGNAL(activated()), this, SLOT(pasteToBatchTable()));
m_uiForm.batch_table->addAction(m_batch_paste);
m_batch_clear = new QAction(tr("&Clear"),m_uiForm.batch_table);
m_uiForm.batch_table->addAction(m_batch_clear);
connect(m_batch_clear, SIGNAL(activated()), this, SLOT(clearBatchTable()));
//Logging
connect(this, SIGNAL(logMessageReceived(const QString&)), this, SLOT(updateLogWindow(const QString&)));
connect(m_uiForm.logger_clear, SIGNAL(clicked()), this, SLOT(clearLogger()));
m_uiForm.logging_field->ensureCursorVisible();
connect(m_uiForm.verbose_check, SIGNAL(stateChanged(int)), this, SLOT(verboseMode(int)));
//Create the widget hash maps
initWidgetMaps();
connectChangeSignals();
// Add Python set functions as underlying data
m_uiForm.inst_opt->setItemData(0, "LOQ()");
m_uiForm.inst_opt->setItemData(1, "SANS2D()");
//Add shortened forms of step types to step boxes
m_uiForm.wav_dw_opt->setItemData(0, "LIN");
m_uiForm.wav_dw_opt->setItemData(1, "LOG");
m_uiForm.q_dq_opt->setItemData(0, "LIN");
m_uiForm.q_dq_opt->setItemData(1, "LOG");
m_uiForm.qy_dqy_opt->setItemData(0, "LIN");
m_uiForm.trans_opt->setItemData(0,"Log");
m_uiForm.trans_opt->setItemData(1,"Linear");
m_uiForm.trans_opt->setItemData(2,"Off");
if( ! m_addFilesTab )
{//sets up the AddFiles tab which must be deleted in the destructor
m_addFilesTab = new SANSAddFiles(this, &m_uiForm);
}
//List for Workspace delete signals
AnalysisDataService::Instance().notificationCenter.addObserver(m_delete_observer);
readSettings();
}
/**
* Run local Python initialization code
*/
void SANSRunWindow::initLocalPython()
{
// Import the SANS module and set the correct instrument
QString result = runPythonCode("try:\n\tfrom SANSReduction import *\nexcept (ImportError,SyntaxError), details:\tprint 'Error importing SANSReduction: ' + str(details)");
if( result.trimmed().isEmpty() )
{
m_have_reducemodule = true;
}
else
{
showInformationBox(result);
m_have_reducemodule = false;
setProcessingState(true, -1);
}
}
/** Initialise some of the data and signal connections in the save box
*/
void SANSRunWindow::setupSaveBox()
{
connect(m_uiForm.saveDefault_btn, SIGNAL(clicked()), this, SLOT(handleDefSaveClick()));
connect(m_uiForm.saveSel_btn, SIGNAL(clicked()),
this, SLOT(saveWorkspacesDialog()));
connect(m_uiForm.saveFilename_btn, SIGNAL(clicked()),
this, SLOT(saveFileBrowse()));
connect(m_uiForm.outfile_edit, SIGNAL(textEdited(const QString &)),
this, SLOT(setUserFname()));
//link the save option tick boxes to their save algorithm
m_savFormats.insert(m_uiForm.saveNex_check, "SaveNexus");
m_savFormats.insert(m_uiForm.saveCan_check, "SaveCanSAS1D");
m_savFormats.insert(m_uiForm.saveRKH_check, "SaveRKH");
m_savFormats.insert(m_uiForm.saveCSV_check, "SaveCSV");
for(SavFormatsConstIt i=m_savFormats.begin(); i != m_savFormats.end(); ++i)
{
connect(i.key(), SIGNAL(stateChanged(int)),
this, SLOT(enableOrDisableDefaultSave()));
}
}
/** Raises a saveWorkspaces dialog which allows people to save any workspace or
* workspaces the user chooses
*/
void SANSRunWindow::saveWorkspacesDialog()
{
//this dialog must have delete on close selected to aviod a memory leak
m_saveWorkspaces =
new SaveWorkspaces(this, m_uiForm.outfile_edit->text(), m_savFormats);
//this dialog sometimes needs to run Python, pass this to Mantidplot via our runAsPythonScript() signal
connect(m_saveWorkspaces, SIGNAL(runAsPythonScript(const QString&)),
this, SIGNAL(runAsPythonScript(const QString&)));
//we need know if we have a pointer to a valid window or not
connect(m_saveWorkspaces, SIGNAL(closing()),
this, SLOT(saveWorkspacesClosed()));
m_uiForm.saveSel_btn->setEnabled(false);
m_saveWorkspaces->show();
}
/**When the save workspaces dialog box is closes its pointer, m_saveWorkspaces,
* is set to NULL and the raise dialog button is re-enabled
*/
void SANSRunWindow::saveWorkspacesClosed()
{
m_uiForm.saveSel_btn->setEnabled(true);
m_saveWorkspaces = NULL;
}
/** Connection the buttons to their signals
*/
void SANSRunWindow::connectButtonSignals()
{
connect(m_uiForm.data_dirBtn, SIGNAL(clicked()), this, SLOT(selectDataDir()));
connect(m_uiForm.userfileBtn, SIGNAL(clicked()), this, SLOT(selectUserFile()));
connect(m_uiForm.csv_browse_btn,SIGNAL(clicked()), this, SLOT(selectCSVFile()));
connect(m_uiForm.load_dataBtn, SIGNAL(clicked()), this, SLOT(handleLoadButtonClick()));
connect(m_uiForm.runcentreBtn, SIGNAL(clicked()), this, SLOT(handleRunFindCentre()));
// Reduction buttons
connect(m_uiForm.oneDBtn, SIGNAL(clicked()), m_reducemapper, SLOT(map()));
m_reducemapper->setMapping(m_uiForm.oneDBtn, "1D");
connect(m_uiForm.twoDBtn, SIGNAL(clicked()), m_reducemapper, SLOT(map()));
m_reducemapper->setMapping(m_uiForm.twoDBtn, "2D");
connect(m_reducemapper, SIGNAL(mapped(const QString &)), this, SLOT(handleReduceButtonClick(const QString &)));
connect(m_uiForm.showMaskBtn, SIGNAL(clicked()), this, SLOT(handleShowMaskButtonClick()));
connect(m_uiForm.clear_log, SIGNAL(clicked()), m_uiForm.centre_logging, SLOT(clear()));
}
/** Connect signals from the textChanged() signal from text boxes, index changed
* on ComboBoxes etc.
*/
void SANSRunWindow::connectChangeSignals()
{
//Connect each box's edited signal to flag if the box's text has changed
for( int idx = 0; idx < 9; ++idx )
{
connect(m_run_no_boxes.value(idx), SIGNAL(textEdited(const QString&)), this, SLOT(runChanged()));
}
connect(m_uiForm.smpl_offset, SIGNAL(textEdited(const QString&)), this, SLOT(runChanged()));
connect(m_uiForm.outfile_edit, SIGNAL(textEdited(const QString&)),
this, SLOT(enableOrDisableDefaultSave()));
// Combo boxes
connect(m_uiForm.wav_dw_opt, SIGNAL(currentIndexChanged(int)), this,
SLOT(handleStepComboChange(int)));
connect(m_uiForm.q_dq_opt, SIGNAL(currentIndexChanged(int)), this,
SLOT(handleStepComboChange(int)));
connect(m_uiForm.qy_dqy_opt, SIGNAL(currentIndexChanged(int)), this,
SLOT(handleStepComboChange(int)));
connect(m_uiForm.inst_opt, SIGNAL(currentIndexChanged(int)), this,
SLOT(handleInstrumentChange(int)));
// Default transmission switch
connect(m_uiForm.def_trans, SIGNAL(stateChanged(int)), this, SLOT(updateTransInfo(int)));
}
/**
* Initialize the widget maps
*/
void SANSRunWindow::initWidgetMaps()
{
// single run mode settings
//Text edit map
m_run_no_boxes.insert(0, m_uiForm.sct_sample_edit);
m_run_no_boxes.insert(1, m_uiForm.sct_can_edit);
m_run_no_boxes.insert(2, m_uiForm.sct_bkgd_edit);
m_run_no_boxes.insert(3, m_uiForm.tra_sample_edit);
m_run_no_boxes.insert(4, m_uiForm.tra_can_edit);
m_run_no_boxes.insert(5, m_uiForm.tra_bkgd_edit);
m_run_no_boxes.insert(6, m_uiForm.direct_sample_edit);
m_run_no_boxes.insert(7, m_uiForm.direct_can_edit);
m_run_no_boxes.insert(8, m_uiForm.direct_bkgd_edit);
//Period label hash. Each label has a buddy set to its corresponding text edit field
m_period_lbls.insert(0, m_uiForm.sct_prd_tot1);
m_period_lbls.insert(1, m_uiForm.sct_prd_tot2);
m_period_lbls.insert(2, m_uiForm.sct_prd_tot3);
m_period_lbls.insert(3, m_uiForm.tra_prd_tot1);
m_period_lbls.insert(4, m_uiForm.tra_prd_tot2);
m_period_lbls.insert(5, m_uiForm.tra_prd_tot3);
m_period_lbls.insert(6, m_uiForm.direct_prd_tot1);
m_period_lbls.insert(7, m_uiForm.direct_prd_tot2);
m_period_lbls.insert(8, m_uiForm.direct_prd_tot3);
// batch mode settings
m_allowed_batchtags.insert("sample_sans",0);
m_allowed_batchtags.insert("sample_trans",1);
m_allowed_batchtags.insert("sample_direct_beam",2);
m_allowed_batchtags.insert("can_sans",3);
m_allowed_batchtags.insert("can_trans",4);
m_allowed_batchtags.insert("can_direct_beam",5);
m_allowed_batchtags.insert("background_sans",-1);
m_allowed_batchtags.insert("background_trans",-1);
m_allowed_batchtags.insert("background_direct_beam",-1);
m_allowed_batchtags.insert("output_as",6);
// detector info
// SANS2D det names/label map
QHash<QString, QLabel*> labelsmap;
labelsmap.insert("Front_Det_Z", m_uiForm.dist_smp_frontZ);
labelsmap.insert("Front_Det_X", m_uiForm.dist_smp_frontX);
labelsmap.insert("Front_Det_Rot", m_uiForm.smp_rot);
labelsmap.insert("Rear_Det_X", m_uiForm.dist_smp_rearX);
labelsmap.insert("Rear_Det_Z", m_uiForm.dist_smp_rearZ);
m_s2d_detlabels.append(labelsmap);
labelsmap.clear();
labelsmap.insert("Front_Det_Z", m_uiForm.dist_can_frontZ);
labelsmap.insert("Front_Det_X", m_uiForm.dist_can_frontX);
labelsmap.insert("Front_Det_Rot", m_uiForm.can_rot);
labelsmap.insert("Rear_Det_X", m_uiForm.dist_can_rearX);
labelsmap.insert("Rear_Det_Z", m_uiForm.dist_can_rearZ);
m_s2d_detlabels.append(labelsmap);
labelsmap.clear();
labelsmap.insert("Front_Det_Z", m_uiForm.dist_bkgd_frontZ);
labelsmap.insert("Front_Det_X", m_uiForm.dist_bkgd_frontX);
labelsmap.insert("Front_Det_Rot", m_uiForm.bkgd_rot);
labelsmap.insert("Rear_Det_X", m_uiForm.dist_bkgd_rearX);
labelsmap.insert("Rear_Det_Z", m_uiForm.dist_bkgd_rearZ);
m_s2d_detlabels.append(labelsmap);
//LOQ labels
labelsmap.clear();
labelsmap.insert("moderator-sample", m_uiForm.dist_sample_ms);
labelsmap.insert("sample-main-detector-bank", m_uiForm.dist_smp_mdb);
labelsmap.insert("sample-HAB",m_uiForm.dist_smp_hab);
m_loq_detlabels.append(labelsmap);
labelsmap.clear();
labelsmap.insert("moderator-sample", m_uiForm.dist_can_ms);
labelsmap.insert("sample-main-detector-bank", m_uiForm.dist_can_mdb);
labelsmap.insert("sample-HAB",m_uiForm.dist_can_hab);
m_loq_detlabels.append(labelsmap);
labelsmap.clear();
labelsmap.insert("moderator-sample", m_uiForm.dist_bkgd_ms);
labelsmap.insert("sample-main-detector-bank", m_uiForm.dist_bkgd_mdb);
labelsmap.insert("sample-HAB", m_uiForm.dist_bkgd_hab);
m_loq_detlabels.append(labelsmap);
// Full workspace names as they appear in the service
m_workspace_names.clear();
}
/**
* Restore previous input
*/
void SANSRunWindow::readSettings()
{
g_log.debug("Reading settings.");
QSettings value_store;
value_store.beginGroup("CustomInterfaces/SANSRunWindow");
m_uiForm.datadir_edit->setText(value_store.value("data_dir").toString());
m_uiForm.userfile_edit->setText(value_store.value("user_file").toString());
m_last_dir = value_store.value("last_dir", "").toString();
m_uiForm.inst_opt->setCurrentIndex(value_store.value("instrument", 0).toInt());
int mode_flag = value_store.value("runmode", 0).toInt();
if( mode_flag == SANSRunWindow::SingleMode )
{
m_uiForm.single_mode_btn->click();
}
else
{
m_uiForm.batch_mode_btn->click();
}
//The instrument definition directory
m_ins_defdir = QString::fromStdString(ConfigService::Instance().getString("instrumentDefinition.directory"));
// Setup for instrument
handleInstrumentChange(m_uiForm.inst_opt->currentIndex());
//Set old file extension
m_uiForm.file_opt->setCurrentIndex(value_store.value("fileextension", 0).toInt());
value_store.endGroup();
readSaveSettings(value_store);
g_log.debug() << "Found previous data directory " << m_uiForm.datadir_edit->text().toStdString()
<< "\nFound previous user mask file" << m_uiForm.userfile_edit->text().toStdString()
<< "\nFound instrument definition directory " << m_ins_defdir.toStdString() << std::endl;
}
/** Sets the states of the checkboxes in the save box using those
* in the passed QSettings object
* @param valueStore where the settings will be stored
*/
void SANSRunWindow::readSaveSettings(QSettings & valueStore)
{
valueStore.beginGroup("CustomInterfaces/SANSRunWindow/SaveOutput");
m_uiForm.saveNex_check->setChecked(valueStore.value("nexus",false).toBool());
m_uiForm.saveCan_check->setChecked(valueStore.value("canSAS",false).toBool());
m_uiForm.saveRKH_check->setChecked(valueStore.value("RKH", false).toBool());
m_uiForm.saveCSV_check->setChecked(valueStore.value("CSV", false).toBool());
}
/**
* Save input for future use
*/
void SANSRunWindow::saveSettings()
{
QSettings value_store;
value_store.beginGroup("CustomInterfaces/SANSRunWindow");
if( !m_data_dir.isEmpty() )
{
value_store.setValue("data_dir", m_data_dir);
}
if( !m_uiForm.userfile_edit->text().isEmpty() )
{
value_store.setValue("user_file", m_uiForm.userfile_edit->text());
}
value_store.setValue("last_dir", m_last_dir);
value_store.setValue("instrument", m_uiForm.inst_opt->currentIndex());
value_store.setValue("fileextension", m_uiForm.file_opt->currentIndex());
unsigned int mode_id(0);
if( m_uiForm.single_mode_btn->isChecked() )
{
mode_id = SANSRunWindow::SingleMode;
}
else
{
mode_id = SANSRunWindow::BatchMode;
}
value_store.setValue("runmode",mode_id);
value_store.endGroup();
saveSaveSettings(value_store);
}
/** Stores the state of the checkboxes in the save box with the
* passed QSettings object
* @param valueStore where the settings will be stored
*/
void SANSRunWindow::saveSaveSettings(QSettings & valueStore)
{
valueStore.beginGroup("CustomInterfaces/SANSRunWindow/SaveOutput");
valueStore.setValue("nexus", m_uiForm.saveNex_check->isChecked());
valueStore.setValue("canSAS", m_uiForm.saveCan_check->isChecked());
valueStore.setValue("RKH", m_uiForm.saveRKH_check->isChecked());
valueStore.setValue("CSV", m_uiForm.saveCSV_check->isChecked());
}
/**
* Run a function from the SANS reduction script, ensuring that the first call imports the module
* @param pycode The code to execute
* @returns A trimmed string containing the output of the code execution
*/
QString SANSRunWindow::runReduceScriptFunction(const QString & pycode)
{
if( !m_have_reducemodule )
{
return QString();
}
//Ensure the correct instrument is set
QString code_torun = "SetNoPrintMode(True)\n" + pycode + "\nSetNoPrintMode(False)";
return runPythonCode(code_torun).trimmed();
}
/**
* Trim off Python markers surrounding things like strings or lists that have been
* printed by Python
*/
void SANSRunWindow::trimPyMarkers(QString & txt)
{
txt.remove(0,1);
txt.chop(1);
}
/**
* Load the user file specified in the text field
* @returns Boolean indicating whether we were successful or not
*/
bool SANSRunWindow::loadUserFile()
{
QString filetext = m_uiForm.userfile_edit->text();
if( filetext.isEmpty() ) return false;
if( QFileInfo(filetext).isRelative() )
{
filetext = QDir(m_data_dir).absoluteFilePath(filetext);
}
if( !QFileInfo(filetext).exists() ) return false;
QFile user_file(filetext);
if( !user_file.open(QIODevice::ReadOnly) ) return false;
user_file.close();
//Clear the def masking info table.
int mask_table_count = m_uiForm.mask_table->rowCount();
for( int i = mask_table_count - 1; i >= 0; --i )
{
m_uiForm.mask_table->removeRow(i);
}
// Use python function to read the file and then extract the fields
runReduceScriptFunction("MaskFile(r'" + filetext + "')");
handleInstrumentChange(m_uiForm.inst_opt->currentIndex());
double unit_conv(1000.);
// Radius
double dbl_param = runReduceScriptFunction("printParameter('RMIN'),").toDouble();
m_uiForm.rad_min->setText(QString::number(dbl_param*unit_conv));
dbl_param = runReduceScriptFunction("printParameter('RMAX'),").toDouble();
m_uiForm.rad_max->setText(QString::number(dbl_param*unit_conv));
//Wavelength
m_uiForm.wav_min->setText(runReduceScriptFunction("printParameter('WAV1'),"));
m_uiForm.wav_max->setText(runReduceScriptFunction("printParameter('WAV2'),"));
setLimitStepParameter("wavelength", runReduceScriptFunction("printParameter('DWAV'),"), m_uiForm.wav_dw, m_uiForm.wav_dw_opt);
//Q
QString text = runReduceScriptFunction("printParameter('Q_REBIN'),");
QStringList values = text.split(",");
if( values.count() == 3 )
{
m_uiForm.q_min->setText(values[0].trimmed());
m_uiForm.q_max->setText(values[2].trimmed());
setLimitStepParameter("Q", values[1].trimmed(), m_uiForm.q_dq, m_uiForm.q_dq_opt);
}
else
{
m_uiForm.q_rebin->setText(text.trimmed());
m_uiForm.q_dq_opt->setCurrentIndex(2);
}
//Qxy
m_uiForm.qy_max->setText(runReduceScriptFunction("printParameter('QXY2'),"));
setLimitStepParameter("Qxy", runReduceScriptFunction("printParameter('DQXY'),"), m_uiForm.qy_dqy, m_uiForm.qy_dqy_opt);
// Tranmission options
m_uiForm.trans_min->setText(runReduceScriptFunction("printParameter('TRANS_WAV1'),"));
m_uiForm.trans_max->setText(runReduceScriptFunction("printParameter('TRANS_WAV2'),"));
text = runReduceScriptFunction("printParameter('TRANS_FIT')");
int index = m_uiForm.trans_opt->findData(text, Qt::UserRole, Qt::MatchFixedString);
if( index >= 0 )
{
m_uiForm.trans_opt->setCurrentIndex(index);
}
//Monitor spectra
m_uiForm.monitor_spec->setText(runReduceScriptFunction("printParameter('MONITORSPECTRUM'),"));
m_uiForm.trans_monitor->setText(runReduceScriptFunction("printParameter('TRANS_INCID_MON'),"));
m_uiForm.monitor_interp->setChecked(
runReduceScriptFunction("printParameter('SAMP_INTERPOLATE'),") == "True");
m_uiForm.trans_interp->setChecked(
runReduceScriptFunction("printParameter('TRANS_INTERPOLATE'),") == "True");
//Direct efficiency correction
m_uiForm.direct_file->setText(runReduceScriptFunction("printParameter('DIRECT_BEAM_FILE_R'),"));
m_uiForm.front_direct_file->setText(runReduceScriptFunction("printParameter('DIRECT_BEAM_FILE_F'),"));
//Scale factor
dbl_param = runReduceScriptFunction("printParameter('RESCALE'),").toDouble();
m_uiForm.scale_factor->setText(QString::number(dbl_param/100.));
//Sample offset if one has been specified
dbl_param = runReduceScriptFunction("printParameter('SAMPLE_Z_CORR'),").toDouble();
m_uiForm.smpl_offset->setText(QString::number(dbl_param*unit_conv));
//Centre coordinates
dbl_param = runReduceScriptFunction("printParameter('XBEAM_CENTRE'),").toDouble();
m_uiForm.beam_x->setText(QString::number(dbl_param*1000.0));
dbl_param = runReduceScriptFunction("printParameter('YBEAM_CENTRE'),").toDouble();
m_uiForm.beam_y->setText(QString::number(dbl_param*1000.0));
//Gravity switch
QString param = runReduceScriptFunction("printParameter('GRAVITY')");
if( param == "True" )
{
m_uiForm.gravity_check->setChecked(true);
}
else
{
m_uiForm.gravity_check->setChecked(false);
}
////Detector bank
param = runReduceScriptFunction(
"printParameter('INSTRUMENT.curDetector().name()')");
index = m_uiForm.detbank_sel->findText(param);
if( index >= 0 && index < 2 )
{
m_uiForm.detbank_sel->setCurrentIndex(index);
}
//Masking table
updateMaskTable();
// Phi values
m_uiForm.phi_min->setText(runReduceScriptFunction("printParameter('PHIMIN')"));
m_uiForm.phi_max->setText(runReduceScriptFunction("printParameter('PHIMAX')"));
if ( runReduceScriptFunction("printParameter('PHIMIRROR')") == "True" )
{
m_uiForm.mirror_phi->setChecked(true);
}
else
{
m_uiForm.mirror_phi->setChecked(false);
}
m_cfg_loaded = true;
m_uiForm.userfileBtn->setText("Reload");
m_uiForm.tabWidget->setTabEnabled(m_uiForm.tabWidget->count() - 1, true);
return true;
}
/**
* Load a CSV file specifying information run numbers and populate the batch mode grid
*/
bool SANSRunWindow::loadCSVFile()
{
QString filename = m_uiForm.csv_filename->text();
QFile csv_file(filename);
if( !csv_file.open(QIODevice::ReadOnly | QIODevice::Text) )
{
showInformationBox("Error: Cannot open CSV file \"" + filename + "\"");
return false;
}
//Clear the current table
clearBatchTable();
QTextStream file_in(&csv_file);
int errors(0);
while( !file_in.atEnd() )
{
QString line = file_in.readLine().simplified();
if( !line.isEmpty() )
{
errors += addBatchLine(line, ",");
}
}
if( errors > 0 )
{
showInformationBox("Warning: " + QString::number(errors) + " malformed lines detected in \"" + filename + "\". Lines skipped.");
}
return true;
}
/**
* Set a pair of an QLineEdit field and type QComboBox using the parameter given
* @param pname The name of the parameter
* @param param A string representing a value that maybe prefixed with a minus to indicate a different step type
* @param step_value The field to store the actual value
* @param step_type The combo box with the type options
*/
void SANSRunWindow::setLimitStepParameter(const QString& pname, QString param, QLineEdit* step_value, QComboBox* step_type)
{
if( param.startsWith("-") )
{
int index = step_type->findText("Logarithmic");
if( index < 0 )
{
raiseOneTimeMessage("Warning: Unable to find logarithmic scale option for " + pname + ", setting as linear.", 1);
index = step_type->findText("Linear");
}
step_type->setCurrentIndex(index);
step_value->setText(param.remove(0,1));
}
else
{
step_type->setCurrentIndex(step_type->findText("Linear"));
step_value->setText(param);
}
}
/**
* Construct the mask table on the Mask tab
*/
void SANSRunWindow::updateMaskTable()
{
//Clear the current contents
for( int i = m_uiForm.mask_table->rowCount() - 1; i >= 0; --i )
{
m_uiForm.mask_table->removeRow(i);
}
QString reardet_name("rear-detector"), frontdet_name("front-detector");
if( m_uiForm.inst_opt->currentIndex() == 0 )
{
reardet_name = "main-detector-bank";
frontdet_name = "HAB";
}
// First create 2 default mask cylinders at min and max radius for the beam stop and
// corners
m_uiForm.mask_table->insertRow(0);
m_uiForm.mask_table->setItem(0, 0, new QTableWidgetItem("beam stop"));
m_uiForm.mask_table->setItem(0, 1, new QTableWidgetItem(reardet_name));
m_uiForm.mask_table->setItem(0, 2, new QTableWidgetItem("infinite-cylinder, r = rmin"));
if( m_uiForm.rad_max->text() != "-1" )
{
m_uiForm.mask_table->insertRow(1);
m_uiForm.mask_table->setItem(1, 0, new QTableWidgetItem("corners"));
m_uiForm.mask_table->setItem(1, 1, new QTableWidgetItem(reardet_name));
m_uiForm.mask_table->setItem(1, 2, new QTableWidgetItem("infinite-cylinder, r = rmax"));
}
//Now add information from the mask file
//Spectrum mask
QString mask_string = runReduceScriptFunction("printParameter('SPECMASKSTRING')");
addSpectrumMasksToTable(mask_string, "-");
//"Rear" det
mask_string = runReduceScriptFunction("printParameter('SPECMASKSTRING_R')");
addSpectrumMasksToTable(mask_string, reardet_name);
//"Front" det
mask_string = runReduceScriptFunction("printParameter('SPECMASKSTRING_F')");
addSpectrumMasksToTable(mask_string, frontdet_name);
//Time masks
mask_string = runReduceScriptFunction("printParameter('TIMEMASKSTRING')");
addTimeMasksToTable(mask_string, "-");
//Rear detector
mask_string = runReduceScriptFunction("printParameter('TIMEMASKSTRING_R')");
addTimeMasksToTable(mask_string, reardet_name);
//Front detectors
mask_string = runReduceScriptFunction("printParameter('TIMEMASKSTRING_F')");
addTimeMasksToTable(mask_string, frontdet_name);
}
/**
* Add a spectrum mask string to the mask table
* @param mask_string The string of mask information
* @param det_name The detector it relates to
*/
void SANSRunWindow::addSpectrumMasksToTable(const QString & mask_string, const QString & det_name)
{
QStringList elements = mask_string.split(",", QString::SkipEmptyParts);
QStringListIterator sitr(elements);
while(sitr.hasNext())
{
QString item = sitr.next();
QString col1_txt;
if( item.startsWith('s', Qt::CaseInsensitive) )
{
col1_txt = "Spectrum";
}
else if( item.startsWith('h', Qt::CaseInsensitive) || item.startsWith('v', Qt::CaseInsensitive) )
{
if( item.contains('+') )
{
col1_txt = "Box";
}
else
{
col1_txt = "Strip";
}
}
else continue;
int row = m_uiForm.mask_table->rowCount();
//Insert line after last row
m_uiForm.mask_table->insertRow(row);
m_uiForm.mask_table->setItem(row, 0, new QTableWidgetItem(col1_txt));
m_uiForm.mask_table->setItem(row, 1, new QTableWidgetItem(det_name));
m_uiForm.mask_table->setItem(row, 2, new QTableWidgetItem(item));
}
}
/**
* Add a time mask string to the mask table
* @param mask_string The string of mask information
* @param det_name The detector it relates to
*/
void SANSRunWindow::addTimeMasksToTable(const QString & mask_string, const QString & det_name)
{
QStringList elements = mask_string.split(";",QString::SkipEmptyParts);
QStringListIterator sitr(elements);
while(sitr.hasNext())
{
int row = m_uiForm.mask_table->rowCount();
m_uiForm.mask_table->insertRow(row);
m_uiForm.mask_table->setItem(row, 0, new QTableWidgetItem("time"));
m_uiForm.mask_table->setItem(row, 1, new QTableWidgetItem(det_name));
m_uiForm.mask_table->setItem(row, 2, new QTableWidgetItem(sitr.next()));
}
}
/**
* Retrieve and set the component distances
* @param workspace The workspace pointer
* @param lms The result of the moderator-sample distance
* @param lsda The result of the sample-detector bank 1 distance
* @param lsdb The result of the sample-detector bank 2 distance
*/
void SANSRunWindow::componentLOQDistances(Mantid::API::MatrixWorkspace_sptr workspace, double & lms, double & lsda, double & lsdb)
{
IInstrument_sptr instr = workspace->getInstrument();
if( instr == boost::shared_ptr<IInstrument>() ) return;
Mantid::Geometry::IObjComponent_sptr source = instr->getSource();
if( source == boost::shared_ptr<Mantid::Geometry::IObjComponent>() ) return;
Mantid::Geometry::IObjComponent_sptr sample = instr->getSample();
if( sample == boost::shared_ptr<Mantid::Geometry::IObjComponent>() ) return;
lms = source->getPos().distance(sample->getPos()) * 1000.;
//Find the main detector bank
boost::shared_ptr<Mantid::Geometry::IComponent> comp = instr->getComponentByName("main-detector-bank");
if( comp != boost::shared_ptr<Mantid::Geometry::IComponent>() )
{
lsda = sample->getPos().distance(comp->getPos()) * 1000.;
}
comp = instr->getComponentByName("HAB");
if( comp != boost::shared_ptr<Mantid::Geometry::IComponent>() )
{
lsdb = sample->getPos().distance(comp->getPos()) * 1000.;
}
}
/**
* Set the state of processing.
* @param running If we are processing then some interaction is disabled
* @param type The reduction type, 0 = 1D and 1 = 2D
*/
void SANSRunWindow::setProcessingState(bool running, int type)
{
if( m_uiForm.single_mode_btn->isChecked() )
{
m_uiForm.load_dataBtn->setEnabled(!running);
}
else
{
m_uiForm.load_dataBtn->setEnabled(false);
}
//buttons that are available as long as Python is available
m_uiForm.oneDBtn->setEnabled(!running);
m_uiForm.twoDBtn->setEnabled(!running);
m_uiForm.saveSel_btn->setEnabled(!running);
m_uiForm.runcentreBtn->setEnabled(!running);
m_uiForm.userfileBtn->setEnabled(!running);
m_uiForm.data_dirBtn->setEnabled(!running);
if( running )
{
m_uiForm.saveDefault_btn->setEnabled(false);
if( type == 0 )
{
m_uiForm.oneDBtn->setText("Running ...");
}
else if( type == 1 )
{
m_uiForm.twoDBtn->setText("Running ...");
}
else {}
}
else
{
enableOrDisableDefaultSave();
m_uiForm.oneDBtn->setText("1D Reduce");
m_uiForm.twoDBtn->setText("2D Reduce");
}
for( int i = 0; i < 4; ++i)
{
if( i == m_uiForm.tabWidget->currentIndex() ) continue;
m_uiForm.tabWidget->setTabEnabled(i, !running);
}
QCoreApplication::processEvents();
}
/**
* Does the workspace exist in the AnalysisDataService
* @param ws_name The name of the workspace
* @returns A boolean indicatingif the given workspace exists in the AnalysisDataService
*/
bool SANSRunWindow::workspaceExists(const QString & ws_name) const
{
return AnalysisDataService::Instance().doesExist(ws_name.toStdString());
}
/**
* @returns A list of the currently available workspaces
*/
QStringList SANSRunWindow::currentWorkspaceList() const
{
std::set<std::string> ws_list = AnalysisDataService::Instance().getObjectNames();
std::set<std::string>::const_iterator iend = ws_list.end();
QStringList current_list;
for( std::set<std::string>::const_iterator itr = ws_list.begin(); itr != iend; ++itr )
{
current_list.append(QString::fromStdString(*itr));
}
return current_list;
}
/**
* Is the user file loaded
* @returns A boolean indicating whether the user file has been parsed in to the details tab
*/
bool SANSRunWindow::isUserFileLoaded() const
{
return m_cfg_loaded;
}
/**
* Create the mask strings for spectra and times
*/
void SANSRunWindow::addUserMaskStrings(QString & exec_script)
{
//Clear current
exec_script += "Mask('MASK/CLEAR')\nMask('MASK/CLEAR/TIME')\n";
//Pull in the table details first, skipping the first two rows
int nrows = m_uiForm.mask_table->rowCount();
for(int row = 0; row < nrows; ++row)
{
if( m_uiForm.mask_table->item(row, 2)->text().startsWith("inf") )
{
continue;
}
//Details are in the third column
exec_script += "Mask('MASK";
if( m_uiForm.mask_table->item(row, 0)->text() == "time")
{
exec_script += "/TIME";
}
QString details = m_uiForm.mask_table->item(row, 2)->text();
QString detname = m_uiForm.mask_table->item(row, 1)->text();
if( detname == "-" )
{
exec_script += " " + details;
}
else if( detname == "rear-detector" || detname == "main-detector-bank" )
{
exec_script += "/REAR " + details;
}
else
{
exec_script += "/FRONT " + details;
}
exec_script += "')\n";
}
//Spectra mask first
QStringList mask_params = m_uiForm.user_spec_mask->text().split(",", QString::SkipEmptyParts);
QStringListIterator sitr(mask_params);
QString bad_masks;
while(sitr.hasNext())
{
QString item = sitr.next().trimmed();
if( item.startsWith("REAR", Qt::CaseInsensitive) || item.startsWith("FRONT", Qt::CaseInsensitive) )
{
exec_script += "Mask('MASK/" + item + "')\n";
}
else if( item.startsWith('S', Qt::CaseInsensitive) || item.startsWith('H', Qt::CaseInsensitive) ||
item.startsWith('V', Qt::CaseInsensitive) )
{
exec_script += "Mask('MASK " + item + "')\n";
}
else
{
bad_masks += item + ",";
}
}
if( !bad_masks.isEmpty() )
{
m_uiForm.tabWidget->setCurrentIndex(3);
showInformationBox(QString("Warning: Could not parse the following spectrum masks: ") + bad_masks + ". Values skipped.");
}
//Time masks
mask_params = m_uiForm.user_time_mask->text().split(",", QString::SkipEmptyParts);
sitr = QStringListIterator(mask_params);
bad_masks = "";
while(sitr.hasNext())
{
QString item = sitr.next().trimmed();
if( item.startsWith("REAR", Qt::CaseInsensitive) || item.startsWith("FRONT", Qt::CaseInsensitive) )
{
int ndetails = item.split(" ").count();
if( ndetails == 3 || ndetails == 2 )
{
exec_script += "Mask('/TIME" + item + "')\n";
}
else
{
bad_masks += item + ",";
}
}
}
if( !bad_masks.isEmpty() )
{
m_uiForm.tabWidget->setCurrentIndex(3);
showInformationBox(QString("Warning: Could not parse the following time masks: ") + bad_masks + ". Values skipped.");
}
}
/**
* Set the information about component distances on the geometry tab
*/
void SANSRunWindow::setGeometryDetails(const QString & sample_logs, const QString & can_logs)
{
resetGeometryDetailsBox();
double unit_conv(1000.);
QString workspace_name = getWorkspaceName(0);
if( workspace_name.isEmpty() ) return;
Workspace_sptr workspace_ptr = AnalysisDataService::Instance().retrieve(workspace_name.toStdString());
MatrixWorkspace_sptr sample_workspace = boost::dynamic_pointer_cast<MatrixWorkspace>(workspace_ptr);
if ( !sample_workspace )
{//assume all geometry information is in the first member of the group and it is constant for all group members
//function throws if a fisrt member can't be retrieved
sample_workspace = getGroupMember(workspace_ptr, 1);
}
IInstrument_sptr instr = sample_workspace->getInstrument();
boost::shared_ptr<Mantid::Geometry::IComponent> source = instr->getSource();
// Moderator-monitor distance is common to LOQ and S2D
int monitor_spectrum = m_uiForm.monitor_spec->text().toInt();
std::vector<int> dets = sample_workspace->spectraMap().getDetectors(monitor_spectrum);
if( dets.empty() ) return;
double dist_mm(0.0);
QString colour("black");
try
{
Mantid::Geometry::IDetector_sptr detector = instr->getDetector(dets[0]);
dist_mm = detector->getDistance(*source) * unit_conv;
}
catch(std::runtime_error&)
{
colour = "red";
}
//LOQ
if( m_uiForm.inst_opt->currentIndex() == 0 )
{
if( colour == "red" )
{
m_uiForm.dist_mod_mon->setText("<font color='red'>error<font>");
}
else
{
m_uiForm.dist_mod_mon->setText(formatDouble(dist_mm, colour));
}
setLOQGeometry(sample_workspace, 0);
QString can = getWorkspaceName(1);
if( !can.isEmpty() )
{
Workspace_sptr workspace_ptr = Mantid::API::AnalysisDataService::Instance().retrieve(can.toStdString());
MatrixWorkspace_sptr can_workspace = boost::dynamic_pointer_cast<MatrixWorkspace>(workspace_ptr);
if ( ! can_workspace )
{//assume all geometry information is in the first member of the group and it is constant for all group members
//function throws if a fisrt member can't be retrieved
can_workspace = getGroupMember(workspace_ptr, 1);
}
setLOQGeometry(can_workspace, 1);
}
}
else
{
if( colour == "red" )
{
m_uiForm.dist_mon_s2d->setText("<font color='red'>error<font>");
}
else
{
m_uiForm.dist_mon_s2d->setText(formatDouble(dist_mm, colour));
}
//SANS2D - Sample
setSANS2DGeometry(sample_workspace, sample_logs, 0);
//Get the can workspace if there is one
QString can = getWorkspaceName(1);
if( can.isEmpty() )
{
return;
}
Workspace_sptr workspace_ptr;
try
{
workspace_ptr = AnalysisDataService::Instance().retrieve(can.toStdString());
}
catch(std::runtime_error&)
{
return;
}
Mantid::API::MatrixWorkspace_sptr can_workspace = boost::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(workspace_ptr);
if ( !can_workspace )
{//assume all geometry information is in the first member of the group and it is constant for all group members
//function throws if a fisrt member can't be retrieved
can_workspace = getGroupMember(workspace_ptr, 1);
}
setSANS2DGeometry(can_workspace, can_logs, 1);
//Check for discrepancies
bool warn_user(false);
double lms_sample(m_uiForm.dist_sample_ms_s2d->text().toDouble()), lms_can(m_uiForm.dist_can_ms_s2d->text().toDouble());
if( std::fabs(lms_sample - lms_can) > 5e-03 )
{
warn_user = true;
markError(m_uiForm.dist_sample_ms_s2d);
markError(m_uiForm.dist_can_ms_s2d);
}
QString marked_dets = runReduceScriptFunction("print GetMismatchedDetList(),").trimmed();
trimPyMarkers(marked_dets);
if( !marked_dets.isEmpty() )
{
QStringList detnames = marked_dets.split(",");
QStringListIterator itr(detnames);
while( itr.hasNext() )
{
QString name = itr.next().trimmed();
trimPyMarkers(name);
for( int i = 0; i < 2; ++i )
{
markError(m_s2d_detlabels[i].value(name));
warn_user = true;
}
}
}
if( warn_user )
{
raiseOneTimeMessage("Warning: Some detector distances do not match for the assigned Sample/Can runs, see Geometry tab for details.");
}
}
}
/**
* Set SANS2D geometry info
* @param workspace The workspace
* @param logs The log information
*/
void SANSRunWindow::setSANS2DGeometry(Mantid::API::MatrixWorkspace_sptr workspace, const QString & logs, int wscode)
{
if( m_uiForm.inst_opt->currentIndex() == 0 ) return;
double unitconv = 1000.;
Mantid::API::IInstrument_sptr instr = workspace->getInstrument();
boost::shared_ptr<Mantid::Geometry::IComponent> sample = instr->getSample();
boost::shared_ptr<Mantid::Geometry::IComponent> source = instr->getSource();
double distance = source->getDistance(*sample) * unitconv;
//Moderator-sample
QLabel *dist_label(NULL);
if( wscode == 0 )
{
dist_label = m_uiForm.dist_sample_ms_s2d;
}
else if( wscode == 1 )
{
dist_label = m_uiForm.dist_can_ms_s2d;
}
else
{
dist_label = m_uiForm.dist_bkgd_ms_s2d;
}
dist_label->setText(formatDouble(distance, "black"));
//Detectors
QStringList det_info = logs.split(",");
QStringListIterator itr(det_info);
while( itr.hasNext() )
{
QString line = itr.next();
QStringList values = line.split(":");
QString detname = values[0].trimmed();
QString distance = values[1].trimmed();
trimPyMarkers(detname);
trimPyMarkers(distance);
QLabel *lbl = m_s2d_detlabels[wscode].value(detname);
if( lbl ) lbl->setText(distance);
}
}
/**
* Set LOQ geometry information
* @param workspace The workspace to operate on
*/
void SANSRunWindow::setLOQGeometry(Mantid::API::MatrixWorkspace_sptr workspace, int wscode)
{
if( m_uiForm.inst_opt->currentIndex() == 1 ) return;
double dist_ms(0.0), dist_mdb(0.0), dist_hab(0.0);
//Sample
componentLOQDistances(workspace, dist_ms, dist_mdb, dist_hab);
QHash<QString, QLabel*> & labels = m_loq_detlabels[wscode];
QLabel *detlabel = labels.value("moderator-sample");
if( detlabel )
{
detlabel->setText(QString::number(dist_ms));
}
detlabel = labels.value("sample-main-detector-bank");
if( detlabel )
{
detlabel->setText(QString::number(dist_mdb));
}
detlabel = labels.value("sample-HAB");
if( detlabel )
{
detlabel->setText(QString::number(dist_hab));
}
}
/**
* Mark an error on a label
* @param label A pointer to a QLabel instance
*/
void SANSRunWindow::markError(QLabel* label)
{
if( label )
{
label->setText("<font color=\"red\">" + label->text() + "</font>");
}
}
//-------------------------------------
// Private SLOTS
//------------------------------------
/**
* Select the base directory for the data
*/
void SANSRunWindow::selectDataDir()
{
QString data_dir = QFileDialog::getExistingDirectory(this, tr("Choose a directory"), m_last_dir);
if( !data_dir.isEmpty() && QDir(data_dir).exists() )
{
m_last_dir = data_dir;
m_data_dir = data_dir;
m_uiForm.datadir_edit->setText(data_dir);
}
}
/**
* Select and load the user file
*/
void SANSRunWindow::selectUserFile()
{
if( !browseForFile("Select a user file", m_uiForm.userfile_edit) )
{
return;
}
runReduceScriptFunction("UserPath('" + QFileInfo(m_uiForm.userfile_edit->text()).path() + "')");
if( !loadUserFile() )
{
m_cfg_loaded = false;
showInformationBox("Error loading user file '" + m_uiForm.userfile_edit->text() + "', cannot continue.");
return;
}
//Check for warnings
checkLogFlags();
m_cfg_loaded = true;
m_uiForm.tabWidget->setTabEnabled(1, true);
m_uiForm.tabWidget->setTabEnabled(2, true);
m_uiForm.tabWidget->setTabEnabled(3, true);
//path() returns the directory
m_last_dir = QFileInfo(m_uiForm.userfile_edit->text()).path();
}
/**
* Select and load a CSV file
*/
void SANSRunWindow::selectCSVFile()
{
if( !m_cfg_loaded )
{
showInformationBox("Please load the relevant user file.");
return;
}
if( !browseForFile("Select CSV file",m_uiForm.csv_filename, "CSV files (*.csv)") )
{
return;
}
if( !loadCSVFile() )
{
return;
}
//path() returns the directory
m_last_dir = QFileInfo(m_uiForm.csv_filename->text()).path();
if( m_cfg_loaded ) setProcessingState(false, -1);
}
/** Raises a browse dialog and inserts the selected file into the
* save text edit box, outfile_edit
*/
void SANSRunWindow::saveFileBrowse()
{
QString title = "Save output workspace as";
QSettings prevValues;
prevValues.beginGroup("CustomInterfaces/SANSRunWindow/SaveOutput");
//use their previous directory first and go to their default if that fails
QString prevPath = prevValues.value("dir", QString::fromStdString(
ConfigService::Instance().getString("defaultsave.directory"))).toString();
QString filter = ";;AllFiles (*.*)";
QString oFile = QFileDialog::getSaveFileName(this, title, prevPath, filter);
if( ! oFile.isEmpty() )
{
m_uiForm.outfile_edit->setText(oFile);
QString directory = QFileInfo(oFile).path();
prevValues.setValue("dir", directory);
}
}
/**
* Mark that a run number has changed
*/
void SANSRunWindow::runChanged()
{
m_warnings_issued = false;
forceDataReload(true);
}
/**
* Flip the flag to confirm whether data is reloaded
* @param force If true, the data is reloaded when reduce is clicked
*/
void SANSRunWindow::forceDataReload(bool force)
{
m_force_reload = force;
}
/**
* Browse for a file and set the text of the given edit box
* @param box_title The title field for the display box
* @param A QLineEdit box to use for the file path
* @param file_filter An optional file filter
*/
bool SANSRunWindow::browseForFile(const QString & box_title, QLineEdit* file_field, QString file_filter)
{
QString box_text = file_field->text();
QString start_path = box_text;
if( box_text.isEmpty() )
{
start_path = m_last_dir;
}
file_filter += ";;AllFiles (*.*)";
QString file_path = QFileDialog::getOpenFileName(this, box_title, start_path, file_filter);
if( file_path.isEmpty() || QFileInfo(file_path).isDir() ) return false;
file_field->setText(file_path);
return true;
}
/**
* Receive a load button click signal
*/
bool SANSRunWindow::handleLoadButtonClick()
{
QString origin_dir = QDir::currentPath();
QString work_dir = QDir(m_uiForm.datadir_edit->text()).absolutePath();
if( work_dir.isEmpty() || !QDir(work_dir).exists() )
{
showInformationBox("The specified data directory " + m_uiForm.datadir_edit->text() + " does not exist.");
return false;
}
if( !work_dir.endsWith('/') ) work_dir += "/";
m_data_dir = work_dir;
runReduceScriptFunction("DataPath('" + m_data_dir + "')");
// Check if we have loaded the data_file
if( !isUserFileLoaded() )
{
showInformationBox("Please load the relevant user file.");
return false;
}
//ensure the Python objects are up to date with our instrument selection
QString pythonSetInst =
m_uiForm.inst_opt->itemData(m_uiForm.inst_opt->currentIndex()).toString();
runReduceScriptFunction(pythonSetInst);
setProcessingState(true, -1);
m_uiForm.load_dataBtn->setText("Loading ...");
if( m_force_reload ) cleanup();
QString run_number = m_run_no_boxes.value(0)->text();
if( run_number.isEmpty() )
{
showInformationBox("Error: No sample run given, cannot continue.");
setProcessingState(false, -1);
m_uiForm.load_dataBtn->setText("Loading Data");
return false;
}
if(!m_run_no_boxes.value(3)->text().isEmpty() && m_run_no_boxes.value(6)->text().isEmpty() )
{
showInformationBox("Error: Can run supplied without direct run, cannot continue.");
setProcessingState(false, -1);
m_uiForm.load_dataBtn->setText("Load Data");
return false;
}
QString sample_logs, can_logs;
bool is_loaded(true);
QString error;
//Quick check that there is a can direct run if a trans can is defined. If not use the sample one
if( !m_run_no_boxes.value(4)->text().isEmpty() && m_run_no_boxes.value(7)->text().isEmpty() )
{
m_run_no_boxes.value(7)->setText(m_run_no_boxes.value(6)->text());
}
QHashIterator<int, QLineEdit*> itr(m_run_no_boxes);
while( itr.hasNext() )
{
itr.next();
int key = itr.key();
// Skip background as we are not using those at the moment.
if( key == 2 ) continue;
if( key == 5 ) break;
QString run_no = itr.value()->text();
QString logs;
if( run_no.isEmpty() )
{
m_workspace_names.insert(key, "");
//Clear any that are assigned
runAssign(key, logs);
continue;
}
is_loaded &= runAssign(key, logs);
// Check if the last LoadRaw algorithm was run successfully. If so then any problem with
// loading is with the log files for the first 2 keys
Mantid::API::IAlgorithm_sptr last_run = Mantid::API::AlgorithmManager::Instance().algorithms().back();
bool raw_data_ok = last_run->isExecuted();
if( !raw_data_ok )
{
QString period("");
if ( getPeriod(key) > 1 )
{
period = QString("period ") + QString::number(getPeriod(key));
}
showInformationBox("Error: Cannot load run \""+run_no+"\" " + period + ", see results log for details.");
break;
}
if( key == 0 )
{
sample_logs = logs;
if( m_uiForm.inst_opt->currentIndex() == 1 && sample_logs.isEmpty() )
{
is_loaded = false;
showInformationBox("Error: Cannot find log file for sample run, cannot continue.");
break;
}
}
else if( key == 1 )
{
can_logs = logs;
if( m_uiForm.inst_opt->currentIndex() == 1 && can_logs.isEmpty() )
{
can_logs = sample_logs;
showInformationBox("Warning: Cannot find log file for can run, using sample values.");
}
}
else{}
}
if (!is_loaded)
{
setProcessingState(false, -1);
m_uiForm.load_dataBtn->setText("Load Data");
return false;
}
// Sort out the log information
setGeometryDetails(sample_logs, can_logs);
Mantid::API::Workspace_sptr baseWS =
Mantid::API::AnalysisDataService::Instance().retrieve(getWorkspaceName(0).toStdString());
// Enter information from sample workspace on to analysis and geometry tab
Mantid::API::MatrixWorkspace_sptr sample_workspace =
boost::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(baseWS);
if ( ! sample_workspace )
{
try
{
sample_workspace = getGroupMember(baseWS, m_uiForm.sct_smp_prd->text().toInt());
}
catch(std::exception &)
{
setProcessingState(false, -1);
m_uiForm.load_dataBtn->setText("Load Data");
showInformationBox("Error: Could not retrieve sample workspace from Mantid");
return false;
}
}
if( sample_workspace != boost::shared_ptr<Mantid::API::MatrixWorkspace>() && !sample_workspace->readX(0).empty() )
{
m_uiForm.tof_min->setText(QString::number(sample_workspace->readX(0).front()));
m_uiForm.tof_max->setText(QString::number(sample_workspace->readX(0).back()));
}
// Set the geometry if the sample has been changed
if ( m_sample_no != run_number )
{
int geomid = sample_workspace->sample().getGeometryFlag();
if( geomid > 0 && geomid < 4 )
{
m_uiForm.sample_geomid->setCurrentIndex(geomid - 1);
m_uiForm.sample_thick->setText(QString::number(sample_workspace->sample().getThickness()));
m_uiForm.sample_width->setText(QString::number(sample_workspace->sample().getWidth()));
m_uiForm.sample_height->setText(QString::number(sample_workspace->sample().getHeight()));
}
else
{
m_uiForm.sample_geomid->setCurrentIndex(2);
m_uiForm.sample_thick->setText("1");
m_uiForm.sample_width->setText("8");
m_uiForm.sample_height->setText("8");
//Warn user
showInformationBox("Warning: Incorrect geometry flag encountered: " + QString::number(geomid) +". Using default values.");
}
}
forceDataReload(false);
for( int index = 1; index < m_uiForm.tabWidget->count(); ++index )
{
m_uiForm.tabWidget->setTabEnabled(index, true);
}
m_sample_no = run_number;
setProcessingState(false, -1);
m_uiForm.load_dataBtn->setText("Load Data");
return true;
}
/**
* Construct the python code to perform the analysis based on the
* current settings
* @param type The reduction type: 1D or 2D
*/
QString SANSRunWindow::createAnalysisDetailsScript(const QString & type)
{
//Construct a run script based upon the current values within the various widgets
QString exec_reduce = m_uiForm.inst_opt->itemData(m_uiForm.inst_opt->currentIndex()).toString() +
"\nDetector('" + m_uiForm.detbank_sel->currentText() + "')\n";
//Add the path in the single mode data box if it is not empty
QString data_path = m_uiForm.datadir_edit->text();
if( !data_path.isEmpty() )
{
exec_reduce += "DataPath('" + data_path + "')\n";
}
if( type.startsWith("1D") )
{
exec_reduce += "Set1D()\n";
}
else
{
exec_reduce += "Set2D()\n";
}
//Analysis details
exec_reduce +=
"LimitsR(" + m_uiForm.rad_min->text() + "," + m_uiForm.rad_max->text() + ")\n" +
"LimitsWav(" + m_uiForm.wav_min->text() + "," + m_uiForm.wav_max->text() + "," +
m_uiForm.wav_dw->text() + ",'" + m_uiForm.wav_dw_opt->itemData(m_uiForm.wav_dw_opt->currentIndex()).toString() + "')\n";
if( m_uiForm.q_dq_opt->currentIndex() == 2 )
{
exec_reduce += "LimitsQ('" + m_uiForm.q_rebin->text() + "')\n";
}
else
{
exec_reduce += "LimitsQ(" + m_uiForm.q_min->text() + "," + m_uiForm.q_max->text() + "," +
m_uiForm.q_dq->text() + ",'" + m_uiForm.q_dq_opt->itemData(m_uiForm.q_dq_opt->currentIndex()).toString() + "')\n";
}
exec_reduce += "LimitsQXY(0.0," + m_uiForm.qy_max->text() + "," +
m_uiForm.qy_dqy->text() + ",'" + m_uiForm.qy_dqy_opt->itemData(m_uiForm.qy_dqy_opt->currentIndex()).toString() + "')\n" +
"LimitsPhi(" + m_uiForm.phi_min->text() + "," + m_uiForm.phi_max->text();
if ( m_uiForm.mirror_phi->isChecked() )
{
exec_reduce += ", True)\n";
}
else
{
exec_reduce += ", False)\n";
}
//Transmission behaviour
exec_reduce += "TransFit('" + m_uiForm.trans_opt->itemData(m_uiForm.trans_opt->currentIndex()).toString() + "'," +
m_uiForm.trans_min->text() + "," + m_uiForm.trans_max->text() + ")\n";
//Centre values
exec_reduce += "SetCentre(" + m_uiForm.beam_x->text() + "," + m_uiForm.beam_y->text() + ")\n";
//Gravity correction
exec_reduce += "Gravity(";
if( m_uiForm.gravity_check->isChecked() )
{
exec_reduce += "True)\n";
}
else
{
exec_reduce += "False)\n";
}
//Sample offset
exec_reduce += "SetSampleOffset(" + m_uiForm.smpl_offset->text() + ")\n";
//Monitor spectrum
exec_reduce += "SetMonitorSpectrum(" + m_uiForm.monitor_spec->text() + ", ";
exec_reduce += m_uiForm.monitor_interp->isChecked() ? "True" : "False";
exec_reduce += ")\n";
//the monitor to normalise the tranmission spectrum against
exec_reduce += "SetTransSpectrum(" + m_uiForm.trans_monitor->text() + ", ";
exec_reduce += m_uiForm.trans_interp->isChecked() ? "True" : "False";
exec_reduce += ")\n";
//Extra mask information
addUserMaskStrings(exec_reduce);
//Set geometry info
exec_reduce +=
"SampleHeight(" + m_uiForm.sample_height->text() + ")\n" +
"SampleWidth(" + m_uiForm.sample_width->text() + ")\n" +
"SampleThickness(" + m_uiForm.sample_thick->text() + ")\n"
"SampleGeometry(" + m_uiForm.sample_geomid->currentText().at(0) + ")\n";
return exec_reduce;
}
/**
* Run the analysis script
* @param type The data reduction type, 1D or 2D
*/
void SANSRunWindow::handleReduceButtonClick(const QString & type)
{
//new reduction is going to take place, remove the results from the last reduction
resetDefaultOutput();
//The possiblities are batch mode or single run mode
const RunMode runMode =
m_uiForm.single_mode_btn->isChecked() ? SingleMode : BatchMode;
if ( runMode == SingleMode )
{
// Currently the components are moved with each reduce click. Check if a load is necessary
// This must be done before the script is written as we need to get correct values from the
// loaded raw data
handleLoadButtonClick();
}
QString py_code = createAnalysisDetailsScript(type);
if( py_code.isEmpty() )
{
showInformationBox("Error: An error occurred while constructing the reduction code, please check installation.");
return;
}
QString trans_behav;
if( m_uiForm.def_trans->isChecked() )
{
trans_behav += "DefaultTrans";
}
else
{
trans_behav += "NewTrans";
}
const static QString runPythonSep("C++ImplementationReservedC++");
//Need to check which mode we're in
if ( runMode == SingleMode )
{
py_code += "\nreduced = WavRangeReduction(use_def_trans=" + trans_behav + ")\n";
//output the name of the output workspace, this is returned up by the runPythonCode() call below
py_code += "print '"+runPythonSep+"'+reduced";
if( m_uiForm.plot_check->isChecked() )
{
py_code += "\nPlotResult(reduced)\n";
}
}
else
{
//Have we got anything to reduce?
if( m_uiForm.batch_table->rowCount() == 0 )
{
showInformationBox("Error: No run information specified.");
return;
}
QString csv_file(m_uiForm.csv_filename->text());
if( m_dirty_batch_grid )
{
QString selected_file = QFileDialog::getSaveFileName(this, "Save as CSV", m_last_dir);
csv_file = saveBatchGrid(selected_file);
}
py_code = "import SANSBatchMode as batch\n" + py_code;
py_code += "\nbatch.BatchReduce('" + csv_file + "','" + m_uiForm.file_opt->itemData(m_uiForm.file_opt->currentIndex()).toString() + "',"
+ trans_behav;
if( m_uiForm.plot_check->isChecked() )
{
py_code += ", plotresults = True";
}
if( m_uiForm.log_colette->isChecked() )
{
py_code += ", verbose = True";
}
py_code += ")";
}
int idtype(0);
if( type.startsWith("2") ) idtype = 1;
//Disable buttons so that interaction is limited while processing data
setProcessingState(true, idtype);
m_lastreducetype = idtype;
//Execute the code
QString pythonStdOut = runPythonCode(py_code, false);
if ( runMode == SingleMode )
{
QString reducedWS = pythonStdOut.split(runPythonSep)[1];
reducedWS = reducedWS.split("\n")[0];
resetDefaultOutput(reducedWS);
}
// Mark that a reload is necessary to rerun the same reduction
forceDataReload();
//Reenable stuff
setProcessingState(false, idtype);
//If we used a temporary file in batch mode, remove it
if( m_uiForm.batch_mode_btn->isChecked() && !m_tmp_batchfile.isEmpty() )
{
QFile tmp_file(m_tmp_batchfile);
tmp_file.remove();
}
checkLogFlags();
}
/**
* Plot button slot (deprecated)
*/
void SANSRunWindow::handlePlotButtonClick()
{
SANSPlotDialog dialog(this);
dialog.setAvailableData(currentWorkspaceList());
connect(&dialog, SIGNAL(pythonCodeConstructed(const QString&)), this, SIGNAL(runAsPythonScript(const QString&)));
dialog.exec();
}
void SANSRunWindow::handleRunFindCentre()
{
if( m_uiForm.beamstart_box->currentIndex() == 1 && (m_uiForm.beam_x->text().isEmpty() || m_uiForm.beam_y->text().isEmpty()) )
{
showInformationBox("Current centre postion is invalid, please check input.");
return;
}
// Start iteration
updateCentreFindingStatus("::SANS::Loading data");
handleLoadButtonClick();
// Disable interaction
setProcessingState(true, 0);
// This checks whether we have a sample run and that it has been loaded
QString py_code = createAnalysisDetailsScript("1D");
if( py_code.isEmpty() )
{
setProcessingState(false, 0);
return;
}
if( m_uiForm.beam_rmin->text().isEmpty() )
{
m_uiForm.beam_rmin->setText("60");
}
if( m_uiForm.beam_rmax->text().isEmpty() )
{
if( m_uiForm.inst_opt->currentIndex() == 0 )
{
m_uiForm.beam_rmax->setText("200");
}
else
{
m_uiForm.beam_rmax->setText("280");
}
}
if( m_uiForm.beam_iter->text().isEmpty() )
{
m_uiForm.beam_iter->setText("15");
}
//Find centre function
py_code += "FindBeamCentre(rlow=" + m_uiForm.beam_rmin->text() + ",rupp=" + m_uiForm.beam_rmax->text() +
",MaxIter=" + m_uiForm.beam_iter->text() + ",";
if( m_uiForm.beamstart_box->currentIndex() == 0 )
{
py_code += "xstart = None, ystart = None)\n";
}
else
{
py_code += "xstart=float(" + m_uiForm.beam_x->text() + ")/1000.,ystart=float(" + m_uiForm.beam_y->text() + ")/1000.),\n";
}
updateCentreFindingStatus("::SANS::Iteration 1");
m_uiForm.beamstart_box->setFocus();
//Execute the code
//Connect up the logger to handle updating the centre finding status box
connect(this, SIGNAL(logMessageReceived(const QString&)), this,
SLOT(updateCentreFindingStatus(const QString&)));
disconnect(this, SIGNAL(logMessageReceived(const QString&)), this, SLOT(updateLogWindow(const QString&)));
runReduceScriptFunction(py_code);
disconnect(this, SIGNAL(logMessageReceived(const QString&)), this,
SLOT(updateCentreFindingStatus(const QString&)));
connect(this, SIGNAL(logMessageReceived(const QString&)), this, SLOT(updateLogWindow(const QString&)));
QString coordstr = runReduceScriptFunction("printParameter('XBEAM_CENTRE');printParameter('YBEAM_CENTRE')\n");
QString result("");
if( coordstr.isEmpty() )
{
result = "::SANS::No coordinates returned!";
}
else
{
//Remove all internal whitespace characters and replace with single space
coordstr = coordstr.simplified();
QStringList xycoords = coordstr.split(" ");
if( xycoords.count() == 2 )
{
double coord = xycoords[0].toDouble();
m_uiForm.beam_x->setText(QString::number(coord*1000.));
coord = xycoords[1].toDouble();
m_uiForm.beam_y->setText(QString::number(coord*1000.));
result = "::SANS::Coordinates updated";
}
else
{
result = "::SANS::Incorrect number of parameters returned from function, check script.";
}
}
updateCentreFindingStatus(result);
//Reenable stuff
setProcessingState(false, 0);
}
/** Save the output workspace from a single run reduction (i.e. the
* workspace m_outputWS) in all the user selected formats
*/
void SANSRunWindow::handleDefSaveClick()
{
const QString fileBase = m_uiForm.outfile_edit->text();
if (fileBase.isEmpty())
{
QMessageBox::warning(this, "Filename required", "A filename must be entred into the text box above to save this file");
}
QString saveCommand;
for(SavFormatsConstIt i = m_savFormats.begin(); i != m_savFormats.end(); ++i)
{//the key is the check box
if (i.key()->isChecked())
{// and value() is the name of the algorithm associated with that chackbox
QString algName = i.value();
QString ext = SaveWorkspaces::getSaveAlgExt(algName);
QString fname = fileBase.endsWith(ext) ? fileBase : fileBase+ext;
saveCommand += algName+"('"+m_outputWS+"','"+fname+"')\n";
}
}
runPythonCode(saveCommand);
}
/**
* A ComboBox option change
* @param new_index The new index that has been set
*/
void SANSRunWindow::handleStepComboChange(int new_index)
{
if( !sender() ) return;
QString origin = sender()->objectName();
if( origin.startsWith("wav") )
{
if( new_index == 0 ) m_uiForm.wav_step_lbl->setText("stepping");
else m_uiForm.wav_step_lbl->setText("dW / W");
}
else if( origin.startsWith("q_dq") )
{
if( new_index == 0 )
{
m_uiForm.q_stack->setCurrentIndex(0);
m_uiForm.q_step_lbl->setText("stepping");
}
else if( new_index == 1 )
{
m_uiForm.q_stack->setCurrentIndex(0);
m_uiForm.q_step_lbl->setText("dQ / Q");
}
else
{
m_uiForm.q_stack->setCurrentIndex(1);
}
}
else
{
if( new_index == 0 ) m_uiForm.qy_step_lbl->setText("XY step");
else m_uiForm.qy_step_lbl->setText("dQ / Q");
}
}
/**
* Called when the show mask button has been clicked
*/
void SANSRunWindow::handleShowMaskButtonClick()
{
QString analysis_script = createAnalysisDetailsScript("1D");
analysis_script += "\nViewCurrentMask()";
m_uiForm.showMaskBtn->setEnabled(false);
m_uiForm.showMaskBtn->setText("Working...");
runReduceScriptFunction(analysis_script);
m_uiForm.showMaskBtn->setEnabled(true);
m_uiForm.showMaskBtn->setText("Display mask");
}
/**
* A different instrument has been selected
* @throw runtime_error if the instrument doesn't have exactly two detectors
*/
void SANSRunWindow::handleInstrumentChange(int index)
{
//Inform the Python objects of the change
QString pythonSetInst =
m_uiForm.inst_opt->itemData(m_uiForm.inst_opt->currentIndex()).toString();
runReduceScriptFunction(pythonSetInst);
fillDetectNames(m_uiForm.detbank_sel);
QString detect = runReduceScriptFunction(
"printParameter('INSTRUMENT.curDetector().name()')");
int ind = m_uiForm.detbank_sel->findText(detect);
if( ind >= 0 && ind < 2 )
{
m_uiForm.detbank_sel->setCurrentIndex(ind);
}
const QString instrument = m_uiForm.inst_opt->itemText(index);
if( instrument == "LOQ" )
{
m_uiForm.beam_rmin->setText("60");
m_uiForm.beam_rmax->setText("200");
m_uiForm.geom_stack->setCurrentIndex(0);
// Set allowed extensions
m_uiForm.file_opt->clear();
m_uiForm.file_opt->addItem("raw", QVariant(".raw"));
}
else if ( instrument == "SANS2D" )
{
m_uiForm.beam_rmin->setText("60");
m_uiForm.beam_rmax->setText("280");
m_uiForm.geom_stack->setCurrentIndex(1);
//File extensions
m_uiForm.file_opt->clear();
m_uiForm.file_opt->addItem("raw", QVariant(".raw"));
m_uiForm.file_opt->addItem("nexus", QVariant(".nxs"));
}
m_cfg_loaded = false;
}
/** Record if the user has changed the default filename, because then we don't
* change it
*/
void SANSRunWindow::setUserFname()
{
m_userFname = true;
}
/**
* Update the centre finding status label
* @param msg The message string
*/
void SANSRunWindow::updateCentreFindingStatus(const QString & msg)
{
static QString prefix = "::SANS";
if( msg.startsWith(prefix) )
{
QStringList sections = msg.split("::");
QString txt = sections.at(2);
m_uiForm.centre_logging->append(txt);
if( sections.at(1) == "SANSIter" )
{
m_uiForm.centre_stat->setText(txt);
}
}
}
/**Enables the default save button, saveDefault_Btn, if there is an output workspace
* stored in m_outputWS and text in outfile_edit
*/
void SANSRunWindow::enableOrDisableDefaultSave()
{
if ( m_outputWS.isEmpty() )
{//setEnabled(false) gets run below
}
else if ( m_uiForm.outfile_edit->text().isEmpty() )
{//setEnabled(false) gets run below
}
else
{//ensure that one format box is checked
for(SavFormatsConstIt i=m_savFormats.begin(); i != m_savFormats.end(); ++i)
{
if (i.key()->isChecked())
{
m_uiForm.saveDefault_btn->setEnabled(true);
return;
}
}
}
m_uiForm.saveDefault_btn->setEnabled(false);
}
/**
* Update the logging window with status messages
* @param msg The message received
*/
void SANSRunWindow::updateLogWindow(const QString & msg)
{
static QString prefix = "::SANS";
if( msg.startsWith(prefix) )
{
QString txt = msg.section("::",2);
bool logwarnings = txt.contains("warning", Qt::CaseInsensitive);
if( m_uiForm.verbose_check->isChecked() || logwarnings || m_uiForm.log_colette->isChecked() )
{
if( logwarnings )
{
m_log_warnings = true;
m_uiForm.logging_field->setTextColor(Qt::red);
}
else
{
m_uiForm.logging_field->setTextColor(Qt::black);
}
m_uiForm.logging_field->append(txt);
}
}
}
/**
* Switch between run modes
* @param mode_id Indicates which toggle has been pressed
*/
void SANSRunWindow::switchMode(int mode_id)
{
if( mode_id == SANSRunWindow::SingleMode )
{
m_uiForm.mode_stack->setCurrentIndex(0);
m_uiForm.load_dataBtn->setEnabled(true);
}
else if( mode_id == SANSRunWindow::BatchMode )
{
m_uiForm.mode_stack->setCurrentIndex(1);
m_uiForm.load_dataBtn->setEnabled(false);
}
else {}
}
/**
* Paste to the batch table
*/
void SANSRunWindow::pasteToBatchTable()
{
if( !m_cfg_loaded )
{
showInformationBox("Please load the relevant user file before continuing.");
return;
}
QClipboard *clipboard = QApplication::clipboard();
QString copied_text = clipboard->text();
if( copied_text.isEmpty() ) return;
QStringList runlines = copied_text.split("\n");
QStringListIterator sitr(runlines);
int errors(0);
while( sitr.hasNext() )
{
QString line = sitr.next().simplified();
if( !line.isEmpty() )
{
errors += addBatchLine(line);
}
}
if( errors > 0 )
{
showInformationBox("Warning: " + QString::number(errors) + " malformed lines detected in pasted text. Lines skipped.");
}
if( m_uiForm.batch_table->rowCount() > 0 )
{
m_dirty_batch_grid = true;
setProcessingState(false, -1);
}
}
/**
* Clear the batch table
*/
void SANSRunWindow::clearBatchTable()
{
int row_count = m_uiForm.batch_table->rowCount();
for( int i = row_count - 1; i >= 0; --i )
{
m_uiForm.batch_table->removeRow(i);
}
m_dirty_batch_grid = false;
m_tmp_batchfile = "";
}
/**
* Clear the logger field
*/
void SANSRunWindow::clearLogger()
{
m_uiForm.logging_field->clear();
m_uiForm.tabWidget->setTabText(4, "Logging");
}
/**
* Handle a verbose mode check box state change
* state The new state
*/
void SANSRunWindow::verboseMode(int state)
{
if( state == Qt::Checked )
{
runReduceScriptFunction("SetVerboseMode(True)");
}
else if( state == Qt::Unchecked )
{
runReduceScriptFunction("SetVerboseMode(False)");
}
else {}
}
/**
* Update the transmission range boxes if the "Use Transmission defaults is checked"
*/
void SANSRunWindow::updateTransInfo(int state)
{
if( state == Qt::Checked )
{
m_uiForm.trans_min->setText(runReduceScriptFunction("printParameter('TRANS_WAV1_FULL'),"));
m_uiForm.trans_max->setText(runReduceScriptFunction("printParameter('TRANS_WAV2_FULL'),"));
}
}
/** Record the output workspace name, if there is no output
* workspace pass an empty string or an empty argument list
* @param wsName the name of the output workspace or empty for no output
*/
void SANSRunWindow::resetDefaultOutput(const QString & wsName)
{
m_outputWS = wsName;
enableOrDisableDefaultSave();
if ( ! m_userFname )
{
m_uiForm.outfile_edit->setText(wsName);
}
}
/**
* Run a SANS assign command
* @param key The key of the edit box to assign from
* @param logs An output parameter specifying the log data
*/
bool SANSRunWindow::runAssign(int key, QString & logs)
{
//Work out if sans/trans and sample/can
bool is_trans(false);
if( key > 2 && key < 6 )
{
is_trans = true;
}
bool is_can(false);
if( key == 1 || key == 4 )
{
is_can = true;
}
// Default extension if the box run number does not contain one
QString extension = m_uiForm.file_opt->itemData(m_uiForm.file_opt->currentIndex()).toString();
QString run_number = m_run_no_boxes.value(key)->text();
if( QFileInfo(run_number).completeSuffix().isEmpty() )
{
if( run_number.endsWith(".") )
{
run_number.chop(1);
}
run_number += extension;
}
bool status(true);
//need something to place between names printed by Python that won't be intepreted as the names or removed as white space
const static QString runPythonSep("C++ImplementationReservedC++");
if( is_trans )
{
QString direct_run = m_run_no_boxes.value(key + 3)->text();
if( QFileInfo(direct_run).completeSuffix().isEmpty() )
{
if( direct_run.endsWith(".") )
{
direct_run.chop(1);
}
direct_run += extension;
}
QString assign_fn;
if( is_can )
{
assign_fn = "TransmissionCan";
}
else
{
assign_fn = "TransmissionSample";
}
assign_fn += "('"+run_number+"','"+direct_run+"', reload = True";
assign_fn += ", period = " + QString::number(getPeriod(key))+")";
//assign the workspace name to a Python variable and read back some details
QString pythonC="t1, t2 = " + assign_fn + ";print t1,'"+runPythonSep+"',t2";
QString ws_names = runReduceScriptFunction(pythonC);
//read the informtion returned from Python
QString trans_ws = ws_names.section(runPythonSep, 0,0).trimmed();
QString direct_ws = ws_names.section(runPythonSep, 1).trimmed();
status = ( ! trans_ws.isEmpty() ) && ( ! direct_ws.isEmpty() );
//if the workspaces have loaded
if (status)
{//save the workspace names
m_workspace_names.insert(key, trans_ws);
m_workspace_names.insert(key + 3, direct_ws);
//and display to the user how many periods are in the run
QString pythonVar = is_can ? "TRANS_CAN_N_PERIODS" : "_TRANS_SAMPLE_N_PERIODS";
int nPeriods =
runReduceScriptFunction("printParameter('"+pythonVar+"'),").toInt();
setNumberPeriods(key, nPeriods);
pythonVar = is_can ? "DIRECT_CAN_N_PERIODS" : "DIRECT_SAMPLE_N_PERIODS";
nPeriods =
runReduceScriptFunction("printParameter('"+pythonVar+"'),").toInt();
setNumberPeriods(key + 3, nPeriods);
}
else
{//workspaces didn't load so remove the (out of date) period information
unSetPeriods(key);
unSetPeriods(key+3);
}
}
else
{
QString assign_fn;
if( is_can )
{
assign_fn = "AssignCan";
}
else
{
assign_fn = "AssignSample";
}
assign_fn += "('" + run_number + "', reload = True";
assign_fn += ", period = " + QString::number(getPeriod(key)) + ")";
//assign the workspace name to a Python variable and read back some details
QString run_info = "SCATTER_SAMPLE, logvalues = " + assign_fn + ";print SCATTER_SAMPLE,'"+runPythonSep+"',logvalues";
run_info = runReduceScriptFunction(run_info);
//read the informtion returned from Python
QString base_workspace = run_info.section(runPythonSep, 0, 0).trimmed();
logs = run_info.section(runPythonSep, 1);
if( !logs.isEmpty() )
{
trimPyMarkers(logs);
}
status = ! base_workspace.isEmpty();
//if the workspace was loaded
if (status)
{//save the workspace name
m_workspace_names.insert(key, base_workspace);
//and display to the user how many periods are in the run
QString pythonVar = is_can ? "_CAN_N_PERIODS" : "_SAMPLE_N_PERIODS";
int nPeriods =
runReduceScriptFunction("printParameter('"+pythonVar+"'),").toInt();
setNumberPeriods(key, nPeriods);
}
else
{
unSetPeriods(key);
}
}
return status;
}
/** Gets the detectors that the instrument has and fills the
* combination box with these, there must exactly two detectors
* @parma output[out] this combination box will be cleared and filled with the new names
* @throw runtime_error if there aren't exactly two detectors
*/
void SANSRunWindow::fillDetectNames(QComboBox *output)
{
QString detsTuple = runReduceScriptFunction(
"printParameter('INSTRUMENT.listDetectors()')");
if (detsTuple.isEmpty())
{//this happens if the run Python signal hasn't yet been connected
return;
}
QStringList dets = detsTuple.split("'", QString::SkipEmptyParts);
// the tuple will be of the form ('det1', 'det2'), hence the split should return 5 parts
if ( dets.count() != 5 )
{
QMessageBox::critical(this, "Can't Load Instrument", "The instrument must have only 2 detectors. Can't proceed with this instrument");
throw std::runtime_error("Invalid instrument setting, you should be able to continue by selecting a valid instrument");
}
output->setItemText(0, dets[1]);
output->setItemText(1, dets[3]);
}
/** gets the number entered into the periods box
* @param key The box this applies to
* @return the entry number the user entered into the box, or -1 if the box is empty
*/
int SANSRunWindow::getPeriod(const int key)
{
QLabel *label = qobject_cast<QLabel*>(m_period_lbls.value(key));
QLineEdit *userentry = qobject_cast<QLineEdit*>(label->buddy());
if ( ! userentry->text().isEmpty() )
{
return userentry->text().toInt();
}
else
{
return -1;
}
}
/** Set number of periods for the given workspace
* @param key The box this applies to
* @param num the number of periods there are known to be
*/
void SANSRunWindow::setNumberPeriods(const int key, const int num)
{
QLabel *label = qobject_cast<QLabel*>(m_period_lbls.value(key));
QLineEdit *userentry = qobject_cast<QLineEdit*>(label->buddy());
if (num > 0)
{
label->setText("/ " + QString::number(num));
if (userentry->text().isEmpty())
{//default period to analysis is the first one
userentry->setText("1");
}
}
else
{
userentry->clear();
label->setText("/ ??");
}
}
/** Blank the periods information in a box
* @param key The box this applies to
*/
void SANSRunWindow::unSetPeriods(const int key)
{
setNumberPeriods(key, -1);
}
/** Checks if the workspace is a group and returns the first member of group, throws
* if nothing can be retrived
* @param[in] workspace the group to examine
* @param[in] member entry or period number of the requested workspace, these start at 1
* @return the first member of the passed group
* @throw NotFoundError if a workspace can't be returned
*/
Mantid::API::MatrixWorkspace_sptr SANSRunWindow::getGroupMember(Mantid::API::Workspace_const_sptr in, const int member) const
{
Mantid::API::WorkspaceGroup_const_sptr group =
boost::dynamic_pointer_cast<const Mantid::API::WorkspaceGroup>(in);
if ( ! group )
{
throw Mantid::Kernel::Exception::NotFoundError("Problem retrieving workspace ", in->getName());
}
const std::vector<std::string> gNames = group->getNames();
//currently the names array starts with the name of the group
if ( gNames.size() < member + 1 )
{
throw Mantid::Kernel::Exception::NotFoundError("Workspace group" + in->getName() + " doesn't have " + boost::lexical_cast<std::string>(member) + " entries", member);
}
//throws NotFoundError if the workspace couldn't be found
Mantid::API::Workspace_sptr base = Mantid::API::AnalysisDataService::Instance().retrieve(gNames[member]);
Mantid::API::MatrixWorkspace_sptr memberWS =
boost::dynamic_pointer_cast<Mantid::API::MatrixWorkspace>(base);
if ( ! memberWS )
{
throw Mantid::Kernel::Exception::NotFoundError("Problem getting period number " + boost::lexical_cast<std::string>(member) + " from group workspace " + base->getName(), member);
}
return memberWS;
}
/**
* Get a properly qualified workspace name for the given key
*/
QString SANSRunWindow::getWorkspaceName(int key)
{
return m_workspace_names.value(key);
}
/**
* Handle a delete notification from Mantid
* @param p_dnf A Mantid delete notification
*/
void SANSRunWindow::handleMantidDeleteWorkspace(Mantid::API::WorkspaceDeleteNotification_ptr p_dnf)
{
QString wksp_name = QString::fromStdString(p_dnf->object_name());
int names_count = m_workspace_names.count();
for( int key = 0; key < names_count; ++key )
{
if( wksp_name == m_workspace_names.value(key) )
{
forceDataReload();
return;
}
}
}
/**
* Format a double as a string
* @param value The double to convert to a string
* @param colour The colour
* @param format The format char
* @param precision The precision
*/
QString SANSRunWindow::formatDouble(double value, const QString & colour, char format, int precision)
{
return QString("<font color='") + colour + QString("'>") + QString::number(value, format, precision) + QString("</font>");
}
/**
* Raise a message if current status allows
* @param msg The message to include in the box
* @param index The tab index to set as current
*/
void SANSRunWindow::raiseOneTimeMessage(const QString & msg, int index)
{
if( m_warnings_issued ) return;
if( index >= 0 )
{
m_uiForm.tabWidget->setCurrentIndex(index);
}
showInformationBox(msg);
m_warnings_issued = true;
}
/**
* Rest the geometry details box
*/
void SANSRunWindow::resetGeometryDetailsBox()
{
QString blank("-");
//LOQ
m_uiForm.dist_mod_mon->setText(blank);
//SANS2D
m_uiForm.dist_mon_s2d->setText(blank);
m_uiForm.dist_sample_ms_s2d->setText(blank);
m_uiForm.dist_can_ms_s2d->setText(blank);
m_uiForm.dist_bkgd_ms_s2d->setText(blank);
for(int i = 0; i < 3; ++i )
{
//LOQ
QMutableHashIterator<QString,QLabel*> litr(m_loq_detlabels[i]);
while(litr.hasNext())
{
litr.next();
litr.value()->setText(blank);
}
//SANS2D
QMutableHashIterator<QString,QLabel*> sitr(m_s2d_detlabels[i]);
while(sitr.hasNext())
{
sitr.next();
sitr.value()->setText(blank);
}
}
}
void SANSRunWindow::cleanup()
{
Mantid::API::AnalysisDataServiceImpl & ads = Mantid::API::AnalysisDataService::Instance();
std::set<std::string> workspaces = ads.getObjectNames();
std::set<std::string>::const_iterator iend = workspaces.end();
for( std::set<std::string>::const_iterator itr = workspaces.begin(); itr != iend; ++itr )
{
QString name = QString::fromStdString(*itr);
if( name.endsWith("_raw") || name.endsWith("_nxs"))
{
ads.remove(*itr);
}
}
}
/**
* Add a csv line to the batch grid
* @param csv_line Add a line of csv text to the grid
* @param separator An optional separator, default = ","
*/
int SANSRunWindow::addBatchLine(QString csv_line, QString separator)
{
//Try to detect separator if one is not specified
if( separator.isEmpty() )
{
if( csv_line.contains(",") )
{
separator = ",";
}
else
{
separator = " ";
}
}
QStringList elements = csv_line.split(separator);
//Insert new row
int row = m_uiForm.batch_table->rowCount();
m_uiForm.batch_table->insertRow(row);
int nelements = elements.count() - 1;
bool error(false);
for( int i = 0; i < nelements; )
{
QString cola = elements.value(i);
QString colb = elements.value(i+1);
if( m_allowed_batchtags.contains(cola) )
{
if( !m_allowed_batchtags.contains(colb) )
{
if( !colb.isEmpty() && !cola.contains("background") )
{
m_uiForm.batch_table->setItem(row, m_allowed_batchtags.value(cola), new QTableWidgetItem(colb));
}
i += 2;
}
else
{
++i;
}
}
else
{
error = true;
break;
}
}
if( error )
{
m_uiForm.batch_table->removeRow(row);
return 1;
}
return 0;
}
/**
* Save the batch file to a CSV file.
* @param filename An optional filename. If none is given then a temporary file is used and its name returned
*/
QString SANSRunWindow::saveBatchGrid(const QString & filename)
{
QString csv_filename = filename;
if( csv_filename.isEmpty() )
{
//Generate a temporary filename
QTemporaryFile tmp;
tmp.open();
csv_filename = tmp.fileName();
tmp.close();
m_tmp_batchfile = csv_filename;
}
QFile csv_file(csv_filename);
if( !csv_file.open(QIODevice::WriteOnly|QIODevice::Text) )
{
showInformationBox("Error: Cannot write to CSV file \"" + csv_filename + "\".");
return "";
}
QTextStream out_strm(&csv_file);
int nrows = m_uiForm.batch_table->rowCount();
const QString separator(",");
for( int r = 0; r < nrows; ++r )
{
for( int c = 0; c < 7; ++c )
{
out_strm << m_allowed_batchtags.key(c) << separator;
if( QTableWidgetItem* item = m_uiForm.batch_table->item(r, c) )
{
out_strm << item->text();
}
if( c < 6 ) out_strm << separator;
}
out_strm << "\n";
}
csv_file.close();
if( !filename.isEmpty() )
{
m_tmp_batchfile = "";
m_dirty_batch_grid = false;
m_uiForm.csv_filename->setText(csv_filename);
}
else
{
m_uiForm.csv_filename->clear();
}
return csv_filename;
}
void SANSRunWindow::checkLogFlags()
{
if( m_log_warnings )
{
m_uiForm.tabWidget->setTabText(4, "Logging - WARNINGS");
// m_uiForm.tabWidget->tabBar()->setTabTextColor(4, QColor("red"));
}
m_log_warnings = false;
}