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"
Janik Zikovsky
committed
#include "MantidAPI/IMDIterator.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 <qwt_plot_panner.h>
Federico Montesino Pouzols
committed
#include <Poco/AutoPtr.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;
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;
//------------------------------------------------------------------------------------
/** Constructor */
SliceViewer::SliceViewer(QWidget *parent)
: 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_peaksPresenter(boost::make_shared<CompositePeaksPresenter>(this)),
boost::make_shared<ProxyCompositePeaksPresenter>(m_peaksPresenter)),
m_peaksSliderWidget(NULL){
m_inf = std::numeric_limits<double>::infinity();
// 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;
m_colorBar->setLog(true);
QObject::connect(m_colorBar, SIGNAL(changedColorRange(double, double, bool)),
this, SLOT(colorRangeChanged()));
Janik Zikovsky
committed
// ---- Set the color map on the data ------
m_data = new API::QwtRasterDataMD();
m_spect->setColorMap(m_colorBar->getColorMap());
Janik Zikovsky
committed
m_plot->autoRefresh();
Janik Zikovsky
committed
// 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();
Janik Zikovsky
committed
ui.btnZoom->hide();
Janik Zikovsky
committed
// ----------- Toolbar button signals ----------------
QObject::connect(ui.btnResetZoom, SIGNAL(clicked()), this, SLOT(resetZoom()));
QObject::connect(ui.btnClearLine, SIGNAL(clicked()), this, SLOT(clearLine()));
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()));
Janik Zikovsky
committed
// ----------- Other signals ----------------
QObject::connect(m_colorBar, SIGNAL(colorBarDoubleClicked()), this,
SLOT(loadColorMapSlot()));
m_algoRunner = new AlgorithmRunner(this);
QObject::connect(m_algoRunner, SIGNAL(algorithmComplete(bool)), this,
SLOT(dynamicRebinComplete(bool)));
Janik Zikovsky
committed
loadSettings();
Janik Zikovsky
committed
updateDisplay();
// -------- 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 ----------------
m_peakTransformSelector.registerCandidate(
boost::make_shared<PeakTransformHKLFactory>());
m_peakTransformSelector.registerCandidate(
boost::make_shared<PeakTransformQSampleFactory>());
m_peakTransformSelector.registerCandidate(
boost::make_shared<PeakTransformQLabFactory>());
//------------------------------------------------------------------------------------
/// Destructor
saveSettings();
// Don't delete Qt objects, I think these are auto-deleted
//------------------------------------------------------------------------------------
/** Load QSettings from .ini-type files */
QSettings settings;
settings.beginGroup("Mantid/SliceViewer");
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())
m_colorBar->setLog(scaleType);
// 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();
Mantid::API::MDNormalization normaliz =
static_cast<Mantid::API::MDNormalization>(norm);
this->setNormalization(normaliz);
settings.endGroup();
}
//------------------------------------------------------------------------------------
/** Save settings for next time. */
QSettings settings;
settings.beginGroup("Mantid/SliceViewer");
settings.setValue("ColormapFile", m_currentColorMapFile);
settings.setValue("LogColorScale", (int)m_colorBar->getLog());
settings.setValue("LastSavedImagePath", m_lastSavedFile);
settings.setValue("TransparentZeros",
(m_actionTransparentZeros->isChecked() ? 1 : 0));
settings.setValue("Normalization",
static_cast<int>(this->getNormalization()));
settings.endGroup();
}
//------------------------------------------------------------------------------------
/** Create the menus */
// ---------------------- Build the menu bar -------------------------
// Find the top-level parent
QWidget *widget = this;
while (widget && widget->parentWidget())
widget = widget->parentWidget();
QMainWindow *parentWindow = dynamic_cast<QMainWindow *>(widget);
if (parentWindow)
// Use the QMainWindow menu bar
bar = parentWindow->menuBar();
// Widget is not in a QMainWindow. Make a menu bar
bar = new QMenuBar(this, "Main Menu Bar");
// --------------- 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()));
{
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);
m_menuView->addSeparator();
action = new QAction(QPixmap(), "Dynamic R&ebin Mode", this);
m_syncRebinMode = new SyncedCheckboxes(action, ui.btnRebinMode, false);
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);
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();
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);
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);
connect(action, SIGNAL(triggered()), this,
SLOT(changeNormalizationNumEvents()));
m_actionNormalizeNumEvents = action;
// --------------- Color options Menu ----------------------------------------
m_menuColorOptions = new QMenu("&ColorMap", this);
Janik Zikovsky
committed
action = new QAction(QPixmap(), "&Load Colormap", this);
connect(action, SIGNAL(triggered()), this, SLOT(loadColorMapSlot()));
Janik Zikovsky
committed
m_menuColorOptions->addAction(action);
action = new QAction(QPixmap(), "&Full range", this);
connect(action, SIGNAL(triggered()), this, SLOT(setColorScaleAutoFull()));
{
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);
{
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);
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);
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);
connect(action, SIGNAL(triggered()), this,
SLOT(onPeaksViewerOverlayOptions()));
m_menuPeaks->addAction(action);
action = new QAction(QPixmap(), "&Visible Columns", this);
connect(action, SIGNAL(triggered()), this,
SIGNAL(peaksTableColumnOptions())); // just re-emit
m_menuPeaks->setEnabled(false); // Until a PeaksWorkspace is selected.
// Add all the needed menus
bar->addMenu(m_menuFile);
bar->addMenu(m_menuView);
bar->addMenu(m_menuColorOptions);
bar->addMenu(m_menuLine);
bar->addMenu(m_menuPeaks);
bar->addMenu(m_menuHelp);
Janik Zikovsky
committed
//------------------------------------------------------------------------------------
/** Intialize the zooming/panning tools */
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);
QObject::connect(zoomer, SIGNAL(selected(const QwtDoubleRect &)), this,
SLOT(zoomRectSlot(const QwtDoubleRect &)));
Janik Zikovsky
committed
// Zoom in/out using middle-click+drag or the mouse wheel
CustomMagnifier *magnif = new CustomMagnifier(m_plot->canvas());
Janik Zikovsky
committed
magnif->setAxisEnabled(QwtPlot::yRight, false); // Don't do the colorbar axis
magnif->setWheelFactor(0.9);
magnif->setMouseButton(Qt::MidButton);
Janik Zikovsky
committed
// 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);
QObject::connect(magnif, SIGNAL(rescaled(double)), this,
SLOT(magnifierRescaled(double)));
Janik Zikovsky
committed
// Pan using the right mouse button + drag
Janik Zikovsky
committed
QwtPlotPanner *panner = new QwtPlotPanner(m_plot->canvas());
panner->setMouseButton(Qt::RightButton);
Janik Zikovsky
committed
panner->setAxisEnabled(QwtPlot::yRight, false); // Don't do the colorbar axis
QObject::connect(panner, SIGNAL(panned(int, int)), this,
SLOT(panned(int, int))); // Handle panning.
Janik Zikovsky
committed
// Custom picker for showing the current coordinates
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)));
Janik Zikovsky
committed
}
Janik Zikovsky
committed
//------------------------------------------------------------------------------------
/** Programmatically show/hide the controls (sliders etc)
*
* @param visible :: true if you want to show the controls.
*/
Janik Zikovsky
committed
ui.frmControls->setVisible(visible);
}
//------------------------------------------------------------------------------------
/** Add (as needed) and update DimensionSliceWidget's. */
void SliceViewer::updateDimensionSliceWidgets() {
// Create all necessary widgets
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
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
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
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
for (size_t d = 0; d < m_dimensions.size(); d++) {
DimensionSliceWidget *widget = m_dimWidgets[d];
widget->blockSignals(true);
// 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();
w = widget->ui.lblUnits->sizeHint().width();
widget->blockSignals(false);
}
// Make the labels all the same width
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.
void SliceViewer::setWorkspace(Mantid::API::IMDWorkspace_sptr ws) {
m_data->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)
this->setNormalization(Mantid::API::NoNormalization,
false /* without updating */);
// Emit the signal that we changed the workspace
emit workspaceChanged();
// 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();
Janik Zikovsky
committed
// Copy the dimensions to this so they can be modified
m_dimensions.clear();
std::ostringstream mess;
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) ||
boost::math::isnan(max) || boost::math::isinf(max)) {
mess << "Dimension " << m_ws->getDimension(d)->getName()
<< " has a bad range: (";
mess << min << ", " << max << ")" << std::endl;
}
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);
}
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());
}
Janik Zikovsky
committed
// Adjust the range to that of visible data
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());
Janik Zikovsky
committed
}
Janik Zikovsky
committed
// Build up the widgets
this->updateDimensionSliceWidgets();
// Find the full range. And use it
findRangeFull();
m_colorBar->setViewRange(m_colorRangeFull);
// Initial display update
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);
if (m_ws->hasOriginalWorkspace()) {
IMDWorkspace_sptr origWS =
boost::dynamic_pointer_cast<IMDWorkspace>(m_ws->getOriginalWorkspace());
CoordTransform *toOrig = m_ws->getTransformToOriginal();
if (toOrig) {
ui.lblOriginalWorkspace->setText(
QString::fromStdString("in '" + origWS->getName() + "'"));
// Enable peaks overlays according to the dimensionality and the displayed
// dimensions.
enablePeakOverlaysIfAppropriate();
// Send out a signal
emit changedShownDim(m_dimX, m_dimY);
Janik Zikovsky
committed
}
//------------------------------------------------------------------------------------
/** 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
* @throw std::runtime_error if the workspace is not found or is a
*MatrixWorkspace
void SliceViewer::setWorkspace(const QString &wsName) {
IMDWorkspace_sptr ws = boost::dynamic_pointer_cast<IMDWorkspace>(
AnalysisDataService::Instance().retrieve(wsName.toStdString()));
if (!ws)
throw std::runtime_error("SliceViewer can only view MDWorkspaces.");
if (boost::dynamic_pointer_cast<MatrixWorkspace>(ws))
throw std::runtime_error(
"SliceViewer cannot view MatrixWorkspaces. "
"Please select a MDEventWorkspace or a MDHistoWorkspace.");
this->setWorkspace(ws);
}
//------------------------------------------------------------------------------------
/** @return the workspace in the SliceViewer */
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.
void SliceViewer::loadColorMap(QString filename) {
fileselection = MantidColorMap::loadMapDialog(m_currentColorMapFile, this);
if (fileselection.isEmpty())
return;
} else
fileselection = filename;
m_currentColorMapFile = fileselection;
// Load from file
m_colorBar->getColorMap().loadMap(fileselection);
m_spect->setColorMap(m_colorBar->getColorMap());
m_colorBar->updateColorMap();
this->updateDisplay();
}
//=================================================================================================
//========================================== 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).
*/
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)
*/
m_colorBar->setViewRange(m_colorRangeSlice);
this->updateDisplay();
}
//------------------------------------------------------------------------------------
/// Slot called when the ColorBarWidget changes the range of colors
void SliceViewer::colorRangeChanged() {
m_spect->setColorMap(m_colorBar->getColorMap());
this->updateDisplay();
}
//------------------------------------------------------------------------------------
/** Set whether to display 0 signal as "transparent" color.
*
* @param transparent :: true if you want zeros to be transparent.
*/
void SliceViewer::setTransparentZeros(bool transparent) {
m_actionTransparentZeros->blockSignals(true);
m_actionTransparentZeros->setChecked(transparent);
m_actionTransparentZeros->blockSignals(false);
// Set and display
this->updateDisplay();
}
//------------------------------------------------------------------------------------
/// Slot called when changing the normalization menu
void SliceViewer::changeNormalizationNone() {
this->setNormalization(Mantid::API::NoNormalization, true);
}
void SliceViewer::changeNormalizationVolume() {
this->setNormalization(Mantid::API::VolumeNormalization, true);
}
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
* @param update :: update the displayed image. If false, just sets it and shows
*the checkboxes.
*/
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);
m_actionNormalizeNumEvents->setChecked(norm ==
Mantid::API::NumEventsNormalization);
m_actionNormalizeNone->blockSignals(false);
m_actionNormalizeVolume->blockSignals(false);
m_actionNormalizeNumEvents->blockSignals(false);
m_data->setNormalization(norm);
//------------------------------------------------------------------------------------
/** @return the current normalization */
Mantid::API::MDNormalization SliceViewer::getNormalization() const {
return m_data->getNormalization();
}
//------------------------------------------------------------------------------------
/** 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.
* @throw runtime_error if the dimension index is invalid or the thickness is <=
*0.0.
void SliceViewer::setRebinThickness(int dim, double thickness) {
if (dim < 0 || dim >= static_cast<int>(m_dimWidgets.size()))
throw std::runtime_error(
"SliceViewer::setRebinThickness(): Invalid dimension index");
if (thickness <= 0.0)
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
*/
void SliceViewer::setRebinNumBins(int xBins, int yBins) {
if (xBins < 1 || yBins < 1)
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.
*/
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
* currently viewed area. See setXYLimits(), setRebinNumBins(),
* setRebinThickness()
void SliceViewer::refreshRebin() { this->rebinParamsChanged(); }
//------------------------------------------------------------------------------------
/// Slot called when the btnDoLine button is checked/unchecked
void SliceViewer::LineMode_toggled(bool checked) {
m_lineOverlay->setShown(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
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.
*/
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
m_lineOverlay->reset();
m_plot->update();
}
//------------------------------------------------------------------------------------
/// Slot called when the snap to grid is checked
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);
m_lineOverlay->setSnapX(dlg->getSnapX());
m_lineOverlay->setSnapY(dlg->getSnapY());
} else {
ui.btnSnapToGrid->setChecked(false);
m_lineOverlay->setSnapEnabled(false);
m_lineOverlay->setSnapEnabled(false);
}
}
//------------------------------------------------------------------------------------
/** Slot called when going into or out of dynamic rebinning mode */
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_actionRefreshRebin->setEnabled(checked);
m_rebinMode = checked;
// uncheck auto-rebin
ui.btnAutoRebin->setChecked(false);
// Remove the overlay WS
this->m_overlayWS.reset();
this->m_data->setOverlayWorkspace(m_overlayWS);
this->updateDisplay();
// Start the rebin
this->rebinParamsChanged();
}
//------------------------------------------------------------------------------------
/** Slot called when locking/unlocking the dynamically rebinned
* overlaid workspace
* @param checked :: DO lock the workspace in place
*/
void SliceViewer::RebinLock_toggled(bool checked) {
m_rebinLocked = checked;
// Rebin immediately
if (!m_rebinLocked && m_rebinMode)
this->rebinParamsChanged();
//------------------------------------------------------------------------------------
/// Slot for zooming into
void SliceViewer::zoomInSlot() { this->zoomBy(1.1); }
/// Slot for zooming out
void SliceViewer::zoomOutSlot() { this->zoomBy(1.0 / 1.1); }
/** Slot called when zooming using QwtPlotZoomer (rubber-band method)
*
* @param rect :: rectangle to zoom to
*/
void SliceViewer::zoomRectSlot(const QwtDoubleRect &rect) {
if ((rect.width() == 0) || (rect.height() == 0))
return;
this->setXYLimits(rect.left(), rect.right(), rect.top(), rect.bottom());
QString helpPage = "MantidPlot:_SliceViewer";
QDesktopServices::openUrl(
QUrl(QString("http://www.mantidproject.org/") + helpPage));
}
/// Slot for opening help page
QString helpPage = "MantidPlot:_LineViewer";
QDesktopServices::openUrl(
QUrl(QString("http://www.mantidproject.org/") + helpPage));
QDesktopServices::openUrl(
QUrl(QString("http://www.mantidproject.org/") + helpPage));