Commit 0cf05e40 authored by Hahn, Steven's avatar Hahn, Steven Committed by GitHub
Browse files

Merge pull request #19237 from mantidproject/18159_harmonise_plotting2

Harmonise plotting of workspaces from right-button click
parents 07abacaa 9ea9554c
......@@ -183,9 +183,10 @@ set ( MANTID_SRCS src/Mantid/AlgorithmDockWidget.cpp
src/Mantid/MantidMDCurve.cpp
src/Mantid/MantidMDCurveDialog.cpp
src/Mantid/MantidMatrixDialog.cpp
src/Mantid/MantidPlotUtilities.cpp
src/Mantid/MantidSampleLogDialog.cpp
src/Mantid/MantidSampleMaterialDialog.cpp
src/Mantid/MantidGroupPlotGenerator.cpp
src/Mantid/MantidSurfaceContourPlotGenerator.cpp
src/Mantid/MantidUI.cpp
src/Mantid/MantidTable.cpp
src/Mantid/PeakPickerTool.cpp
......@@ -375,9 +376,10 @@ set ( MANTID_HDRS src/Mantid/AlgorithmMonitor.h
src/Mantid/MantidMatrixDialog.h
src/Mantid/MantidMatrix.h
src/Mantid/MantidMatrixFunction.h
src/Mantid/MantidPlotUtilities.h
src/Mantid/MantidSampleLogDialog.h
src/Mantid/MantidSampleMaterialDialog.h
src/Mantid/MantidGroupPlotGenerator.h
src/Mantid/MantidSurfaceContourPlotGenerator.h
src/Mantid/MantidUI.h
src/Mantid/MantidTable.h
src/Mantid/PeakPickerTool.h
......
......@@ -107,7 +107,8 @@ private:
mutable QwtDoubleRect m_boundingRect;
// To ensure that all MantidCurves can work with Mantid Workspaces.
virtual void init(Graph *g, bool distr, GraphOptions::CurveType style) = 0;
virtual void init(Graph *g, bool distr, GraphOptions::CurveType style,
bool multileSpectra = false) = 0;
};
#endif
......@@ -50,11 +50,16 @@ MantidMDCurve::MantidMDCurve(const MantidMDCurve &c)
/**
* @param g :: The Graph widget which will display the curve
* @param distr :: True if this is a distribution
* @param distr :: True if this is a distribution,
* not applicable here.
* @param style :: The graph style to use
* @param multipleSpectra :: True if there are multiple spectra,
* not applicable here.
*/
void MantidMDCurve::init(Graph *g, bool distr, GraphOptions::CurveType style) {
void MantidMDCurve::init(Graph *g, bool distr, GraphOptions::CurveType style,
bool multipleSpectra) {
UNUSED_ARG(distr);
UNUSED_ARG(multipleSpectra);
IMDWorkspace_const_sptr ws = boost::dynamic_pointer_cast<IMDWorkspace>(
AnalysisDataService::Instance().retrieve(m_wsName.toStdString()));
if (!ws) {
......
......@@ -90,7 +90,8 @@ private:
using PlotCurve::draw; // Avoid Intel compiler warning
/// Init the curve
void init(Graph *g, bool distr, GraphOptions::CurveType style) override;
void init(Graph *g, bool distr, GraphOptions::CurveType style,
bool multipleSpectra = false) override;
/// Handles delete notification
void postDeleteHandle(const std::string &wsName) override {
......
......@@ -28,6 +28,7 @@ Mantid::Kernel::Logger g_log("MantidMatrixCurve");
/**
* @param name :: The curve's name - shown in the legend
*default name is used, if empty.
* @param wsName :: The workspace name.
* @param g :: The Graph widget which will display the curve
* @param index :: The index of the spectrum or bin in the workspace
......@@ -36,22 +37,27 @@ Mantid::Kernel::Logger g_log("MantidMatrixCurve");
* @param err :: True if the errors are to be plotted
* @param distr :: True if it is a distribution
* @param style :: CurveType style to use
* @param multipleSpectra :: indicates that there are multiple spectra and
* so spectrum numbers must always be shown in the plot legend.
*..@throw Mantid::Kernel::Exception::NotFoundError if the workspace cannot be
*found
* @throw std::invalid_argument if the index is out of range for the given
*workspace
*/
MantidMatrixCurve::MantidMatrixCurve(const QString &, const QString &wsName,
MantidMatrixCurve::MantidMatrixCurve(const QString &name, const QString &wsName,
Graph *g, int index, IndexDir indexType,
bool err, bool distr,
GraphOptions::CurveType style)
GraphOptions::CurveType style,
bool multipleSpectra)
: MantidCurve(err), m_wsName(wsName), m_index(index),
m_indexType(indexType) {
if (!g) {
throw std::invalid_argument("MantidMatrixCurve::MantidMatrixCurve - NULL "
"graph pointer not allowed");
}
init(g, distr, style);
if (!name.isEmpty())
this->setTitle(name);
init(g, distr, style, multipleSpectra);
}
/**
......@@ -91,9 +97,12 @@ MantidMatrixCurve::MantidMatrixCurve(const MantidMatrixCurve &c)
* @param g :: The Graph widget which will display the curve
* @param distr :: True for a distribution
* @param style :: The curve type to use
* @param multipleSpectra :: indicates that there are multiple spectra and
* so spectrum numbers must always be shown in the plot legend.
*/
void MantidMatrixCurve::init(Graph *g, bool distr,
GraphOptions::CurveType style) {
GraphOptions::CurveType style,
bool multipleSpectra) {
// Will throw if name not found but return NULL ptr if the type is incorrect
MatrixWorkspace_const_sptr workspace =
AnalysisDataService::Instance().retrieveWS<MatrixWorkspace>(
......@@ -110,14 +119,21 @@ void MantidMatrixCurve::init(Graph *g, bool distr,
}
// Set the curve name if it the non-naming constructor was called
// or the naming constructor was called with empty name.
if (this->title().isEmpty()) {
// If there's only one spectrum in the workspace, title is simply workspace
// If there's only one histrogram in the workspace, title is simply
// workspace
// name
if (workspace->getNumberHistograms() == 1)
this->setTitle(m_wsName);
else
this->setTitle(createCurveName(workspace));
this->setTitle(createCurveName("", workspace));
} else if (multipleSpectra) {
this->setTitle(createCurveName(this->title().text(), workspace));
}
// Here we have to catch the case when there is more than on spectrum and
// append the spectrum name like in createCurveName(Workspace).
// Perhaps, with new CreateCurveName(this->title(), Workspace)
Mantid::API::MatrixWorkspace_const_sptr matrixWS =
boost::dynamic_pointer_cast<const Mantid::API::MatrixWorkspace>(
......@@ -262,15 +278,24 @@ void MantidMatrixCurve::itemChanged() {
/**
* Create the name for a curve from the following input:
* @param prefix :: prefix for name, if empty, workspace name is used.
* @param ws :: Pointer to workspace
*/
QString MantidMatrixCurve::createCurveName(
const QString &prefix,
const boost::shared_ptr<const Mantid::API::MatrixWorkspace> ws) {
QString name = m_wsName + "-";
QString name = "";
if (prefix.isEmpty())
name += m_wsName + "-";
else
name += prefix + "-";
if (m_indexType == Spectrum)
name += QString::fromStdString(ws->getAxis(1)->label(m_index));
else
name += "bin-" + QString::number(m_index);
return name;
}
......
......@@ -53,7 +53,8 @@ public:
MantidMatrixCurve(const QString &name, const QString &wsName, Graph *g,
int index, IndexDir indexType, bool err = false,
bool distr = false,
GraphOptions::CurveType style = GraphOptions::Unspecified);
GraphOptions::CurveType style = GraphOptions::Unspecified,
bool multipleSpectra = false);
/// More complex constructor setting some defaults for the curve
MantidMatrixCurve(const QString &wsName, Graph *g, int index,
......@@ -129,7 +130,8 @@ private:
using PlotCurve::draw; // Avoid Intel compiler warning
/// Init the curve
void init(Graph *g, bool distr, GraphOptions::CurveType style) override;
void init(Graph *g, bool distr, GraphOptions::CurveType style,
bool multipleSpectra = false) override;
/// Handles delete notification
void postDeleteHandle(const std::string &wsName) override {
......@@ -157,6 +159,7 @@ private slots:
private:
/// Make the curve name
QString createCurveName(
const QString &prefix,
const boost::shared_ptr<const Mantid::API::MatrixWorkspace> ws);
QString
......
#include "MantidPlotUtilities.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidGeometry/MDGeometry/IMDDimension.h"
#include <MantidQtMantidWidgets/MantidDisplayBase.h>
using namespace MantidQt::MantidWidgets;
using Mantid::API::WorkspaceGroup_const_sptr;
using Mantid::API::WorkspaceGroup_sptr;
using Mantid::API::MatrixWorkspace_const_sptr;
using Mantid::API::MatrixWorkspace_sptr;
using Mantid::API::MatrixWorkspace;
using Mantid::API::ExperimentInfo;
using Mantid::HistogramData::Histogram;
/**Compare two CurveSpecs to sort according to log value and
* if equal by workspace index.
* @param lhs left hand comparee
* @param rhs right hand comparee
* @returns true if right hand comparee has greater log value than left hand
* comparee
*/
bool byLogValue(const CurveSpec &lhs, const CurveSpec &rhs) {
if (lhs.logVal == rhs.logVal)
return (lhs.index < rhs.index);
return (lhs.logVal < rhs.logVal);
}
/**
* Gets the given log value from the given workspace as a double.
* Should be a single-valued log!
* @param wsIndex :: [input] Index of workspace in group
* @param matrixWS :: [input] Workspace to find log from
* @param logName :: [input] Name of log
* @returns log value as a double, or workspace index
* @throws invalid_argument if log is wrong type or not present
*/
double getSingleWorkspaceLogValue(
size_t wsIndex, const Mantid::API::MatrixWorkspace_const_sptr &matrixWS,
const QString &logName) {
if (logName == MantidWSIndexWidget::WORKSPACE_INDEX || logName == "")
return static_cast<double>(wsIndex); // cast for plotting
// MatrixWorkspace is an ExperimentInfo
auto log = matrixWS->run().getLogData(logName.toStdString());
if (!log)
throw std::invalid_argument("Log not present in workspace");
if (dynamic_cast<Mantid::Kernel::PropertyWithValue<int> *>(log) ||
dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(log))
return std::stod(log->value());
throw std::invalid_argument(
"Log is of wrong type (expected single numeric value");
}
/**
* Gets the custom, user-provided log value of the given index.
* i.e. the nth in order from smallest to largest.
* If the index is outside the range, returns 0.
* @param wsIndex :: [input] Index of log value to use
* @param logValues :: [input] User-provided set of log values
* @returns Numeric log value
*/
double getSingleWorkspaceLogValue(size_t wsIndex,
const std::set<double> &logValues) {
if (wsIndex >= logValues.size())
return 0;
auto it = logValues.begin();
std::advance(it, wsIndex);
return *it;
}
#ifndef MANTIDPLOTUTILITIES_H_
#define MANTIDPLOTUTILITIES_H_
#include "MantidQtMantidWidgets/MantidWSIndexDialog.h"
/**
* These utilities assist with plotting in Mantid
*/
/// Structure to aid ordering of plots
struct CurveSpec {
double logVal;
QString wsName;
int index;
};
/// Compare to sort according to log value
bool byLogValue(const CurveSpec &lhs, const CurveSpec &rhs);
/// Returns a single log value from the given workspace
double getSingleWorkspaceLogValue(
size_t wsIndex, const Mantid::API::MatrixWorkspace_const_sptr &matrixWS,
const QString &logName);
/// Returns a single custom log value
double getSingleWorkspaceLogValue(size_t wsIndex,
const std::set<double> &logValues);
#endif // MANTIDPLOTUTILITIES_H_
#include "MantidGroupPlotGenerator.h"
#include "MantidSurfaceContourPlotGenerator.h"
#include "MantidAPI/MatrixWorkspace.h"
#include "MantidAPI/Run.h"
#include "MantidAPI/WorkspaceFactory.h"
#include "MantidAPI/WorkspaceGroup.h"
#include "MantidPlotUtilities.h"
#include "MantidGeometry/MDGeometry/IMDDimension.h"
#include <MantidQtMantidWidgets/MantidDisplayBase.h>
......@@ -20,75 +21,103 @@ using Mantid::HistogramData::Histogram;
* Constructor
* @param mantidUI :: [input] Pointer to the Mantid UI
*/
MantidGroupPlotGenerator::MantidGroupPlotGenerator(MantidDisplayBase *mantidUI)
MantidSurfaceContourPlotGenerator::MantidSurfaceContourPlotGenerator(
MantidDisplayBase *mantidUI)
: m_mantidUI(mantidUI) {}
/**
* Plots a surface graph from the given workspace group
* @param wsGroup :: [input] Workspace group to plot
* @param options :: [input] User-selected plot options
* @param accepted :: [input] true if plot has been accepted
* @param plotIndex :: [input] plot index
* @param axisName :: [input] axis name
* @param logName :: [input] log name
* @param customLogValues :: [input] custom log values
* @param workspaces :: [input] set of workspaces forming the group to be
* plotted
*/
void MantidGroupPlotGenerator::plotSurface(
const WorkspaceGroup_const_sptr &wsGroup,
const MantidSurfacePlotDialog::UserInputSurface &options) const {
plot(Type::Surface, wsGroup, options);
void MantidSurfaceContourPlotGenerator::plotSurface(
bool accepted, int plotIndex, const QString &axisName,
const QString &logName, const std::set<double> &customLogValues,
const std::vector<Mantid::API::MatrixWorkspace_const_sptr> &workspaces)
const {
plot(Type::Surface, accepted, plotIndex, axisName, logName, customLogValues,
workspaces);
}
/**
* Plots a contour plot from the given workspace group
* @param wsGroup :: [input] Workspace group to plot
* @param options :: [input] User-selected plot options
* @param accepted :: [input] true if plot has been accepted
* @param plotIndex :: [input] plot index
* @param axisName :: [input] axis name
* @param logName :: [input] log name
* @param customLogValues :: [input] custom log values
* @param workspaces :: [input] set of workspaces forming the group to be
* plotted
*/
void MantidGroupPlotGenerator::plotContour(
const WorkspaceGroup_const_sptr &wsGroup,
const MantidSurfacePlotDialog::UserInputSurface &options) const {
plot(Type::Contour, wsGroup, options);
void MantidSurfaceContourPlotGenerator::plotContour(
bool accepted, int plotIndex, const QString &axisName,
const QString &logName, const std::set<double> &customLogValues,
const std::vector<Mantid::API::MatrixWorkspace_const_sptr> &workspaces)
const {
plot(Type::Contour, accepted, plotIndex, axisName, logName, customLogValues,
workspaces);
}
/**
* Plots a graph from the given workspace group
* Plots a contour or surface graph from the given workspace group
* @param graphType :: [input] Type of graph to plot
* @param wsGroup :: [input] Workspace group to plot
* @param options :: [input] User-selected plot options
* @param accepted :: [input] true if plot has been accepted
* @param plotIndex :: [input] plot index
* @param axisName :: [input] axis name
* @param logName :: [input] log name
* @param customLogValues :: [input] custom log values
* @param workspaces :: [input] set of workspaces forming the group to be
* plotted
*/
void MantidGroupPlotGenerator::plot(
Type graphType, const WorkspaceGroup_const_sptr &wsGroup,
const MantidSurfacePlotDialog::UserInputSurface &options) const {
if (wsGroup && options.accepted) {
void MantidSurfaceContourPlotGenerator::plot(
Type graphType, bool accepted, int plotIndex, const QString &axisName,
const QString &logName, const std::set<double> &customLogValues,
const std::vector<Mantid::API::MatrixWorkspace_const_sptr> &workspaces)
const {
if (!workspaces.empty() && accepted) {
// Set up one new matrix workspace to hold all the data for plotting
MatrixWorkspace_sptr matrixWS;
try {
matrixWS = createWorkspaceForGroupPlot(graphType, wsGroup, options);
matrixWS = createWorkspaceForGroupPlot(graphType, workspaces, plotIndex,
logName, customLogValues);
} catch (const std::logic_error &err) {
m_mantidUI->showCritical(err.what());
return;
}
} // We can now assume every workspace is a Matrix Workspace
// Generate X axis title
const auto &xLabelQ = getXAxisTitle(wsGroup);
const auto &xLabelQ = getXAxisTitle(workspaces);
// Import the data for plotting
auto matrixToPlot =
m_mantidUI->importMatrixWorkspace(matrixWS, -1, -1, false);
// Change the default plot title
QString title = QString("plot for %1, spectrum %2")
.arg(wsGroup->getName().c_str(),
QString::number(options.plotIndex));
QString title =
QString("plot for %1, spectrum %2")
.arg(workspaces[0]->getName().c_str(), QString::number(plotIndex));
// For the time being we use the name of the first workspace.
// Later we need a way of conveying a name for this set of workspaces.
// Plot the correct type of graph
if (graphType == Type::Surface) {
auto plot = matrixToPlot->plotGraph3D(Qwt3D::PLOTSTYLE::FILLED);
plot->setTitle(QString("Surface ").append(title));
plot->setXAxisLabel(xLabelQ);
plot->setYAxisLabel(options.axisName);
plot->setYAxisLabel(axisName);
plot->setResolution(1); // If auto-set too high, appears empty
} else if (graphType == Type::Contour) {
MultiLayer *plot =
matrixToPlot->plotGraph2D(GraphOptions::ColorMapContour);
plot->activeGraph()->setXAxisTitle(xLabelQ);
plot->activeGraph()->setYAxisTitle(options.axisName);
plot->activeGraph()->setTitle(QString("Contour ").append(title));
plot->activeGraph()->setXAxisTitle(xLabelQ);
plot->activeGraph()->setYAxisTitle(axisName);
}
}
}
......@@ -101,31 +130,32 @@ void MantidGroupPlotGenerator::plot(
* Table or Peaks workspaces then it cannot be used.
*
* @param graphType :: [input] Type of graph to plot
* @param wsGroup :: [input] Pointer to workspace group to use as input
* @param options :: [input] User input from dialog
* @param workspaces :: [input] set of workspaces forming the group to be
*plotted
* @param plotIndex :: [input] plot index
* @param logName :: [input] log name
* @param customLogValues :: [input] custom log
* @returns Pointer to the created workspace
*/
const MatrixWorkspace_sptr
MantidGroupPlotGenerator::createWorkspaceForGroupPlot(
Type graphType, WorkspaceGroup_const_sptr wsGroup,
const MantidSurfacePlotDialog::UserInputSurface &options) const {
const auto index = static_cast<size_t>(
options.plotIndex); // which spectrum to plot from each WS
const auto &logName = options.logName; // Log to read for axis of XYZ plot
MantidSurfaceContourPlotGenerator::createWorkspaceForGroupPlot(
Type graphType,
const std::vector<Mantid::API::MatrixWorkspace_const_sptr> &workspaces,
int plotIndex, const QString &logName,
const std::set<double> &customLogValues) const {
const auto index =
static_cast<size_t>(plotIndex); // which spectrum to plot from each WS
validateWorkspaceChoices(wsGroup, index);
validateWorkspaceChoices(workspaces, index);
// Create workspace to hold the data
// Each "spectrum" will be the data from one workspace
const auto nWorkspaces = wsGroup->getNumberOfEntries();
if (nWorkspaces < 0) {
return MatrixWorkspace_sptr();
}
const auto nWorkspaces = workspaces.size();
MatrixWorkspace_sptr matrixWS; // Workspace to return
// Cast succeeds: have already checked group contains only MatrixWorkspaces
const auto firstWS =
boost::dynamic_pointer_cast<const MatrixWorkspace>(wsGroup->getItem(0));
boost::dynamic_pointer_cast<const MatrixWorkspace>(workspaces[0]);
// If we are making a surface plot, create a point data workspace.
// If it's a contour plot, make a histo workspace.
......@@ -139,9 +169,9 @@ MantidGroupPlotGenerator::createWorkspaceForGroupPlot(
// For each workspace in group, add data and log values
std::vector<double> logValues;
for (int i = 0; i < nWorkspaces; i++) {
for (size_t i = 0; i < nWorkspaces; i++) {
const auto ws =
boost::dynamic_pointer_cast<const MatrixWorkspace>(wsGroup->getItem(i));
boost::dynamic_pointer_cast<const MatrixWorkspace>(workspaces[i]);
if (ws) {
// Make sure the X data is set as the correct mode
if (xMode == Histogram::XMode::BinEdges) {
......@@ -152,8 +182,8 @@ MantidGroupPlotGenerator::createWorkspaceForGroupPlot(
// Y and E can be shared
matrixWS->setSharedY(i, ws->sharedY(index));
matrixWS->setSharedE(i, ws->sharedE(index));
if (logName == MantidSurfacePlotDialog::CUSTOM) {
logValues.push_back(getSingleLogValue(i, options.customLogValues));
if (logName == MantidWSIndexWidget::CUSTOM) {
logValues.push_back(getSingleLogValue(i, customLogValues));
} else {
logValues.push_back(getSingleLogValue(i, ws, logName));
}
......@@ -166,33 +196,6 @@ MantidGroupPlotGenerator::createWorkspaceForGroupPlot(
return matrixWS;
}
/**
* Check if the supplied group contains only MatrixWorkspaces
* @param wsGroup :: [input] Pointer to a WorkspaceGroup
* @returns True if contains only MatrixWorkspaces, false if contains
* other types or is empty
*/
bool MantidGroupPlotGenerator::groupIsAllMatrixWorkspaces(
const WorkspaceGroup_const_sptr &wsGroup) {
bool allMatrixWSes = true;
if (wsGroup) {
if (wsGroup->isEmpty()) {
allMatrixWSes = false;
} else {
for (int index = 0; index < wsGroup->getNumberOfEntries(); index++) {
if (nullptr == boost::dynamic_pointer_cast<MatrixWorkspace>(
wsGroup->getItem(index))) {
allMatrixWSes = false;
break;
}
}
}
} else {
allMatrixWSes = false;
}
return allMatrixWSes;
}
/**
* Gets the custom, user-provided log value of the given index.
* i.e. the nth in order from smallest to largest.
......@@ -203,15 +206,9 @@ bool MantidGroupPlotGenerator::groupIsAllMatrixWorkspaces(
* @param logValues :: [input] User-provided set of log values
* @returns Numeric log value
*/
double MantidGroupPlotGenerator::getSingleLogValue(
int wsIndex, const std::set<double> &logValues) const {
double value = 0;
if (wsIndex < static_cast<int>(logValues.size())) {
auto it = logValues.begin();
std::advance(it, wsIndex);
value = *it;
}
return value;
double MantidSurfaceContourPlotGenerator::getSingleLogValue(
size_t wsIndex, const std::set<double> &logValues) const {
return getSingleWorkspaceLogValue(wsIndex, logValues);
}
/**
......@@ -223,30 +220,11 @@ double MantidGroupPlotGenerator::getSingleLogValue(
* @returns log value as a double, or workspace index
* @throws invalid_argument if log is wrong type or not present
*/
double MantidGroupPlotGenerator::getSingleLogValue(
int wsIndex, const Mantid::API::MatrixWorkspace_const_sptr &matrixWS,
double MantidSurfaceContourPlotGenerator::getSingleLogValue(
size_t wsIndex, const Mantid::API::MatrixWorkspace_const_sptr &matrixWS,
const QString &logName) const {
if (logName == MantidSurfacePlotDialog::WORKSPACE_INDEX) {
return wsIndex;
} else {
// MatrixWorkspace is an ExperimentInfo
if (auto ei = boost::dynamic_pointer_cast<const ExperimentInfo>(matrixWS)) {
auto log = ei->run().getLogData(logName.toStdString());
if (log) {
if (dynamic_cast<Mantid::Kernel::PropertyWithValue<int> *>(log) ||
dynamic_cast<Mantid::Kernel::PropertyWithValue<double> *>(log)) {
return std::stod(log->value());
} else {
throw std::invalid_argument(
"Log is of wrong type (expected single numeric value");
}
} else {
throw std::invalid_argument("Log not present in workspace");
}
} else {
throw std::invalid_argument("Bad input workspace type");
}
}
return getSingleWorkspaceLogValue(wsIndex, matrixWS, logName);