Skip to content
Snippets Groups Projects
SliceViewer.cpp 82.7 KiB
Newer Older
#include <iomanip>
#include <iosfwd>
#include <iostream>
#include <limits>
#include <sstream>
#include <vector>
#include <boost/make_shared.hpp>
#include <boost/math/special_functions/fpclassify.hpp>

#include "MantidAPI/CoordTransform.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/PeakTransformHKL.h"
#include "MantidAPI/PeakTransformQSample.h"
#include "MantidAPI/PeakTransformQLab.h"
#include "MantidAPI/IPeaksWorkspace.h"
#include "MantidAPI/IMDHistoWorkspace.h"
#include "MantidAPI/IMDEventWorkspace.h"
#include "MantidAPI/AlgorithmManager.h"
#include "MantidGeometry/MDGeometry/IMDDimension.h"
#include "MantidGeometry/MDGeometry/MDBoxImplicitFunction.h"
#include "MantidGeometry/MDGeometry/MDHistoDimension.h"
#include "MantidGeometry/MDGeometry/MDTypes.h"
#include "MantidKernel/ReadLock.h"
#include "MantidQtAPI/FileDialogHandler.h"
#include "MantidQtAPI/PlotAxis.h"
#include "MantidQtAPI/MdSettings.h"
#include "MantidQtAPI/SignalRange.h"
#include "MantidQtSliceViewer/SliceViewer.h"
#include "MantidQtSliceViewer/CustomTools.h"
#include "MantidQtSliceViewer/DimensionSliceWidget.h"
#include "MantidQtSliceViewer/LineOverlay.h"
#include "MantidQtSliceViewer/SnapToGridDialog.h"
#include "MantidQtSliceViewer/XYLimitsDialog.h"
#include "MantidQtSliceViewer/ConcretePeaksPresenter.h"
#include "MantidQtSliceViewer/CompositePeaksPresenter.h"
#include "MantidQtSliceViewer/ProxyCompositePeaksPresenter.h"
#include "MantidQtSliceViewer/PeakOverlayMultiCrossFactory.h"
#include "MantidQtSliceViewer/PeakOverlayMultiSphereFactory.h"
#include "MantidQtSliceViewer/FirstExperimentInfoQuery.h"
#include "MantidQtSliceViewer/PeakBoundingBox.h"
#include "MantidQtSliceViewer/PeaksViewerOverlayDialog.h"
#include "MantidQtSliceViewer/PeakOverlayViewFactorySelector.h"
#include "MantidQtMantidWidgets/SelectWorkspacesDialog.h"
#include <Poco/DOM/Document.h>
#include <Poco/DOM/DOMParser.h>
#include <Poco/DOM/NodeIterator.h>
#include <Poco/DOM/NodeList.h>
#include <Poco/Exception.h>
using namespace Mantid::Kernel;
using namespace Mantid::Geometry;
using namespace Mantid::API;
using MantidQt::API::SyncedCheckboxes;
using Poco::XML::DOMParser;
using Poco::XML::Document;
using Poco::XML::Element;
using Poco::XML::Node;
using Poco::XML::NodeList;
using Poco::XML::NodeIterator;
using Poco::XML::NodeFilter;
using MantidQt::API::AlgorithmRunner;
Owen Arnold's avatar
Owen Arnold committed
namespace MantidQt {
namespace SliceViewer {
//------------------------------------------------------------------------------------
/** Constructor */
SliceViewer::SliceViewer(QWidget *parent)
Owen Arnold's avatar
Owen Arnold committed
    : QWidget(parent), m_ws(), m_firstWorkspaceOpen(false), m_dimensions(),
      m_data(NULL), m_X(), m_Y(), m_dimX(0), m_dimY(1), m_logColor(false),
      m_fastRender(true), m_rebinMode(false), m_rebinLocked(true),
      m_mdSettings(new MantidQt::API::MdSettings()),
      m_logger("SliceViewer"),
      m_peaksPresenter(boost::make_shared<CompositePeaksPresenter>(this)),
Owen Arnold's avatar
Owen Arnold committed
      m_proxyPeaksPresenter(
      boost::make_shared<ProxyCompositePeaksPresenter>(m_peaksPresenter)),
      m_peaksSliderWidget(NULL){
Owen Arnold's avatar
Owen Arnold committed
  ui.setupUi(this);
Owen Arnold's avatar
Owen Arnold committed
  m_inf = std::numeric_limits<double>::infinity();
Owen Arnold's avatar
Owen Arnold committed
  // Point m_plot to the plot created in QtDesigner
  m_plot = ui.safeQwtPlot;
  // Add a spectrograph
  m_spect = new QwtPlotSpectrogram();
  m_spect->attach(m_plot);

  // Set up the ColorBarWidget
  m_colorBar = ui.colorBarWidget;
Owen Arnold's avatar
Owen Arnold committed
  m_colorBar->setViewRange(0, 10);
Owen Arnold's avatar
Owen Arnold committed
  QObject::connect(m_colorBar, SIGNAL(changedColorRange(double, double, bool)),
                   this, SLOT(colorRangeChanged()));
  m_data = new API::QwtRasterDataMD();
Owen Arnold's avatar
Owen Arnold committed
  m_spect->setColorMap(m_colorBar->getColorMap());
  // Make the splitter use the minimum size for the controls and not stretch out
  ui.splitter->setStretchFactor(0, 0);
  ui.splitter->setStretchFactor(1, 1);
  initZoomer();

  // ----------- Toolbar button signals ----------------
  QObject::connect(ui.btnResetZoom, SIGNAL(clicked()), this, SLOT(resetZoom()));
  QObject::connect(ui.btnClearLine, SIGNAL(clicked()), this, SLOT(clearLine()));
Owen Arnold's avatar
Owen Arnold committed
  QObject::connect(ui.btnRangeFull, SIGNAL(clicked()), this,
                   SLOT(setColorScaleAutoFull()));
  QObject::connect(ui.btnRangeSlice, SIGNAL(clicked()), this,
                   SLOT(setColorScaleAutoSlice()));
  QObject::connect(ui.btnRebinRefresh, SIGNAL(clicked()), this,
                   SLOT(rebinParamsChanged()));
  QObject::connect(ui.btnAutoRebin, SIGNAL(toggled(bool)), this,
                   SLOT(autoRebin_toggled(bool)));
  QObject::connect(ui.btnPeakOverlay, SIGNAL(clicked()), this,
                   SLOT(peakOverlay_clicked()));
Owen Arnold's avatar
Owen Arnold committed
  QObject::connect(m_colorBar, SIGNAL(colorBarDoubleClicked()), this,
                   SLOT(loadColorMapSlot()));
  m_algoRunner = new AlgorithmRunner(this);
Owen Arnold's avatar
Owen Arnold committed
  QObject::connect(m_algoRunner, SIGNAL(algorithmComplete(bool)), this,
                   SLOT(dynamicRebinComplete(bool)));
  // -------- Line Overlay ----------------
  m_lineOverlay = new LineOverlay(m_plot, m_plot->canvas());
  m_lineOverlay->setShown(false);
  m_overlayWSOutline = new LineOverlay(m_plot, m_lineOverlay);
  m_overlayWSOutline->setShowHandles(false);
  m_overlayWSOutline->setShowLine(false);
  m_overlayWSOutline->setShown(false);

  // -------- Peak Overlay ----------------
Owen Arnold's avatar
Owen Arnold committed
  m_peakTransformSelector.registerCandidate(
      boost::make_shared<PeakTransformHKLFactory>());
  m_peakTransformSelector.registerCandidate(
      boost::make_shared<PeakTransformQSampleFactory>());
  m_peakTransformSelector.registerCandidate(
      boost::make_shared<PeakTransformQLabFactory>());
  this->setAcceptDrops(true);
//------------------------------------------------------------------------------------
/// Destructor
Owen Arnold's avatar
Owen Arnold committed
SliceViewer::~SliceViewer() {
  // Don't delete Qt objects, I think these are auto-deleted
//------------------------------------------------------------------------------------
/** Load QSettings from .ini-type files */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::loadSettings() {
  QSettings settings;
  settings.beginGroup("Mantid/SliceViewer");
Owen Arnold's avatar
Owen Arnold committed
  bool scaleType = (bool)settings.value("LogColorScale", 0).toInt();

  //Load Colormap. If the file is invalid the default stored colour map is used. If the 
  // user selected a unified color map for the SliceViewer and the VSI, then this is loaded.
  if (m_mdSettings != NULL && m_mdSettings->getUsageGeneralMdColorMap())
  {
    m_currentColorMapFile = m_mdSettings->getGeneralMdColorMapFile();
  }
  else
  {
    m_currentColorMapFile = settings.value("ColormapFile", "").toString();
  }

  // Set values from settings
  if (!m_currentColorMapFile.isEmpty())
    loadColorMap(m_currentColorMapFile);
  // Last saved image file
  m_lastSavedFile = settings.value("LastSavedImagePath", "").toString();

  bool transparentZeros = settings.value("TransparentZeros", 1).toInt();
  this->setTransparentZeros(transparentZeros);

  int norm = settings.value("Normalization", 1).toInt();
Owen Arnold's avatar
Owen Arnold committed
  Mantid::API::MDNormalization normaliz =
      static_cast<Mantid::API::MDNormalization>(norm);
  settings.endGroup();
}

//------------------------------------------------------------------------------------
/** Save settings for next time. */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::saveSettings() {
  QSettings settings;
  settings.beginGroup("Mantid/SliceViewer");
  settings.setValue("ColormapFile", m_currentColorMapFile);
Owen Arnold's avatar
Owen Arnold committed
  settings.setValue("LogColorScale", (int)m_colorBar->getLog());
  settings.setValue("LastSavedImagePath", m_lastSavedFile);
Owen Arnold's avatar
Owen Arnold committed
  settings.setValue("TransparentZeros",
                    (m_actionTransparentZeros->isChecked() ? 1 : 0));
  settings.setValue("Normalization",
                    static_cast<int>(this->getNormalization()));
//------------------------------------------------------------------------------------
/** Create the menus */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::initMenus() {
  // ---------------------- Build the menu bar -------------------------

  // Find the top-level parent
Owen Arnold's avatar
Owen Arnold committed
  QWidget *widget = this;
  while (widget && widget->parentWidget())
    widget = widget->parentWidget();
  QMainWindow *parentWindow = dynamic_cast<QMainWindow *>(widget);
Owen Arnold's avatar
Owen Arnold committed
  QMenuBar *bar;
  if (parentWindow)
    // Use the QMainWindow menu bar
    bar = parentWindow->menuBar();
Owen Arnold's avatar
Owen Arnold committed
  else {
    // Widget is not in a QMainWindow. Make a menu bar
    bar = new QMenuBar(this, "Main Menu Bar");
Owen Arnold's avatar
Owen Arnold committed
    ui.verticalLayout->insertWidget(0, bar);
Owen Arnold's avatar
Owen Arnold committed
  QAction *action;
  // --------------- File Menu ----------------------------------------
  m_menuFile = new QMenu("&File", this);
  action = new QAction(QPixmap(), "&Close", this);
  connect(action, SIGNAL(triggered()), this, SLOT(close()));
  m_actionFileClose = action;
  m_menuFile->addAction(action);

  action = new QAction(QPixmap(), "&Save to image file", this);
  action->setShortcut(Qt::Key_S + Qt::ControlModifier);
  connect(action, SIGNAL(triggered()), this, SLOT(saveImage()));
  m_menuFile->addAction(action);

  action = new QAction(QPixmap(), "Copy image to &Clipboard", this);
  action->setShortcut(Qt::Key_C + Qt::ControlModifier);
  connect(action, SIGNAL(triggered()), this, SLOT(copyImageToClipboard()));
  m_menuFile->addAction(action);

  // --------------- View Menu ----------------------------------------
  m_menuView = new QMenu("&View", this);
  action = new QAction(QPixmap(), "&Reset Zoom", this);
  connect(action, SIGNAL(triggered()), this, SLOT(resetZoom()));
Owen Arnold's avatar
Owen Arnold committed
  {
    QIcon icon;
    icon.addFile(QString::fromUtf8(":/SliceViewer/icons/view-fullscreen.png"),
                 QSize(), QIcon::Normal, QIcon::Off);
    action->setIcon(icon);
  }
  m_menuView->addAction(action);

  action = new QAction(QPixmap(), "&Set X/Y View Size", this);
  connect(action, SIGNAL(triggered()), this, SLOT(setXYLimitsDialog()));
  m_menuView->addAction(action);

  action = new QAction(QPixmap(), "Zoom &In", this);
  action->setShortcut(Qt::Key_Plus + Qt::ControlModifier);
  connect(action, SIGNAL(triggered()), this, SLOT(zoomInSlot()));
  m_menuView->addAction(action);

  action = new QAction(QPixmap(), "Zoom &Out", this);
  action->setShortcut(Qt::Key_Minus + Qt::ControlModifier);
  connect(action, SIGNAL(triggered()), this, SLOT(zoomOutSlot()));
  m_menuView->addAction(action);

  action = new QAction(QPixmap(), "&Fast Rendering Mode", this);
  action->setShortcut(Qt::Key_F + Qt::ControlModifier);
  action->setCheckable(true);
  action->setChecked(true);
  connect(action, SIGNAL(toggled(bool)), this, SLOT(setFastRender(bool)));
  m_menuView->addAction(action);

  action = new QAction(QPixmap(), "Dynamic R&ebin Mode", this);
  m_syncRebinMode = new SyncedCheckboxes(action, ui.btnRebinMode, false);
Owen Arnold's avatar
Owen Arnold committed
  connect(m_syncRebinMode, SIGNAL(toggled(bool)), this,
          SLOT(RebinMode_toggled(bool)));
  m_menuView->addAction(action);

  action = new QAction(QPixmap(), "&Lock Rebinned WS", this);
  m_syncRebinLock = new SyncedCheckboxes(action, ui.btnRebinLock, true);
Owen Arnold's avatar
Owen Arnold committed
  connect(m_syncRebinLock, SIGNAL(toggled(bool)), this,
          SLOT(RebinLock_toggled(bool)));
  m_menuView->addAction(action);

  action = new QAction(QPixmap(), "Refresh Rebin", this);
  action->setShortcut(Qt::Key_R + Qt::ControlModifier);
  action->setEnabled(false);
  connect(action, SIGNAL(triggered()), this, SLOT(rebinParamsChanged()));
  m_menuView->addAction(action);
  m_actionRefreshRebin = action;

  action = new QAction(QPixmap(), "Auto Rebin", this);
  m_syncAutoRebin = new SyncedCheckboxes(action, ui.btnAutoRebin, false);
  connect(action, SIGNAL(toggled(bool)), this, SLOT(autoRebin_toggled(bool)));
  m_syncAutoRebin->setEnabled(false); // Cannot auto rebin by default.
  m_menuView->addAction(action);

  m_menuView->addSeparator();

  action = new QAction(QPixmap(), "Peak Overlay", this);
  connect(action, SIGNAL(triggered()), this, SLOT(peakOverlay_clicked()));
  m_menuView->addAction(action);
  m_menuView->addSeparator();
Owen Arnold's avatar
Owen Arnold committed
  QActionGroup *group = new QActionGroup(this);

  action = new QAction(QPixmap(), "No Normalization", this);
  m_menuView->addAction(action);
  action->setActionGroup(group);
  action->setCheckable(true);
  connect(action, SIGNAL(triggered()), this, SLOT(changeNormalizationNone()));
  m_actionNormalizeNone = action;

  action = new QAction(QPixmap(), "Volume Normalization", this);
  m_menuView->addAction(action);
  action->setActionGroup(group);
  action->setCheckable(true);
  action->setChecked(true);
  connect(action, SIGNAL(triggered()), this, SLOT(changeNormalizationVolume()));
  m_actionNormalizeVolume = action;

  action = new QAction(QPixmap(), "Num. Events Normalization", this);
  m_menuView->addAction(action);
  action->setActionGroup(group);
  action->setCheckable(true);
Owen Arnold's avatar
Owen Arnold committed
  connect(action, SIGNAL(triggered()), this,
          SLOT(changeNormalizationNumEvents()));
  m_actionNormalizeNumEvents = action;

  // --------------- Color options Menu ----------------------------------------
  m_menuColorOptions = new QMenu("&ColorMap", this);

  action = new QAction(QPixmap(), "&Load Colormap", this);
  connect(action, SIGNAL(triggered()), this, SLOT(loadColorMapSlot()));
  action = new QAction(QPixmap(), "&Full range", this);
  connect(action, SIGNAL(triggered()), this, SLOT(setColorScaleAutoFull()));
Owen Arnold's avatar
Owen Arnold committed
  {
    QIcon icon;
    icon.addFile(QString::fromUtf8(":/SliceViewer/icons/color-pallette.png"),
                 QSize(), QIcon::Normal, QIcon::Off);
    action->setIcon(icon);
  }
  m_menuColorOptions->addAction(action);

  action = new QAction(QPixmap(), "&Slice range", this);
  connect(action, SIGNAL(triggered()), this, SLOT(setColorScaleAutoSlice()));
  action->setIconVisibleInMenu(true);
Owen Arnold's avatar
Owen Arnold committed
  {
    QIcon icon;
    icon.addFile(
        QString::fromUtf8(":/SliceViewer/icons/color-pallette-part.png"),
        QSize(), QIcon::Normal, QIcon::Off);
    action->setIcon(icon);
  }
  m_menuColorOptions->addAction(action);

  action = new QAction(QPixmap(), "Transparent &Zeros", this);
  action->setCheckable(true);
  action->setChecked(true);
  m_actionTransparentZeros = action;
  connect(action, SIGNAL(toggled(bool)), this, SLOT(setTransparentZeros(bool)));
  m_menuColorOptions->addAction(action);

  // --------------- Help Menu ----------------------------------------
  m_menuHelp = new QMenu("&Help", this);
  action = new QAction(QPixmap(), "&Slice Viewer Help (browser)", this);
  action->setShortcut(Qt::Key_F1);
  connect(action, SIGNAL(triggered()), this, SLOT(helpSliceViewer()));
  m_menuHelp->addAction(action);
  action = new QAction(QPixmap(), "&Line Viewer Help (browser)", this);
  connect(action, SIGNAL(triggered()), this, SLOT(helpLineViewer()));
  m_menuHelp->addAction(action);
  action = new QAction(QPixmap(), "&Peaks Viewer Help (browser)", this);
  connect(action, SIGNAL(triggered()), this, SLOT(helpPeaksViewer()));
  m_menuHelp->addAction(action);

  // --------------- Line Menu ----------------------------------------
  m_menuLine = new QMenu("&Line", this);

  // Line mode menu, synced to the button
  action = new QAction(QPixmap(), "&Line Mode", this);
  action->setShortcut(Qt::Key_L + Qt::ControlModifier);
  m_syncLineMode = new SyncedCheckboxes(action, ui.btnDoLine, false);
Owen Arnold's avatar
Owen Arnold committed
  connect(m_syncLineMode, SIGNAL(toggled(bool)), this,
          SLOT(LineMode_toggled(bool)));
  m_menuLine->addAction(action);

  // Snap-to-grid, synced to the button
  action = new QAction(QPixmap(), "&Snap to Grid", this);
  m_syncSnapToGrid = new SyncedCheckboxes(action, ui.btnSnapToGrid, false);
Owen Arnold's avatar
Owen Arnold committed
  connect(m_syncSnapToGrid, SIGNAL(toggled(bool)), this,
          SLOT(SnapToGrid_toggled(bool)));
  m_menuLine->addAction(action);

  // --------------- Peaks Menu ----------------------------------------
  m_menuPeaks = new QMenu("&Peak", this);
  action = new QAction(QPixmap(), "&Overlay Options", this);
Owen Arnold's avatar
Owen Arnold committed
  connect(action, SIGNAL(triggered()), this,
          SLOT(onPeaksViewerOverlayOptions()));
  m_menuPeaks->addAction(action);
  action = new QAction(QPixmap(), "&Visible Columns", this);
Owen Arnold's avatar
Owen Arnold committed
  connect(action, SIGNAL(triggered()), this,
          SIGNAL(peaksTableColumnOptions())); // just re-emit
  m_menuPeaks->addAction(action);
Owen Arnold's avatar
Owen Arnold committed
  m_menuPeaks->setEnabled(false); // Until a PeaksWorkspace is selected.

  // Add all the needed menus
Owen Arnold's avatar
Owen Arnold committed
  bar->addMenu(m_menuFile);
  bar->addMenu(m_menuView);
  bar->addMenu(m_menuColorOptions);
  bar->addMenu(m_menuLine);
  bar->addMenu(m_menuPeaks);
  bar->addMenu(m_menuHelp);
//------------------------------------------------------------------------------------
/** Intialize the zooming/panning tools */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::initZoomer() {
  //  QwtPlotZoomer * zoomer = new CustomZoomer(m_plot->canvas());
  //  zoomer->setMousePattern(QwtEventPattern::MouseSelect1,  Qt::LeftButton);
  //  zoomer->setTrackerMode(QwtPicker::AlwaysOff);
  //  const QColor c(Qt::darkBlue);
  //  zoomer->setRubberBandPen(c);
  //  zoomer->setTrackerPen(c);
  //  QObject::connect(zoomer, SIGNAL(zoomed(const QRectF &)),
  //      this, SLOT(zoomRectSlot(const QRectF &)));

  QwtPlotPicker *zoomer = new QwtPlotPicker(m_plot->canvas());
  zoomer->setSelectionFlags(QwtPicker::RectSelection |
                            QwtPicker::DragSelection);
  zoomer->setMousePattern(QwtEventPattern::MouseSelect1, Qt::LeftButton);
  zoomer->setTrackerMode(QwtPicker::AlwaysOff);
  const QColor c(Qt::darkBlue);
  zoomer->setRubberBand(QwtPicker::RectRubberBand);
  zoomer->setRubberBandPen(c);
Owen Arnold's avatar
Owen Arnold committed
  QObject::connect(zoomer, SIGNAL(selected(const QwtDoubleRect &)), this,
                   SLOT(zoomRectSlot(const QwtDoubleRect &)));
  // Zoom in/out using middle-click+drag or the mouse wheel
Owen Arnold's avatar
Owen Arnold committed
  CustomMagnifier *magnif = new CustomMagnifier(m_plot->canvas());
  magnif->setAxisEnabled(QwtPlot::yRight, false); // Don't do the colorbar axis
  magnif->setWheelFactor(0.9);
  magnif->setMouseButton(Qt::MidButton);
  // Have to flip the keys to match our flipped mouse wheel
  magnif->setZoomInKey(Qt::Key_Minus, Qt::NoModifier);
  magnif->setZoomOutKey(Qt::Key_Equal, Qt::NoModifier);
  // Hook-up listener to rescaled event
Owen Arnold's avatar
Owen Arnold committed
  QObject::connect(magnif, SIGNAL(rescaled(double)), this,
                   SLOT(magnifierRescaled(double)));
  // Pan using the right mouse button + drag
  QwtPlotPanner *panner = new QwtPlotPanner(m_plot->canvas());
  panner->setMouseButton(Qt::RightButton);
  panner->setAxisEnabled(QwtPlot::yRight, false); // Don't do the colorbar axis
Owen Arnold's avatar
Owen Arnold committed
  QObject::connect(panner, SIGNAL(panned(int, int)), this,
                   SLOT(panned(int, int))); // Handle panning.
  // Custom picker for showing the current coordinates
Owen Arnold's avatar
Owen Arnold committed
  CustomPicker *picker =
      new CustomPicker(m_spect->xAxis(), m_spect->yAxis(), m_plot->canvas());
  QObject::connect(picker, SIGNAL(mouseMoved(double, double)), this,
                   SLOT(showInfoAt(double, double)));
//------------------------------------------------------------------------------------
/** Programmatically show/hide the controls (sliders etc)
 *
 * @param visible :: true if you want to show the controls.
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::showControls(bool visible) {
//------------------------------------------------------------------------------------
/** Add (as needed) and update DimensionSliceWidget's. */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::updateDimensionSliceWidgets() {
  // Create all necessary widgets
Owen Arnold's avatar
Owen Arnold committed
  if (m_dimWidgets.size() < m_ws->getNumDims()) {
    for (size_t d = m_dimWidgets.size(); d < m_ws->getNumDims(); d++) {
      DimensionSliceWidget *widget = new DimensionSliceWidget(this);
      ui.verticalLayoutControls->insertWidget(int(d), widget);
      // Slots for changes on the dimension widget
Owen Arnold's avatar
Owen Arnold committed
      QObject::connect(widget, SIGNAL(changedShownDim(int, int, int)), this,
                       SLOT(changedShownDim(int, int, int)));
      QObject::connect(widget, SIGNAL(changedSlicePoint(int, double)), this,
                       SLOT(updateDisplaySlot(int, double)));
      // Slots for dynamic rebinning
Owen Arnold's avatar
Owen Arnold committed
      QObject::connect(widget, SIGNAL(changedThickness(int, double)), this,
                       SLOT(rebinParamsChanged()));
      QObject::connect(widget, SIGNAL(changedNumBins(int, int)), this,
                       SLOT(rebinParamsChanged()));
      // Save in this list
      m_dimWidgets.push_back(widget);
    }
  }
  // Hide unnecessary ones
Owen Arnold's avatar
Owen Arnold committed
  for (size_t d = m_ws->getNumDims(); d < m_dimWidgets.size(); d++) {
    DimensionSliceWidget *widget = m_dimWidgets[d];
    widget->hide();
  }

  int maxLabelWidth = 10;
  int maxUnitsWidth = 10;
  // Set each dimension
Owen Arnold's avatar
Owen Arnold committed
  for (size_t d = 0; d < m_dimensions.size(); d++) {
    DimensionSliceWidget *widget = m_dimWidgets[d];
Owen Arnold's avatar
Owen Arnold committed
    widget->setDimension(int(d), m_dimensions[d]);
    // Default slicing layout
    if (d == m_dimX)
      widget->setShownDim(0);
    else if (d == m_dimY)
      widget->setShownDim(1);
    else
      widget->setShownDim(-1);

    // To harmonize the layout, find the largest label
    int w;
    w = widget->ui.lblName->sizeHint().width();
Owen Arnold's avatar
Owen Arnold committed
    if (w > maxLabelWidth)
      maxLabelWidth = w;
    w = widget->ui.lblUnits->sizeHint().width();
Owen Arnold's avatar
Owen Arnold committed
    if (w > maxUnitsWidth)
      maxUnitsWidth = w;
  }

  // Make the labels all the same width
Owen Arnold's avatar
Owen Arnold committed
  for (size_t d = 0; d < m_ws->getNumDims(); d++) {
    DimensionSliceWidget *widget = m_dimWidgets[d];
    widget->ui.lblName->setMinimumSize(QSize(maxLabelWidth, 0));
    widget->ui.lblUnits->setMinimumSize(QSize(maxUnitsWidth, 0));
  }
}

//------------------------------------------------------------------------------------
/** Set the displayed workspace. Updates UI.
 * @param ws :: IMDWorkspace to show.
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setWorkspace(Mantid::API::IMDWorkspace_sptr ws) {
  m_plot->setWorkspace(ws);
  // Only allow perpendicular lines if looking at a matrix workspace.
  bool matrix = bool(boost::dynamic_pointer_cast<MatrixWorkspace>(m_ws));
  m_lineOverlay->setAngleSnapMode(matrix);
  m_lineOverlay->setAngleSnap(matrix ? 90 : 45);

  // Can't use dynamic rebin mode with a MatrixWorkspace
  m_syncRebinMode->setEnabled(!matrix);
  m_syncRebinLock->setEnabled(!matrix);
  // Go to no normalization by default for MatrixWorkspaces
  if (matrix)
Owen Arnold's avatar
Owen Arnold committed
    this->setNormalization(Mantid::API::NoNormalization,
                           false /* without updating */);
  // Emit the signal that we changed the workspace
  emit workspaceChanged();

Owen Arnold's avatar
Owen Arnold committed
  // For MDEventWorkspace, estimate the resolution and change the # of bins
  // accordingly
  IMDEventWorkspace_sptr mdew =
      boost::dynamic_pointer_cast<IMDEventWorkspace>(m_ws);
  std::vector<coord_t> binSizes = m_ws->estimateResolution();
  // Copy the dimensions to this so they can be modified
  m_dimensions.clear();
  std::ostringstream mess;
Owen Arnold's avatar
Owen Arnold committed
  for (size_t d = 0; d < m_ws->getNumDims(); d++) {
    // Choose the number of bins based on the resolution of the workspace (for
    // MDEWs)
    coord_t min = m_ws->getDimension(d)->getMinimum();
    coord_t max = m_ws->getDimension(d)->getMaximum();
    if (boost::math::isnan(min) || boost::math::isinf(min) ||
Owen Arnold's avatar
Owen Arnold committed
        boost::math::isnan(max) || boost::math::isinf(max)) {
      mess << "Dimension " << m_ws->getDimension(d)->getName()
           << " has a bad range: (";
      mess << min << ", " << max << ")" << std::endl;
    }
Owen Arnold's avatar
Owen Arnold committed
    size_t numBins = static_cast<size_t>((max - min) / binSizes[d]);
    MDHistoDimension_sptr dim(
        new MDHistoDimension(m_ws->getDimension(d).get()));
    dim->setRange(numBins, min, max);
    m_dimensions.push_back(dim);
  }
Owen Arnold's avatar
Owen Arnold committed
  if (!mess.str().empty()) {
    mess << "Bad ranges could cause memory allocation errors. Please fix the "
            "workspace.";
    mess << std::endl << "You can continue using Mantid.";
    throw std::out_of_range(mess.str());
  }

  // Adjust the range to that of visible data
Owen Arnold's avatar
Owen Arnold committed
  if (mdew) {
    std::vector<Mantid::Geometry::MDDimensionExtents<coord_t>> ext =
        mdew->getMinimumExtents();
    for (size_t d = 0; d < mdew->getNumDims(); d++) {
      size_t newNumBins =
          size_t(ext[d].getSize() / m_dimensions[d]->getBinWidth() + 1);
      m_dimensions[d]->setRange(newNumBins, ext[d].getMin(), ext[d].getMax());
  // Build up the widgets
  this->updateDimensionSliceWidgets();

  // Find the full range. And use it
  findRangeFull();
  m_colorBar->setViewRange(m_colorRangeFull);
  // Initial display update
Owen Arnold's avatar
Owen Arnold committed
  this->updateDisplay(
      !m_firstWorkspaceOpen /*Force resetting the axes, the first time*/);
  // Don't reset axes next time
  m_firstWorkspaceOpen = true;
  // For showing the original coordinates
  ui.frmMouseInfo->setVisible(false);
Owen Arnold's avatar
Owen Arnold committed
  if (m_ws->hasOriginalWorkspace()) {
    IMDWorkspace_sptr origWS =
        boost::dynamic_pointer_cast<IMDWorkspace>(m_ws->getOriginalWorkspace());
    CoordTransform *toOrig = m_ws->getTransformToOriginal();
    if (toOrig) {
      ui.frmMouseInfo->setVisible(true);
Owen Arnold's avatar
Owen Arnold committed
      ui.lblOriginalWorkspace->setText(
          QString::fromStdString("in '" + origWS->getName() + "'"));
Owen Arnold's avatar
Owen Arnold committed
  // Enable peaks overlays according to the dimensionality and the displayed
  // dimensions.
  enablePeakOverlaysIfAppropriate();

  // Send out a signal
  emit changedShownDim(m_dimX, m_dimY);
//------------------------------------------------------------------------------------
/** Set the workspace to view using its name.
 * The workspace should be a MDHistoWorkspace or a MDEventWorkspace,
 * with at least 2 dimensions.
 *
 * @param wsName :: name of the MDWorkspace to look for
Owen Arnold's avatar
Owen Arnold committed
 * @throw std::runtime_error if the workspace is not found or is a
 *MatrixWorkspace
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setWorkspace(const QString &wsName) {
  IMDWorkspace_sptr ws = boost::dynamic_pointer_cast<IMDWorkspace>(
Owen Arnold's avatar
Owen Arnold committed
      AnalysisDataService::Instance().retrieve(wsName.toStdString()));
  if (!ws)
    throw std::runtime_error("SliceViewer can only view MDWorkspaces.");
  if (boost::dynamic_pointer_cast<MatrixWorkspace>(ws))
Owen Arnold's avatar
Owen Arnold committed
    throw std::runtime_error(
        "SliceViewer cannot view MatrixWorkspaces. "
        "Please select a MDEventWorkspace or a MDHistoWorkspace.");
  this->setWorkspace(ws);
}

//------------------------------------------------------------------------------------
/** @return the workspace in the SliceViewer */
Owen Arnold's avatar
Owen Arnold committed
Mantid::API::IMDWorkspace_sptr SliceViewer::getWorkspace() { return m_ws; }
//------------------------------------------------------------------------------------
/** Load a color map from a file
 *
 * @param filename :: file to open; empty to ask via a dialog box.
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::loadColorMap(QString filename) {
  QString fileselection;
Owen Arnold's avatar
Owen Arnold committed
  if (filename.isEmpty()) {
    fileselection = MantidColorMap::loadMapDialog(m_currentColorMapFile, this);
Owen Arnold's avatar
Owen Arnold committed
    if (fileselection.isEmpty())
      return;
  } else
    fileselection = filename;

  m_currentColorMapFile = fileselection;

  // Load from file
Owen Arnold's avatar
Owen Arnold committed
  m_colorBar->getColorMap().loadMap(fileselection);
  m_spect->setColorMap(m_colorBar->getColorMap());
  m_colorBar->updateColorMap();
  this->updateDisplay();
}

//=================================================================================================
Owen Arnold's avatar
Owen Arnold committed
//========================================== SLOTS
//================================================
//=================================================================================================

//------------------------------------------------------------------------------------
/** Automatically sets the min/max of the color scale,
 * using the limits in the entire data set of the workspace
 * (every bin, even those not currently visible).
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setColorScaleAutoFull() {
  this->findRangeFull();
  m_colorBar->setViewRange(m_colorRangeFull);
  this->updateDisplay();
}

//------------------------------------------------------------------------------------
/** Automatically sets the min/max of the color scale,
 * using the limits in the data that is currently visible
 * in the plot (only the bins in this slice and within the
 * view limits)
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setColorScaleAutoSlice() {
  this->findRangeSlice();
  m_colorBar->setViewRange(m_colorRangeSlice);
  this->updateDisplay();
}

//------------------------------------------------------------------------------------
/// Slot called when the ColorBarWidget changes the range of colors
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::colorRangeChanged() {
  m_spect->setColorMap(m_colorBar->getColorMap());
//------------------------------------------------------------------------------------
/** Set whether to display 0 signal as "transparent" color.
 *
 * @param transparent :: true if you want zeros to be transparent.
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setTransparentZeros(bool transparent) {
  m_actionTransparentZeros->blockSignals(true);
  m_actionTransparentZeros->setChecked(transparent);
  m_actionTransparentZeros->blockSignals(false);
  // Set and display
Owen Arnold's avatar
Owen Arnold committed
  m_data->setZerosAsNan(transparent);
//------------------------------------------------------------------------------------
/// Slot called when changing the normalization menu
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::changeNormalizationNone() {
  this->setNormalization(Mantid::API::NoNormalization, true);
}
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::changeNormalizationVolume() {
  this->setNormalization(Mantid::API::VolumeNormalization, true);
}
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::changeNormalizationNumEvents() {
  this->setNormalization(Mantid::API::NumEventsNormalization, true);
}

//------------------------------------------------------------------------------------
/** Set the normalization mode for viewing the data
 *
 * @param norm :: MDNormalization enum. 0=none; 1=volume; 2=# of events
Owen Arnold's avatar
Owen Arnold committed
 * @param update :: update the displayed image. If false, just sets it and shows
 *the checkboxes.
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setNormalization(Mantid::API::MDNormalization norm,
                                   bool update) {
  m_actionNormalizeNone->blockSignals(true);
  m_actionNormalizeVolume->blockSignals(true);
  m_actionNormalizeNumEvents->blockSignals(true);

  m_actionNormalizeNone->setChecked(norm == Mantid::API::NoNormalization);
  m_actionNormalizeVolume->setChecked(norm == Mantid::API::VolumeNormalization);
Owen Arnold's avatar
Owen Arnold committed
  m_actionNormalizeNumEvents->setChecked(norm ==
                                         Mantid::API::NumEventsNormalization);

  m_actionNormalizeNone->blockSignals(false);
  m_actionNormalizeVolume->blockSignals(false);
  m_actionNormalizeNumEvents->blockSignals(false);

  m_data->setNormalization(norm);
Owen Arnold's avatar
Owen Arnold committed
  if (update)
    this->updateDisplay();
//------------------------------------------------------------------------------------
/** @return the current normalization */
Owen Arnold's avatar
Owen Arnold committed
Mantid::API::MDNormalization SliceViewer::getNormalization() const {
//------------------------------------------------------------------------------------
/** Set the thickness (above and below the plane) for dynamic rebinning.
 *
 * @param dim :: index of the dimension to adjust
 * @param thickness :: thickness to set, in units of the dimension.
Owen Arnold's avatar
Owen Arnold committed
 * @throw runtime_error if the dimension index is invalid or the thickness is <=
 *0.0.
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setRebinThickness(int dim, double thickness) {
  if (dim < 0 || dim >= static_cast<int>(m_dimWidgets.size()))
Owen Arnold's avatar
Owen Arnold committed
    throw std::runtime_error(
        "SliceViewer::setRebinThickness(): Invalid dimension index");
Owen Arnold's avatar
Owen Arnold committed
    throw std::runtime_error(
        "SliceViewer::setRebinThickness(): Thickness must be > 0.0");
  m_dimWidgets[dim]->setThickness(thickness);
}

//------------------------------------------------------------------------------------
/** Set the number of bins for dynamic rebinning.
 *
 * @param xBins :: number of bins in the viewed X direction
 * @param yBins :: number of bins in the viewed Y direction
 * @throw runtime_error if the number of bins is < 1
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setRebinNumBins(int xBins, int yBins) {
  if (xBins < 1 || yBins < 1)
Owen Arnold's avatar
Owen Arnold committed
    throw std::runtime_error(
        "SliceViewer::setRebinNumBins(): Number of bins must be >= 1");
  m_dimWidgets[m_dimX]->setNumBins(xBins);
  m_dimWidgets[m_dimY]->setNumBins(yBins);
}

//------------------------------------------------------------------------------------
/** Sets the SliceViewer in dynamic rebin mode.
 * In this mode, the current view area (see setXYLimits()) is used as the
 * limits to rebin.
 * See setRebinNumBins() to adjust the number of bins in the X/Y dimensions.
 * See setRebinThickness() to adjust the thickness in other dimensions.
 *
 * @param mode :: true for rebinning mode
 * @param locked :: if true, then the rebinned area is only refreshed manually
 *        or when changing rebinning parameters.
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::setRebinMode(bool mode, bool locked) {
  // The events associated with these controls will trigger a re-draw
  m_syncRebinMode->toggle(mode);
  m_syncRebinLock->toggle(locked);
}

//------------------------------------------------------------------------------------
/** When in dynamic rebinning mode, this refreshes the rebinned area to be the
Owen Arnold's avatar
Owen Arnold committed
 * currently viewed area. See setXYLimits(), setRebinNumBins(),
 * setRebinThickness()
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::refreshRebin() { this->rebinParamsChanged(); }
//------------------------------------------------------------------------------------
/// Slot called when the btnDoLine button is checked/unchecked
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::LineMode_toggled(bool checked) {
  m_lineOverlay->setShown(checked);
Owen Arnold's avatar
Owen Arnold committed
  if (checked) {
    QString text;
    if (m_lineOverlay->getCreationMode())
      text = "Click and drag to draw an integration line.\n"
             "Hold Shift key to limit to 45 degree angles.";
    else
      text = "Drag the existing line with its handles,\n"
             "or click the red X to delete it.";
    // Show a tooltip near the button
Owen Arnold's avatar
Owen Arnold committed
    QToolTip::showText(ui.btnDoLine->mapToGlobal(ui.btnDoLine->pos()), text,
                       this);
  emit showLineViewer(checked);
//------------------------------------------------------------------------------------
/** Toggle "line-drawing" mode (to draw 1D lines using the mouse)
 *
 * @param lineMode :: True to go into line mode, False to exit it.
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::toggleLineMode(bool lineMode) {
  // This should send events to start line mode
  m_syncLineMode->toggle(lineMode);
  m_lineOverlay->setCreationMode(false);
}

//------------------------------------------------------------------------------------
/// Slot called to clear the line in the line overlay
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::clearLine() {
  m_lineOverlay->reset();
  m_plot->update();
}

//------------------------------------------------------------------------------------
/// Slot called when the snap to grid is checked
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::SnapToGrid_toggled(bool checked) {
  if (checked) {
    SnapToGridDialog *dlg = new SnapToGridDialog(this);
    dlg->setSnap(m_lineOverlay->getSnapX(), m_lineOverlay->getSnapY());
    if (dlg->exec() == QDialog::Accepted) {
      m_lineOverlay->setSnapEnabled(true);
Owen Arnold's avatar
Owen Arnold committed
      m_lineOverlay->setSnapX(dlg->getSnapX());
      m_lineOverlay->setSnapY(dlg->getSnapY());
    } else {
      // Uncheck - the user clicked cancel
      ui.btnSnapToGrid->setChecked(false);
      m_lineOverlay->setSnapEnabled(false);
Owen Arnold's avatar
Owen Arnold committed
  } else {
    m_lineOverlay->setSnapEnabled(false);
  }
}

//------------------------------------------------------------------------------------
/** Slot called when going into or out of dynamic rebinning mode */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::RebinMode_toggled(bool checked) {
  for (size_t d = 0; d < m_dimWidgets.size(); d++)
    m_dimWidgets[d]->showRebinControls(checked);
  ui.btnRebinRefresh->setEnabled(checked);
  ui.btnRebinLock->setEnabled(checked);
  m_syncAutoRebin->setEnabled(checked);
  m_actionRefreshRebin->setEnabled(checked);
Owen Arnold's avatar
Owen Arnold committed
  if (!m_rebinMode) {
    // uncheck auto-rebin
    ui.btnAutoRebin->setChecked(false);
    // Remove the overlay WS
    this->m_overlayWS.reset();
    this->m_data->setOverlayWorkspace(m_overlayWS);
    this->updateDisplay();
Owen Arnold's avatar
Owen Arnold committed
  } else {
    // Start the rebin
    this->rebinParamsChanged();
  }
//------------------------------------------------------------------------------------
/** Slot called when locking/unlocking the dynamically rebinned
 * overlaid workspace
 * @param checked :: DO lock the workspace in place
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::RebinLock_toggled(bool checked) {
  m_rebinLocked = checked;
  // Rebin immediately
  if (!m_rebinLocked && m_rebinMode)
    this->rebinParamsChanged();
//------------------------------------------------------------------------------------
/// Slot for zooming into
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::zoomInSlot() { this->zoomBy(1.1); }

/// Slot for zooming out
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::zoomOutSlot() { this->zoomBy(1.0 / 1.1); }
/** Slot called when zooming using QwtPlotZoomer (rubber-band method)
 *
 * @param rect :: rectangle to zoom to
 */
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::zoomRectSlot(const QwtDoubleRect &rect) {
  if ((rect.width() == 0) || (rect.height() == 0))
    return;
  this->setXYLimits(rect.left(), rect.right(), rect.top(), rect.bottom());
  autoRebinIfRequired();
/// Slot for opening help page
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::helpSliceViewer() {
  QString helpPage = "MantidPlot:_SliceViewer";
Owen Arnold's avatar
Owen Arnold committed
  QDesktopServices::openUrl(
      QUrl(QString("http://www.mantidproject.org/") + helpPage));
}

/// Slot for opening help page
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::helpLineViewer() {
  QString helpPage = "MantidPlot:_LineViewer";
Owen Arnold's avatar
Owen Arnold committed
  QDesktopServices::openUrl(
      QUrl(QString("http://www.mantidproject.org/") + helpPage));
Owen Arnold's avatar
Owen Arnold committed
void SliceViewer::helpPeaksViewer() {
  QString helpPage = "PeaksViewer";
Owen Arnold's avatar
Owen Arnold committed
  QDesktopServices::openUrl(
      QUrl(QString("http://www.mantidproject.org/") + helpPage));