#include "MantidQtCustomInterfaces/Reflectometry/QDataProcessorAlgorithmWidget.h" #include "MantidQtAPI/FileDialogHandler.h" #include "MantidQtAPI/HelpWindow.h" #include "MantidQtAPI/MantidWidget.h" #include "MantidQtCustomInterfaces/Reflectometry/DataPreprocessorAlgorithm.h" #include "MantidQtCustomInterfaces/Reflectometry/DataProcessorWhiteList.h" #include "MantidQtCustomInterfaces/Reflectometry/QDataProcessorTableModel.h" #include "MantidQtMantidWidgets/HintingLineEditFactory.h" #include <QWidget> namespace { const QString ReflSettingsGroup = "Mantid/CustomInterfaces/ISISReflectometry"; } namespace MantidQt { namespace CustomInterfaces { using namespace Mantid::API; //---------------------------------------------------------------------------------------------- /** Constructor */ QDataProcessorAlgorithmWidget::QDataProcessorAlgorithmWidget(QWidget *parent) : MantidWidget(parent), m_openMap(new QSignalMapper(this)) { createTable(); // Create the presenters to do the thinking for us // The data processor algorithm's name std::string dataProcessorAlgorithm = "ReflectometryReductionOneAuto"; // Properties blacklisted from Options column std::set<std::string> blacklist{"ThetaIn", "ThetaOut", "InputWorkspace", "OutputWorkspace", "OutputWorkspaceWavelength", "FirstTransmissionRun", "SecondTransmissionRun"}; // Output instructions std::map<std::string, std::string> outputInstructions = { {"OutputWorkspace", "IvsQ"}, {"OutputWorkspaceWavelength", "IvsLam"}}; // Pre-processing instructions std::map<std::string, DataPreprocessorAlgorithm> preprocessor = { {"Run(s)", DataPreprocessorAlgorithm("Plus", "LHSWorkspace", "RHSWorkspace", "OutputWorkspace", false)}, {"Transmission Run(s)", DataPreprocessorAlgorithm("CreateTransmissionWorkspaceAuto", "FirstTransmissionRun", "SecondTransmissionRun", "OutputWorkspace", true)}}; // The whitelist DataProcessorWhiteList whitelist; whitelist.addElement("Run(s)", "InputWorkspace"); whitelist.addElement("Angle", "ThetaIn"); whitelist.addElement("Transmission Run(s)", "FirstTransmissionRun"); whitelist.addElement("Q min", "Qmin"); whitelist.addElement("Q max", "Qmax"); whitelist.addElement("dQ/Q", "Qstep"); whitelist.addElement("Scale", "Scale"); m_presenter = boost::make_shared<GenericDataProcessorPresenter>( this /*table view*/, this /*currently this concrete view is also responsibile for prog reporting*/, preprocessor /*Pre-processing instructions*/, dataProcessorAlgorithm /*the algorithm responsible for the processing*/, blacklist /*Properties we don't want to show in the Options column*/, whitelist /*Properties we want to show as columns in the table*/, outputInstructions /*Names of the outputs produced by the reduction algorithm*/); } //---------------------------------------------------------------------------------------------- /** Destructor */ QDataProcessorAlgorithmWidget::~QDataProcessorAlgorithmWidget() {} /** Initialise the Interface */ void QDataProcessorAlgorithmWidget::createTable() { ui.setupUi(this); ui.buttonProcess->setDefaultAction(ui.actionProcess); // Create a whats this button ui.rowToolBar->addAction(QWhatsThis::createAction(this)); // Allow rows and columns to be reordered ui.viewTable->verticalHeader()->setMovable(true); ui.viewTable->horizontalHeader()->setMovable(true); // Re-emit a signal when the instrument changes connect(ui.comboProcessInstrument, SIGNAL(currentIndexChanged(int)), this, SIGNAL(comboProcessInstrument_currentIndexChanged(int))); // Custom context menu for table connect(ui.viewTable, SIGNAL(customContextMenuRequested(const QPoint &)), this, SLOT(showContextMenu(const QPoint &))); } /** This slot loads a table workspace model and changes to a LoadedMainView presenter @param name : the string name of the workspace to be grabbed */ void QDataProcessorAlgorithmWidget::setModel(QString name) { m_toOpen = name.toStdString(); m_presenter->notify(DataProcessorPresenter::OpenTableFlag); } /** This method loads a table workspace model. Unlike QDataProcessorAlgorithmWidget::setModel(QString name), this method is public and takes a std::string as argument. The reason is that this method is intended to be called by the presenter @param name : the string name of the workspace to be grabbed */ void QDataProcessorAlgorithmWidget::setModel(std::string name) { m_toOpen = name; m_presenter->notify(DataProcessorPresenter::OpenTableFlag); } /** Set a new model in the tableview @param model : the model to be attached to the tableview */ void QDataProcessorAlgorithmWidget::showTable( QDataProcessorTableModel_sptr model) { m_model = model; // So we can notify the presenter when the user updates the table connect(m_model.get(), SIGNAL(dataChanged(const QModelIndex &, const QModelIndex &)), this, SLOT(tableUpdated(const QModelIndex &, const QModelIndex &))); ui.viewTable->setModel(m_model.get()); ui.viewTable->resizeColumnsToContents(); std::string windowTitle = "ISIS Reflectometry (Polref) - " + m_toOpen; auto mainWindowWidget = this->topLevelWidget(); mainWindowWidget->setWindowTitle(QString::fromStdString(windowTitle + "[*]")); this->setWindowModified(false); } /** Set the list of tables the user is offered to open @param tables : the names of the tables in the ADS */ void QDataProcessorAlgorithmWidget::setTableList( const std::set<std::string> &tables) { ui.menuOpenTable->clear(); for (auto it = tables.begin(); it != tables.end(); ++it) { QAction *openTable = ui.menuOpenTable->addAction(QString::fromStdString(*it)); openTable->setIcon(QIcon("://worksheet.png")); // Map this action to the table name m_openMap->setMapping(openTable, QString::fromStdString(*it)); // When repeated corrections happen the QMessageBox from openTable() // method in ReflMainViewPresenter will be called multiple times // when 'no' is clicked. // ConnectionType = UniqueConnection ensures that // each object has only one of these signals. connect(openTable, SIGNAL(triggered()), m_openMap, SLOT(map()), Qt::UniqueConnection); connect(m_openMap, SIGNAL(mapped(QString)), this, SLOT(setModel(QString)), Qt::UniqueConnection); } } /** This slot notifies the presenter that the "save" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionSaveTable_triggered() { m_presenter->notify(DataProcessorPresenter::SaveFlag); } /** This slot notifies the presenter that the "save as" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionSaveTableAs_triggered() { m_presenter->notify(DataProcessorPresenter::SaveAsFlag); } /** This slot notifies the presenter that the "append row" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionAppendRow_triggered() { m_presenter->notify(DataProcessorPresenter::AppendRowFlag); } /** This slot notifies the presenter that the "prepend row" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionPrependRow_triggered() { m_presenter->notify(DataProcessorPresenter::PrependRowFlag); } /** This slot notifies the presenter that the "delete" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionDeleteRow_triggered() { m_presenter->notify(DataProcessorPresenter::DeleteRowFlag); } /** This slot notifies the presenter that the "process" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionProcess_triggered() { m_presenter->notify(DataProcessorPresenter::ProcessFlag); } /** This slot notifies the presenter that the "group rows" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionGroupRows_triggered() { m_presenter->notify(DataProcessorPresenter::GroupRowsFlag); } /** This slot notifies the presenter that the "clear selected" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionClearSelected_triggered() { m_presenter->notify(DataProcessorPresenter::ClearSelectedFlag); } /** This slot notifies the presenter that the "copy selection" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionCopySelected_triggered() { m_presenter->notify(DataProcessorPresenter::CopySelectedFlag); } /** This slot notifies the presenter that the "cut selection" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionCutSelected_triggered() { m_presenter->notify(DataProcessorPresenter::CutSelectedFlag); } /** This slot notifies the presenter that the "paste selection" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionPasteSelected_triggered() { m_presenter->notify(DataProcessorPresenter::PasteSelectedFlag); } /** This slot notifies the presenter that the "new table" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionNewTable_triggered() { m_presenter->notify(DataProcessorPresenter::NewTableFlag); } /** This slot notifies the presenter that the "expand selection" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionExpandSelection_triggered() { m_presenter->notify(DataProcessorPresenter::ExpandSelectionFlag); } /** This slot notifies the presenter that the "options..." button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionOptionsDialog_triggered() { m_presenter->notify(DataProcessorPresenter::OptionsDialogFlag); } /** This slot notifies the presenter that the "export table" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionExportTable_triggered() { m_presenter->notify(DataProcessorPresenter::ExportTableFlag); } /** This slot notifies the presenter that the "import table" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionImportTable_triggered() { m_presenter->notify(DataProcessorPresenter::ImportTableFlag); } /** This slot opens the documentation when the "help" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionHelp_triggered() { MantidQt::API::HelpWindow::showPage( this, QString( "qthelp://org.mantidproject/doc/interfaces/ISIS_Reflectometry.html")); } /** This slot notifies the presenter that the "plot selected rows" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionPlotRow_triggered() { m_presenter->notify(DataProcessorPresenter::PlotRowFlag); } /** This slot notifies the presenter that the "plot selected groups" button has been pressed */ void QDataProcessorAlgorithmWidget::on_actionPlotGroup_triggered() { m_presenter->notify(DataProcessorPresenter::PlotGroupFlag); } /** This slot is used to update the instrument*/ void QDataProcessorAlgorithmWidget:: on_comboProcessInstrument_currentIndexChanged(int index) { ui.comboProcessInstrument->setCurrentIndex(index); } /** This slot notifies the presenter that the table has been updated/changed by the user */ void QDataProcessorAlgorithmWidget::tableUpdated( const QModelIndex &topLeft, const QModelIndex &bottomRight) { Q_UNUSED(topLeft); Q_UNUSED(bottomRight); m_presenter->notify(DataProcessorPresenter::TableUpdatedFlag); } /** This slot is triggered when the user right clicks on the table @param pos : The position of the right click within the table */ void QDataProcessorAlgorithmWidget::showContextMenu(const QPoint &pos) { // If the user didn't right-click on anything, don't show a context menu. if (!ui.viewTable->indexAt(pos).isValid()) return; // parent widget takes ownership of QMenu QMenu *menu = new QMenu(this); menu->addAction(ui.actionProcess); menu->addAction(ui.actionExpandSelection); menu->addSeparator(); menu->addAction(ui.actionPlotRow); menu->addAction(ui.actionPlotGroup); menu->addSeparator(); menu->addAction(ui.actionPrependRow); menu->addAction(ui.actionAppendRow); menu->addSeparator(); menu->addAction(ui.actionGroupRows); menu->addAction(ui.actionCopySelected); menu->addAction(ui.actionCutSelected); menu->addAction(ui.actionPasteSelected); menu->addAction(ui.actionClearSelected); menu->addSeparator(); menu->addAction(ui.actionDeleteRow); menu->popup(ui.viewTable->viewport()->mapToGlobal(pos)); } /** Show an critical error dialog @param prompt : The prompt to appear on the dialog @param title : The text for the title bar of the dialog */ void QDataProcessorAlgorithmWidget::giveUserCritical(std::string prompt, std::string title) { QMessageBox::critical(this, QString(title.c_str()), QString(prompt.c_str()), QMessageBox::Ok, QMessageBox::Ok); } /** Show a warning dialog @param prompt : The prompt to appear on the dialog @param title : The text for the title bar of the dialog */ void QDataProcessorAlgorithmWidget::giveUserWarning(std::string prompt, std::string title) { QMessageBox::warning(this, QString(title.c_str()), QString(prompt.c_str()), QMessageBox::Ok, QMessageBox::Ok); } /** Ask the user a Yes/No question @param prompt : The prompt to appear on the dialog @param title : The text for the title bar of the dialog @returns a boolean true if Yes, false if No */ bool QDataProcessorAlgorithmWidget::askUserYesNo(std::string prompt, std::string title) { auto response = QMessageBox::question( this, QString(title.c_str()), QString(prompt.c_str()), QMessageBox::Yes | QMessageBox::No, QMessageBox::Yes); if (response == QMessageBox::Yes) { return true; } return false; } /** Ask the user to enter a string. @param prompt : The prompt to appear on the dialog @param title : The text for the title bar of the dialog @param defaultValue : The default value entered. @returns The user's string if submitted, or an empty string */ std::string QDataProcessorAlgorithmWidget::askUserString(const std::string &prompt, const std::string &title, const std::string &defaultValue) { bool ok; QString text = QInputDialog::getText( QString::fromStdString(title), QString::fromStdString(prompt), QLineEdit::Normal, QString::fromStdString(defaultValue), &ok); if (ok) return text.toStdString(); return ""; } /** Show the user the dialog for an algorithm * @param algorithm : [input] The algorithm */ void QDataProcessorAlgorithmWidget::showAlgorithmDialog( const std::string &algorithm) { std::stringstream pythonSrc; pythonSrc << "try:\n"; pythonSrc << " algm = " << algorithm << "Dialog()\n"; pythonSrc << "except:\n"; pythonSrc << " pass\n"; runPythonCode(QString::fromStdString(pythonSrc.str()), false); } /** Show the user the dialog for "LoadReflTBL" */ void QDataProcessorAlgorithmWidget::showImportDialog() { std::stringstream pythonSrc; pythonSrc << "try:\n"; pythonSrc << " algm = " << "LoadReflTBL" << "Dialog()\n"; pythonSrc << " print algm.getPropertyValue(\"OutputWorkspace\")\n"; pythonSrc << "except:\n"; pythonSrc << " pass\n"; // outputWorkspaceName will hold the name of the workspace // otherwise this should be an empty string. QString outputWorkspaceName = runPythonCode(QString::fromStdString(pythonSrc.str()), false); m_toOpen = outputWorkspaceName.trimmed().toStdString(); // notifying the presenter that a new table should be opened // The presenter will ask about any unsaved changes etc // before opening the new table m_presenter->notify(DataProcessorPresenter::OpenTableFlag); } /** Show the user file dialog to choose save location of notebook */ std::string QDataProcessorAlgorithmWidget::requestNotebookPath() { // We won't use QFileDialog directly here as using the NativeDialog option // causes problems on MacOS. QString qfilename = API::FileDialogHandler::getSaveFileName( this, "Save notebook file", QDir::currentPath(), "IPython Notebook files (*.ipynb);;All files (*.*)", new QString("IPython Notebook files (*.ipynb)")); // There is a Qt bug (QTBUG-27186) which means the filename returned // from the dialog doesn't always the file extension appended. // So we'll have to ensure this ourselves. // Important, notebooks can't be loaded without this extension. std::string filename = qfilename.toStdString(); if (filename.size() > 6) { if (filename.substr(filename.size() - 6) != ".ipynb") { filename.append(".ipynb"); } } else { filename.append(".ipynb"); } return filename; } /** Save settings @param options : map of user options to save */ void QDataProcessorAlgorithmWidget::saveSettings( const std::map<std::string, QVariant> &options) { QSettings settings; settings.beginGroup(ReflSettingsGroup); for (auto it = options.begin(); it != options.end(); ++it) settings.setValue(QString::fromStdString(it->first), it->second); settings.endGroup(); } /** Load settings @param options : map of user options to load into */ void QDataProcessorAlgorithmWidget::loadSettings( std::map<std::string, QVariant> &options) { QSettings settings; settings.beginGroup(ReflSettingsGroup); QStringList keys = settings.childKeys(); for (auto it = keys.begin(); it != keys.end(); ++it) options[it->toStdString()] = settings.value(*it); settings.endGroup(); } /** Plot a set of workspaces * @param workspaces : [input] The list of workspaces as a set */ void QDataProcessorAlgorithmWidget::plotWorkspaces( const std::set<std::string> &workspaces) { if (workspaces.empty()) return; std::stringstream pythonSrc; pythonSrc << "base_graph = None\n"; for (auto ws = workspaces.begin(); ws != workspaces.end(); ++ws) pythonSrc << "base_graph = plotSpectrum(\"" << *ws << "\", 0, True, window = base_graph)\n"; pythonSrc << "base_graph.activeLayer().logLogAxes()\n"; runPythonCode(QString::fromStdString(pythonSrc.str())); } /** Set the range of the progress bar @param min : The minimum value of the bar @param max : The maxmimum value of the bar */ void QDataProcessorAlgorithmWidget::setProgressRange(int min, int max) { ui.progressBar->setRange(min, max); } /** Set the status of the progress bar @param progress : The current value of the bar */ void QDataProcessorAlgorithmWidget::setProgress(int progress) { ui.progressBar->setValue(progress); } /** Get status of checkbox which determines whether an ipython notebook is produced @return true if a notebook should be output on process, false otherwise */ bool QDataProcessorAlgorithmWidget::getEnableNotebook() { return ui.checkEnableNotebook->isChecked(); } /** Set which rows are selected @param rows : The set of rows to select */ void QDataProcessorAlgorithmWidget::setSelection(const std::set<int> &rows) { ui.viewTable->clearSelection(); auto selectionModel = ui.viewTable->selectionModel(); for (auto row = rows.begin(); row != rows.end(); ++row) selectionModel->select(ui.viewTable->model()->index((*row), 0), QItemSelectionModel::Select | QItemSelectionModel::Rows); } /** Set the list of available instruments to process @param instruments : The list of instruments available @param defaultInstrument : The instrument to have selected by default */ void QDataProcessorAlgorithmWidget::setInstrumentList( const std::vector<std::string> &instruments, const std::string &defaultInstrument) { ui.comboProcessInstrument->clear(); for (auto it = instruments.begin(); it != instruments.end(); ++it) { QString instrument = QString::fromStdString(*it); ui.comboProcessInstrument->addItem(instrument); } int index = ui.comboProcessInstrument->findData( QString::fromStdString(defaultInstrument), Qt::DisplayRole); ui.comboProcessInstrument->setCurrentIndex(index); } /** Set the strategy used for generating hints for the autocompletion in the options column. @param hintStrategy The hinting strategy to use */ void QDataProcessorAlgorithmWidget::setOptionsHintStrategy( HintStrategy *hintStrategy) { ui.viewTable->setItemDelegateForColumn( ReflTableSchema::COL_OPTIONS, new HintingLineEditFactory(hintStrategy)); } /** Sets the contents of the system's clipboard @param text The contents of the clipboard */ void QDataProcessorAlgorithmWidget::setClipboard(const std::string &text) { QApplication::clipboard()->setText(QString::fromStdString(text)); } /** Get the selected instrument for processing @returns the selected instrument to process with */ std::string QDataProcessorAlgorithmWidget::getProcessInstrument() const { return ui.comboProcessInstrument->currentText().toStdString(); } /** Get the indices of the highlighted rows @returns a set of ints containing the highlighted row numbers */ std::set<int> QDataProcessorAlgorithmWidget::getSelectedRows() const { std::set<int> rows; auto selectionModel = ui.viewTable->selectionModel(); if (selectionModel) { auto selectedRows = selectionModel->selectedRows(); for (auto it = selectedRows.begin(); it != selectedRows.end(); ++it) rows.insert(it->row()); } return rows; } /** Get the name of the workspace that the user wishes to open as a table @returns The name of the workspace to open */ std::string QDataProcessorAlgorithmWidget::getWorkspaceToOpen() const { return m_toOpen; } /** Get a pointer to the presenter that's currently controlling this view. @returns A pointer to the presenter */ boost::shared_ptr<DataProcessorPresenter> QDataProcessorAlgorithmWidget::getTablePresenter() const { return m_presenter; } /** Gets the contents of the system's clipboard @returns The contents of the clipboard */ std::string QDataProcessorAlgorithmWidget::getClipboard() const { return QApplication::clipboard()->text().toStdString(); } /** * Clear the progress */ void QDataProcessorAlgorithmWidget::clearProgress() { ui.progressBar->reset(); } } // namespace CustomInterfaces } // namespace Mantid