Skip to content
Snippets Groups Projects
SANSRunWindow.cpp 151 KiB
Newer Older
//----------------------
// Includes
//----------------------
#include "MantidQtCustomInterfaces/SANSRunWindow.h"
#include "MantidQtCustomInterfaces/SANSAddFiles.h"
#include "MantidQtAPI/ManageUserDirectories.h"
#include "MantidQtAPI/FileDialogHandler.h"
#include "MantidKernel/FacilityInfo.h"
Peter Parker's avatar
Peter Parker committed
#include "MantidKernel/PropertyWithValue.h"
#include "MantidKernel/Logger.h"
#include "MantidKernel/Exception.h"
#include "MantidAPI/FrameworkManager.h"
#include "MantidAPI/IAlgorithm.h"
#include "MantidAPI/PropertyManagerDataService.h"
#include "MantidAPI/IEventWorkspace.h"
#include <QTreeWidgetItem>
#include <QMessageBox>
#include <QInputDialog>
#include <QApplication>
#include <QClipboard>
#include <QTemporaryFile>
#include <QDesktopServices>
#include <QUrl>
#include <Poco/StringTokenizer.h>
#include <boost/lexical_cast.hpp>
#include "MantidGeometry/IDetector.h"

#include "MantidQtCustomInterfaces/SANSEventSlicing.h"

#include <boost/assign.hpp>
#include <boost/foreach.hpp>
#include <boost/function.hpp>
#include <boost/tuple/tuple.hpp>
#include <cmath>
using Mantid::detid_t;
//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;
using namespace Mantid;
using Mantid::Geometry::Instrument_const_sptr;
namespace
{
  /// static logger for main window
  Logger g_log("SANSRunWindow");
  /// static logger for centre finding
  Logger g_centreFinderLog("CentreFinder");

  typedef boost::shared_ptr<Kernel::PropertyManager> ReductionSettings_sptr;
  
  /**
   * Returns the PropertyManager object that is used to store the settings
   * used by the reduction.
   *
   * There is a corresponding function in scripts/SANS/isis_reducer.py with
   * more information.
   *
   * @returns the reduction settings.
   */
  ReductionSettings_sptr getReductionSettings()
  {
    // Must match name of the PropertyManager used in the reduction.
    static const std::string SETTINGS_PROP_MAN_NAME = "ISISSANSReductionSettings";

    if( !PropertyManagerDataService::Instance().doesExist(SETTINGS_PROP_MAN_NAME) )
    {
      g_log.debug() << "Creating reduction settings PropertyManager object, with name "
                    << SETTINGS_PROP_MAN_NAME << ".";
      
      const auto propertyManager = boost::make_shared<Kernel::PropertyManager>();
      PropertyManagerDataService::Instance().add(SETTINGS_PROP_MAN_NAME, propertyManager);

      return propertyManager;
    }

    return PropertyManagerDataService::Instance().retrieve(SETTINGS_PROP_MAN_NAME);
  }
  /**
   * Returns the value of the setting with given name, unless the setting does not
   * exist in which case the given defaultValue is returned.
   *
   * @param settingName :: the name of the setting who's value to return
   * @param defaultValue :: the value to return if the setting does not exist
   *
   * @returns the setting value else defaultValue if the setting does not exist
   */
  QString getSettingWithDefault(const QString & settingName, const QString & defaultValue )
  {
    const auto settings = getReductionSettings();
    
    if( settings->existsProperty(settingName.toStdString()) )
      return QString::fromStdString(settings->getPropertyValue(settingName.toStdString()));
    else
      return defaultValue;
  }
Peter Parker's avatar
Peter Parker committed

  /**
   * Convenience method to set the setting with given name to the given value.
   * If a property with the given name does not exist, then one is created.
   *
   * We could have a templated method at some later date, but at the moment this
   * only works for string properties.
   *
   * @param settingName :: the name of the setting to set
   * @param settingValue :: the value to set this setting with
   */
  void setStringSetting(const QString & settingName, const QString & settingValue )
  {
    const auto settings = getReductionSettings();
    const auto name = settingName.toStdString();
    const auto value = settingValue.toStdString();
    
    if( !settings->existsProperty(name) )
      settings->declareProperty(new Kernel::PropertyWithValue<std::string>(name, ""), value);
    else
      settings->setProperty(name, value);
  }
//----------------------------------------------
//----------------------------------------------
SANSRunWindow::SANSRunWindow(QWidget *parent)
    : UserSubWindow(parent), m_addFilesTab(NULL), m_displayTab(NULL),
      m_diagnosticsTab(NULL), m_saveWorkspaces(NULL), m_ins_defdir(""),
      m_last_dir(""), m_cfg_loaded(true), m_userFname(false), m_sample_file(),
      m_reducemapper(NULL), m_warnings_issued(false), m_force_reload(false),
      m_newInDir(*this, &SANSRunWindow::handleInputDirChange),
      m_delete_observer(*this, &SANSRunWindow::handleMantidDeleteWorkspace),
      m_s2d_detlabels(), m_loq_detlabels(), m_allowed_batchtags(),
      m_have_reducemodule(false), m_dirty_batch_grid(false),
      m_tmp_batchfile(""), m_batch_paste(NULL), m_batch_clear(NULL),
      m_mustBeDouble(NULL), m_doubleValidatorZeroToMax(NULL),
      m_intValidatorZeroToMax(NULL),
      slicingWindow(NULL) {
  ConfigService::Instance().addObserver(m_newInDir);
    ConfigService::Instance().removeObserver(m_newInDir);
    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;
    }
    //we've cleaned up the best we can, move on
//--------------------------------------------
//--------------------------------------------
  g_log.debug("Initializing interface layout");
  m_uiForm.setupUi(this);
  m_uiForm.inst_opt->addItem("LARMOR");
  m_uiForm.inst_opt->addItem("LOQ");
  m_uiForm.inst_opt->addItem("SANS2D");
  m_uiForm.inst_opt->addItem("SANS2DTUBES");

  m_reducemapper = new QSignalMapper(this);

  //Set column stretch on the mask table
  m_uiForm.mask_table->horizontalHeader()->setStretchLastSection(true);

  m_uiForm.tabWidget->setCurrentWidget(m_uiForm.runNumbers);
  // 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()),this,SLOT(switchMode()));
  connect(m_uiForm.batch_mode_btn, SIGNAL(clicked()), this,SLOT(switchMode()));

  //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()));

  // Main Logging
  m_uiForm.logging_field->attachLoggingChannel();
  connect(m_uiForm.logging_field, SIGNAL(warningReceived(const QString &)), this, SLOT(setLoggerTabTitleToWarn()));
  connect(m_uiForm.logger_clear, SIGNAL(clicked()), this, SLOT(clearLogger()));

  // Centre finder logger
  m_uiForm.centre_logging->attachLoggingChannel();
  connect(m_uiForm.clear_centre_log, SIGNAL(clicked()), m_uiForm.centre_logging, SLOT(clear()));
  m_runFiles.reserve(6);
  //Text edit map
  m_runFiles.push_back(m_uiForm.scatterSample);
  m_runFiles.push_back(m_uiForm.scatCan);

  m_runFiles.push_back(m_uiForm.transmis);
  m_runFiles.push_back(m_uiForm.transCan);

  m_runFiles.push_back(m_uiForm.direct);
  m_runFiles.push_back(m_uiForm.dirCan);
  std::vector<MWRunFiles *>::const_iterator it = m_runFiles.begin();
  for ( ; it != m_runFiles.end(); ++it )
  {
    (*it)->doButtonOpt(MWRunFiles::Icon);
  }

  connectFirstPageSignals();
  initAnalysDetTab();

  if( ! m_addFilesTab )
  {//sets up the AddFiles tab which must be deleted in the destructor
    m_addFilesTab = new SANSAddFiles(this, &m_uiForm);
  //diagnostics tab
  if(!m_diagnosticsTab)
  {
    m_diagnosticsTab = new SANSDiagnostics(this,&m_uiForm);
  connect(this,SIGNAL(userfileLoaded()),m_diagnosticsTab,SLOT(enableMaskFileControls()));
  AnalysisDataService::Instance().notificationCenter.addObserver(m_delete_observer);
  // Create the "Display" tab
  if ( ! m_displayTab )
  {
    m_displayTab = new SANSPlotSpecial(this);
    m_uiForm.displayLayout->addWidget(m_displayTab);
  }
  const QString ISIS_SANS_WIKI = "http://www.mantidproject.org/ISIS_SANS:";
  m_helpPageUrls[Tab::RUN_NUMBERS]        = ISIS_SANS_WIKI + "_Run_Numbers";
  m_helpPageUrls[Tab::REDUCTION_SETTINGS] = ISIS_SANS_WIKI + "_Reduction_Settings";
  m_helpPageUrls[Tab::GEOMETRY]           = ISIS_SANS_WIKI + "_Geometry";
  m_helpPageUrls[Tab::MASKING]            = ISIS_SANS_WIKI + "_Masking";
  m_helpPageUrls[Tab::LOGGING]            = ISIS_SANS_WIKI + "_Logging";
  m_helpPageUrls[Tab::ADD_RUNS]           = ISIS_SANS_WIKI + "_Add_Runs";
  m_helpPageUrls[Tab::DIAGNOSTICS]        = ISIS_SANS_WIKI + "_Diagnostics";
  m_helpPageUrls[Tab::ONE_D_ANALYSIS]     = ISIS_SANS_WIKI + "_1D_Analysis";

  // connect up phi masking on analysis tab to be in sync with info on masking tab 
  connect(m_uiForm.mirror_phi, SIGNAL(clicked()), this, SLOT(phiMaskingChanged())); 
  connect(m_uiForm.detbank_sel, SIGNAL(currentIndexChanged(int)), this, SLOT(phiMaskingChanged(int))); 
  connect(m_uiForm.phi_min, SIGNAL(editingFinished()), this, SLOT(phiMaskingChanged())); 
  connect(m_uiForm.phi_max, SIGNAL(editingFinished()), this, SLOT(phiMaskingChanged())); 
  connect(m_uiForm.slicePb, SIGNAL(clicked()), this, SLOT(handleSlicePushButton()));
  connect(m_uiForm.pushButton_Help, SIGNAL(clicked()), this, SLOT(openHelpPage()));

  // Setup the Transmission Settings
  initTransmissionSettings();

  // Set the validators
  setValidators();

/** Ssetup the controls for the Analysis Tab on this form
*/
  //Add shortened forms of step types to step boxes
  m_uiForm.q_dq_opt->setItemData(0, "LIN");
  m_uiForm.q_dq_opt->setItemData(1, "LOG");
  m_uiForm.qy_dqy_opt->setItemData(0, "LIN");
//remove the following two lines once the beamfinder is in the new framework
  m_uiForm.wav_dw_opt->setItemData(0, "LIN");
  m_uiForm.wav_dw_opt->setItemData(1, "LOG");

  //the file widget always has a *.* filter, passing an empty list means we get only that
  m_uiForm.floodRearFile->setAlgorithmProperty("CorrectToFile|Filename");
  m_uiForm.floodRearFile->isOptional(true);
  m_uiForm.floodFrontFile->setAlgorithmProperty("CorrectToFile|Filename");
  m_uiForm.floodFrontFile->isOptional(true);
  //the unicode code for the angstrom symbol is 197, doing the below keeps this file ASCII compatible
  static const QChar ANGSROM_SYM(197);
  m_uiForm.wavlength_lb->setText(QString("Wavelength (%1)").arg(ANGSROM_SYM));
  m_uiForm.qx_lb->setText(QString("Qx (%1^-1)").arg(ANGSROM_SYM));
  m_uiForm.qxy_lb->setText(QString("Qxy (%1^-1)").arg(ANGSROM_SYM));
  m_uiForm.transFitOnOff->setText(QString("Trans Fit (%1)").arg(ANGSROM_SYM));
  m_uiForm.transFitOnOff_can->setText(QString("Trans Fit (%1)").arg(ANGSROM_SYM));
  m_uiForm.q_rebin->setToolTip("Any string allowed by the Rebin algorithm may be used");
  makeValidator(m_uiForm.wavRanVal_lb, m_uiForm.wavRanges, m_uiForm.tab_2,
             "A comma separated list of numbers is required here");
/** Formats a Qlabel to be a validator and adds it to the list
*  @param newValid :: a QLabel to use as a validator
*  @param control :: the control whose entry the validator is validates
*  @param tab :: the tab that contains this widgets
*  @param errorMsg :: the tooltip message that the validator should have
*/
void SANSRunWindow::makeValidator(QLabel * const newValid, QWidget * control, QWidget * tab, const QString & errorMsg)
{
  QPalette pal = newValid->palette();
  pal.setColor(QPalette::WindowText, Qt::darkRed);
  newValid->setPalette(pal);
  newValid->setToolTip(errorMsg);
  // regester the validator       and say      where it's control is
  m_validators[newValid] = std::pair<QWidget *, QWidget *>(control, tab);
}

/**
 * Run local Python initialization code
 */
void SANSRunWindow::initLocalPython()
{
  // Import the SANS module and set the correct instrument
  QString result = runPythonCode("try:\n\timport isis_reducer\nexcept (ImportError,SyntaxError), details:\tprint 'Error importing isis_reducer: ' + str(details)\n");
  if( result.trimmed().isEmpty() )
  {
    m_have_reducemodule = true;
  }
  else
  {
    showInformationBox(result);
    m_have_reducemodule = false;
  runPythonCode("import ISISCommandInterface as i\nimport copy");
  runPythonCode("import isis_instrument\nimport isis_reduction_steps");
  handleInstrumentChange();
/** 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.saveNIST_Qxy_check, "SaveNISTDAT");
  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()
{
  //Qt::WA_DeleteOnClose must be set for the dialog to aviod a memory leak
    new SaveWorkspaces(this, m_uiForm.outfile_edit->text(), m_savFormats, m_uiForm.zeroErrorCheckBox->isChecked());
  //this dialog sometimes needs to run Python, pass this to Mantidplot via our runAsPythonScript() signal
  connect(m_saveWorkspaces, SIGNAL(runAsPythonScript(const QString&, bool)),
    this, SIGNAL(runAsPythonScript(const QString&, bool)));
  //we need know if we have a pointer to a valid window or not
  connect(m_saveWorkspaces, SIGNAL(closing()),
    this, SLOT(saveWorkspacesClosed()));
  // Connect the request for a zero-error-free workspace
  // cpp-check does not understand that the input are two references
  // cppcheck-suppress duplicateExpression
  connect(m_saveWorkspaces, SIGNAL(createZeroErrorFreeWorkspace(QString& , QString&)),
          // cppcheck-suppress duplicateExpression
          this, SLOT(createZeroErrorFreeClone(QString&, QString&)));
  // Connect the request for deleting a zero-error-free workspace
  connect(m_saveWorkspaces, SIGNAL(deleteZeroErrorFreeWorkspace(QString&)),
         this, SLOT(deleteZeroErrorFreeClone(QString&) ));
  // Connect to change in the zero-error removal checkbox
  connect(m_uiForm.zeroErrorCheckBox, SIGNAL(stateChanged(int)),
          m_saveWorkspaces, SLOT(onSaveAsZeroErrorFreeChanged(int)));


  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()));
}
/**  Calls connect to fix up all the slots for the run tab to their events
void SANSRunWindow::connectFirstPageSignals()
  connect(m_uiForm.outfile_edit, SIGNAL(textEdited(const QString&)),
    this, SLOT(enableOrDisableDefaultSave()));
  connect(m_uiForm.allowPeriods_ck, SIGNAL(stateChanged(int)), this,
    SLOT(disOrEnablePeriods(const int)));
}
/** Calls connect to fix up all the slots for the analysis details tab to their events
*/
void SANSRunWindow::connectAnalysDetSignals()
{
  connect(m_uiForm.wav_dw_opt, SIGNAL(currentIndexChanged(int)), this, 
  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, 
  connect(m_uiForm.transFit_ck, SIGNAL(stateChanged(int)), this, SLOT(updateTransInfo(int)));
  connect(m_uiForm.transFit_ck_can, SIGNAL(stateChanged(int)), this, SLOT(updateTransInfo(int)));  
  updateTransInfo(m_uiForm.transFit_ck->state());
  m_uiForm.transFit_ck_can->toggle();
  connect(m_uiForm.frontDetQrangeOnOff, SIGNAL(stateChanged(int)), this, SLOT(updateFrontDetQrange(int)));
  updateFrontDetQrange(m_uiForm.frontDetQrangeOnOff->state());

  connect(m_uiForm.enableRearFlood_ck, SIGNAL(stateChanged(int)), this, SLOT(prepareFlood(int)));
  connect(m_uiForm.enableFrontFlood_ck, SIGNAL(stateChanged(int)), this, SLOT(prepareFlood(int)));
  
  connect(m_uiForm.trans_selector_opt, SIGNAL(currentIndexChanged(int)), this, SLOT(transSelectorChanged(int)));
  transSelectorChanged(0);

  connect(m_uiForm.wavRanges, SIGNAL(editingFinished()),
                                    this, SLOT(checkList()));
/**
 * Initialize the widget maps
 */
void SANSRunWindow::initWidgetMaps()
{
  //       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);
  m_allowed_batchtags.insert("user_file",7);
  //            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

/**
 * Restore previous input
 */
void SANSRunWindow::readSettings()
{
  QSettings value_store;
  value_store.beginGroup("CustomInterfaces/SANSRunWindow");
  m_uiForm.userfile_edit->setText(value_store.value("user_file").toString());
  m_last_dir = value_store.value("last_dir", "").toString();

  int index = m_uiForm.inst_opt->findText(
                              value_store.value("instrum", "LOQ").toString());
  // if the saved instrument no longer exists set index to zero
  index = index < 0 ? 0 : index;
  m_uiForm.inst_opt->setCurrentIndex(index);
  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();
  }

  m_ins_defdir = QString::fromStdString(
    ConfigService::Instance().getString("instrumentDefinition.directory"));
  upDateDataDir();
  // Set allowed extensions
  m_uiForm.file_opt->clear();
  m_uiForm.file_opt->addItem("nexus", QVariant(".nxs"));
  m_uiForm.file_opt->addItem("raw", QVariant(".raw"));
  //Set old file extension
  m_uiForm.file_opt->setCurrentIndex(value_store.value("fileextension", 0).toInt());
  
  m_uiForm.allowPeriods_ck->setChecked(
                   value_store.value("allow_periods",false).toBool());
  int i = m_uiForm.wav_dw_opt->findText(
    value_store.value("wave_binning", "Linear").toString());
  i = i > -1 ? i : 0;
  m_uiForm.wav_dw_opt->setCurrentIndex(i);
  //ensure this is called once even if the index hadn't changed
  handleWavComboChange(i);

  g_log.debug() << "Found previous data directory " << "\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.saveNIST_Qxy_check->setChecked(valueStore.value("NIST_Qxy",false).toBool());
  m_uiForm.saveRKH_check->setChecked(valueStore.value("RKH", false).toBool());
  m_uiForm.saveCSV_check->setChecked(valueStore.value("CSV", false).toBool());
 * Save input through QSettings (-> .mantidplot or -> windows registerary) for future use
 */
void SANSRunWindow::saveSettings()
{
  QSettings value_store;
  value_store.beginGroup("CustomInterfaces/SANSRunWindow");
  if( !m_uiForm.userfile_edit->text().isEmpty() ) 
  {
    value_store.setValue("user_file", m_uiForm.userfile_edit->text());
  }
  value_store.setValue("instrum", m_uiForm.inst_opt->currentText());
  value_store.setValue("fileextension", m_uiForm.file_opt->currentIndex());
  value_store.setValue("allow_periods", m_uiForm.allowPeriods_ck->isChecked());
  value_store.setValue("wave_binning", m_uiForm.wav_dw_opt->currentText());

  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);
  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("NIST_Qxy", m_uiForm.saveNIST_Qxy_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
 * @returns A trimmed string containing the output of the code execution
 */
QString SANSRunWindow::runReduceScriptFunction(const QString & pycode)
{
  if( !m_have_reducemodule )
  {
  g_log.debug() << "Executing Python: " << pycode.toStdString() << std::endl;

  const static QString PYTHON_SEP("C++runReduceScriptFunctionC++");
  QString code_torun =  pycode + ";print '"+PYTHON_SEP+"p'";
  QString pythonOut = runPythonCode(code_torun).trimmed();
  
  QStringList allOutput = pythonOut.split(PYTHON_SEP);

  if ( allOutput.count() < 2 )
  {
    QMessageBox::critical(this, "Fatal error found during reduction", "Error reported by Python script, more information maybe found in the scripting console and results log");
  return allOutput[0].trimmed();
}

/**
 * Trim off Python markers surrounding things like strings or lists that have been 
 * printed by Python by removing the first and last character
 */
void SANSRunWindow::trimPyMarkers(QString & txt)
{
 txt.remove(0,1);
 txt.chop(1);
}
/** Issues a Python command to load the user file and returns any output if
*  there are warnings or errors
*  @return the output printed by the Python commands
*/
bool SANSRunWindow::loadUserFile()
  const std::string facility = ConfigService::Instance().getFacility().name();
  if (facility != "ISIS"){
    return false;
  }

  QString filetext = m_uiForm.userfile_edit->text().trimmed();
    QMessageBox::warning(this, "Error loading user file", "No user file has been specified");
  if( !user_file.open(QIODevice::ReadOnly) )
  {
    QMessageBox::critical(this, "Error loading user file", "Could not open user file \""+filetext+"\"");
  //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);
  }
  QString pyCode = "i.Clean()";
  pyCode += "\ni." + getInstrumentClass();
  pyCode += "\ni.ReductionSingleton().user_settings =";
  // Use python function to read the settings file and then extract the fields
  pyCode += "isis_reduction_steps.UserFile(r'"+filetext+"')";
  QString errors = runReduceScriptFunction(
    "print i.ReductionSingleton().user_settings.execute(i.ReductionSingleton())").trimmed();
  // create a string list with a string for each line
  const QStringList allOutput = errors.split("\n");
  errors.clear();
  bool canContinue = false;
  for (int i = 0; i < allOutput.count(); ++i)
  {
    if ( i < allOutput.count()-1 )
    {
      errors += allOutput[i]+"\n";
    }
    else
    {
      canContinue = allOutput[i].trimmed() == "True";
    }
  }

  if ( ! canContinue )
  const auto settings = getReductionSettings();

  // Radius
  double dbl_param = runReduceScriptFunction(
      "print i.ReductionSingleton().mask.min_radius").toDouble();
  m_uiForm.rad_min->setText(QString::number(dbl_param*unit_conv));
  dbl_param = runReduceScriptFunction(
      "print i.ReductionSingleton().mask.max_radius").toDouble();
  m_uiForm.rad_max->setText(QString::number(dbl_param*unit_conv));
  //EventsTime
  m_uiForm.l_events_binning->setText(getSettingWithDefault("events.binning", "").trimmed());
  //Wavelength
  m_uiForm.wav_min->setText(runReduceScriptFunction(
      "print i.ReductionSingleton().to_wavelen.wav_low"));
  m_uiForm.wav_max->setText(runReduceScriptFunction(
      "print i.ReductionSingleton().to_wavelen.wav_high").trimmed());
  const QString wav_step = runReduceScriptFunction(
      "print i.ReductionSingleton().to_wavelen.wav_step").trimmed();
  setLimitStepParameter("wavelength", wav_step, m_uiForm.wav_dw,
                        m_uiForm.wav_dw_opt);
  //Q
  QString text = runReduceScriptFunction(
  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(
  setLimitStepParameter("Qxy", runReduceScriptFunction(
      "print i.ReductionSingleton().DQXY"), m_uiForm.qy_dqy,
  // The tramission line of the Limits section (read settings for sample and can) 
  loadTransmissionSettings(); 
  // The front rescale/shift section
  m_uiForm.frontDetRescale->setText(runReduceScriptFunction(
     "print i.ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.scale").trimmed());
  m_uiForm.frontDetShift->setText(runReduceScriptFunction(
     "print i.ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.shift").trimmed());

  QString fitScale = runReduceScriptFunction("print i.ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.fitScale").trimmed();
  QString fitShift = runReduceScriptFunction("print i.ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.fitShift").trimmed();

  if ( fitScale == "True" )
    m_uiForm.frontDetRescaleCB->setChecked(true);
  else
    m_uiForm.frontDetRescaleCB->setChecked(false);

  if ( fitShift == "True" )
    m_uiForm.frontDetShiftCB->setChecked(true);
  else
    m_uiForm.frontDetShiftCB->setChecked(false);

  QString qRangeUserSelected = runReduceScriptFunction("print i.ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.qRangeUserSelected").trimmed();
  if ( qRangeUserSelected == "True" )
  {
     m_uiForm.frontDetQrangeOnOff->setChecked(true); 
     m_uiForm.frontDetQmin->setText(runReduceScriptFunction(
      "print i.ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.qMin").trimmed());
     m_uiForm.frontDetQmax->setText(runReduceScriptFunction(
      "print i.ReductionSingleton().instrument.getDetector('FRONT').rescaleAndShift.qMax").trimmed());   
  }
  else
     m_uiForm.frontDetQrangeOnOff->setChecked(false);


  //Monitor spectra
  m_uiForm.monitor_spec->setText(runReduceScriptFunction(
    "print i.ReductionSingleton().instrument.get_incident_mon()").trimmed());
  m_uiForm.trans_monitor->setText(runReduceScriptFunction(
    "print i.ReductionSingleton().instrument.incid_mon_4_trans_calc").trimmed());
  m_uiForm.monitor_interp->setChecked(runReduceScriptFunction(
    "print i.ReductionSingleton().instrument.is_interpolating_norm()").trimmed() == "True");
  m_uiForm.trans_interp->setChecked(runReduceScriptFunction(
    "print i.ReductionSingleton().transmission_calculator.interpolate"
  // Transmission settings
  setTransmissionSettingsFromUserFile();

  //Direct efficiency correction
  m_uiForm.direct_file->setText(runReduceScriptFunction(
    "print i.ReductionSingleton().instrument.detector_file('rear')"));
  m_uiForm.front_direct_file->setText(runReduceScriptFunction(
    "print i.ReductionSingleton().instrument.detector_file('front')"));
    "print i.ReductionSingleton().prep_normalize.getPixelCorrFile('REAR')");
  //Check if the file name is set to Python's None object and then adjust the controls if there is an empty entry

  m_uiForm.floodRearFile->setFileTextWithSearch(file == "None" ? "" : file);  
  m_uiForm.enableRearFlood_ck->setChecked( ! m_uiForm.floodRearFile->isEmpty() );
  m_uiForm.floodRearFile->setEnabled(m_uiForm.enableRearFlood_ck->checkState()
                                     == Qt::Checked);
  file = runReduceScriptFunction(
    "print i.ReductionSingleton().prep_normalize.getPixelCorrFile('FRONT')");
  file = file.trimmed();
  m_uiForm.floodFrontFile->setFileTextWithSearch(file == "None" ? "" : file);  
  m_uiForm.enableFrontFlood_ck->setChecked( ! m_uiForm.floodFrontFile->isEmpty() );
  m_uiForm.floodFrontFile->setEnabled(m_uiForm.enableFrontFlood_ck->checkState()
                                     == Qt::Checked);

  //Scale factor
  dbl_param = runReduceScriptFunction(
    "print i.ReductionSingleton()._corr_and_scale.rescale").toDouble();
  m_uiForm.scale_factor->setText(QString::number(dbl_param/100.));

  //Sample offset if one has been specified
  dbl_param = runReduceScriptFunction(
    "print i.ReductionSingleton().instrument.SAMPLE_Z_CORR").toDouble();
  m_uiForm.smpl_offset->setText(QString::number(dbl_param*unit_conv));

  //Centre coordinates
  // from the ticket #5942 both detectors have center coordinates
    "print i.ReductionSingleton().get_beam_center('rear')[0]").toDouble();
  // get the scale factor1 for the beam centre to scale it correctly
  double dbl_paramsf = runReduceScriptFunction(
    "print i.ReductionSingleton().get_beam_center_scale_factor1()").toDouble();
  m_uiForm.rear_beam_x->setText(QString::number(dbl_param*dbl_paramsf));
  // get scale factor2 for the beam centre to scale it correctly
  dbl_paramsf = runReduceScriptFunction(
    "print i.ReductionSingleton().get_beam_center_scale_factor2()").toDouble();
    "print i.ReductionSingleton().get_beam_center('rear')[1]").toDouble();
  m_uiForm.rear_beam_y->setText(QString::number(dbl_param*dbl_paramsf));
  // front
  dbl_param = runReduceScriptFunction(
    "print i.ReductionSingleton().get_beam_center('front')[0]").toDouble();
  m_uiForm.front_beam_x->setText(QString::number(dbl_param*1000.0));
  dbl_param = runReduceScriptFunction(
    "print i.ReductionSingleton().get_beam_center('front')[1]").toDouble();
  m_uiForm.front_beam_y->setText(QString::number(dbl_param*1000.0));



  //Gravity switch
  QString param = runReduceScriptFunction(
    "print i.ReductionSingleton().to_Q.get_gravity()").trimmed();
  if( param == "True" )
  {
    m_uiForm.gravity_check->setChecked(true);
  }
  else
  {
    m_uiForm.gravity_check->setChecked(false);
  }

  // Read the extra length for the gravity correction
  const double extraLengthParam =  runReduceScriptFunction(
    "print i.ReductionSingleton().to_Q.get_extra_length()").toDouble();
  m_uiForm.gravity_extra_length_line_edit->setText(QString::number(extraLengthParam));

  ////Detector bank: support REAR, FRONT, HAB, BOTH, MERGED, MERGE options
  QString detName = runReduceScriptFunction(
    "print i.ReductionSingleton().instrument.det_selection").trimmed();
  if (detName == "REAR" || detName == "MAIN"){
     m_uiForm.detbank_sel->setCurrentIndex(0);
  }else if (detName == "FRONT" || detName == "HAB"){
     m_uiForm.detbank_sel->setCurrentIndex(1);
  }else if (detName == "BOTH"){
     m_uiForm.detbank_sel->setCurrentIndex(2);
  }else if (detName == "MERGED" || detName== "MERGE"){
     m_uiForm.detbank_sel->setCurrentIndex(3);