Plot subplots alignment issues
Reported when testing PR #17830
#### Expected behaviour

Plots should look good, axes and legends should all be aligned correctly.
#### Actual behaviour

Some misalignment seen - see [comment on PR](https://github.com/mantidproject/mantid/pull/17830#issuecomment-255061240).
This particularly affects the legends - perhaps fix legend location in each plot, or autoscale after adding axis labels/legends rather than before. (See [here](https://github.com/mantidproject/mantid/blob/a6c8ef429ab02ba09108410ef058b94e62a046f0/MantidPlot/src/Mantid/MantidUI.cpp#L3545-L3546)).
#### Steps to reproduce the behaviour

Use `plotSubplots` command to plot several layers in a multilayer. The legends may not line up properly.
The bug often appears when using the `window` argument to plot over an existing graph - then the legends can be totally misplaced.

diff --git a/MantidPlot/pymantidplot/__init__.py b/MantidPlot/pymantidplot/__init__.py
index b1afecb6770..53c0133a6f5 100644
--- a/MantidPlot/pymantidplot/__init__.py
+++ b/MantidPlot/pymantidplot/__init__.py
@@ -1224,3 +1224,59 @@ def createDetectorTable(source):
         return new_proxy(proxies.MDIWindow, _qti.app.mantidUI.createDetectorTable, workspace_names[0])
 
 # -----------------------------------------------------------------------------
+def plotSubplots(source, indices, distribution = mantidqtpython.MantidQt.DistributionDefault, error_bars=False, window=None):
+    """Open a tiled plot.
+
+    This plots one or more spectra, with X as the bin boundaries,
+    and Y as the counts in each bin.
+    
+    If one workspace, each spectrum gets its own tile.
+    Otherwise, each workspace gets its own tile.
+
+    Args:
+        source: list of workspace names
+        indices: workspace index, or tuple or list of workspace indices to plot
+        distribution: whether or not to plot as a distribution
+        error_bars: bool, set to True to add error bars.
+        window: window used for plotting. If None a new one will be created
+    Returns:
+        A handle to window if one was specified, otherwise a handle to the created one. None in case of error.
+    """
+
+    workspace_names = getWorkspaceNames(source)
+   
+    # Deal with workspace groups that may contain various types:
+    # Only want to plot MatrixWorkspaces
+    to_plot = []
+    for name in workspace_names:
+        if isinstance(mantid.api.mtd[name], mantid.api.MatrixWorkspace):
+            to_plot.append(name)
+            
+    __checkPlotWorkspaces(to_plot)
+    # check spectrum indices
+    index_list = __getWorkspaceIndices(indices)
+    if len(index_list) == 0:
+        raise ValueError("No spectrum indices given")
+    for idx in index_list:
+        if idx < 0:
+            raise ValueError("Wrong spectrum index (<0): %d" % idx)
+    for name in to_plot:
+        max_spec = workspace(name).getNumberHistograms() - 1
+        for idx in index_list:
+            if idx > max_spec:
+                raise ValueError("Wrong spectrum index for workspace '%s': %d, which is bigger than the"
+                                 " number of spectra in this workspace - 1 (%d)" % (name, idx, max_spec))
+    
+    # Unwrap the window object, if any specified
+    if window != None:
+        window = window._getHeldObject()
+    
+    graph = proxies.Graph(threadsafe_call(_qti.app.mantidUI.plotSubplots,
+                                          to_plot, index_list, distribution, error_bars, window))
+    if graph._getHeldObject() == None:
+        raise RuntimeError("Cannot create graph, see log for details.")
+    else:
+        return graph
+
+
+# ----------------------------------------------------------------------------------------------------
diff --git a/MantidPlot/src/Mantid/MantidUI.cpp b/MantidPlot/src/Mantid/MantidUI.cpp
index 700b7c31740..1e8ba6c0293 100644
--- a/MantidPlot/src/Mantid/MantidUI.cpp
+++ b/MantidPlot/src/Mantid/MantidUI.cpp
@@ -3410,11 +3410,12 @@ MultiLayer *MantidUI::plotSelectedColumns(const MantidMatrix *const m,
  * @param toPlot :: Map of form ws -> [spectra_list]
  * @param distr :: if true, workspace plot as distribution (y data/bin width)
  * @param errs :: if true, plot the errors on the graph
+ * @param plotWindow :: Window to plot in - if null, create a new one
  * @return created MultiLayer, or null on failure
  */
 MultiLayer *MantidUI::plotSubplots(const QMultiMap<QString, set<int>> &toPlot,
-                                   MantidQt::DistributionFlag distr,
-                                   bool errs) {
+                                   MantidQt::DistributionFlag distr, bool errs,
+                                   MultiLayer *plotWindow) {
   // Check if nothing to plot
   if (toPlot.size() == 0)
     return nullptr;
@@ -3456,8 +3457,16 @@ MultiLayer *MantidUI::plotSubplots(const QMultiMap<QString, set<int>> &toPlot,
   // Set the wait cursor while we are plotting
   ScopedOverrideCursor waitCursor;
 
-  // Create window with correct number of layers
-  auto *multi = appWindow()->multilayerPlot(plotTitle, nSubplots, 1, nSubplots);
+  // Create window with correct number of layers, or use existing
+  MultiLayer *multi;
+  if (plotWindow) {
+    multi = plotWindow;
+    multi->setLayersNumber(0); // remove any existing plots
+    multi->setLayersNumber(nSubplots);
+  } else {
+    multi = appWindow()->multilayerPlot(plotTitle, nSubplots, 1, nSubplots);
+  }
+  assert(multi);
   multi->setCloseOnEmpty(true);
   multi->arrangeLayers(true, true);
 
@@ -3555,11 +3564,12 @@ void MantidUI::plotLayerOfMultilayer(MultiLayer *multi, const bool plotErrors,
  * @param toPlot :: A list of workspace/spectra to be shown in the graph
  * @param distr :: if true, workspace plot as distribution (y data/bin width)
  * @param errs :: if true, plot the errors on the graph
+ * @param plotWindow :: Window to plot in - if null, create a new one
  * @return created MultiLayer, or null on failure
  */
 MultiLayer *MantidUI::plotSubplots(const QMultiMap<QString, int> &toPlot,
-                                   MantidQt::DistributionFlag distr,
-                                   bool errs) {
+                                   MantidQt::DistributionFlag distr, bool errs,
+                                   MultiLayer *plotWindow) {
 
   // Convert the input map into a map of workspace->spectra
   QMultiMap<QString, std::set<int>> spectraByWorkspace;
@@ -3573,7 +3583,43 @@ MultiLayer *MantidUI::plotSubplots(const QMultiMap<QString, int> &toPlot,
   }
 
   // Pass over to the overloaded method
-  return plotSubplots(spectraByWorkspace, distr, errs);
+  return plotSubplots(spectraByWorkspace, distr, errs, plotWindow);
+}
+
+/**
+ * Plot a "tiled" plot (with subplots).
+ * Ask user for confirmation if lots of plots are chosen.
+ * If just one workspace, put each spectrum in its own subplot
+ * If multiple workspaces, each ws gets its own subplot
+ *
+ * This overload plots the same spectra for each workspace.
+ *
+ * @param wsNames :: A list of workspace names to be shown in the graph
+ * @param indexList :: list of workspace indices
+ * @param distr :: if true, workspace plot as distribution (y data/bin width)
+ * @param errs :: if true, plot the errors on the graph
+ * @param plotWindow :: Window to plot in - if null, create a new one
+ * @return created MultiLayer, or null on failure
+ */
+MultiLayer *MantidUI::plotSubplots(const QStringList &wsNames,
+                                   const QList<int> &indexList,
+                                   MantidQt::DistributionFlag distr, bool errs,
+                                   MultiLayer *plotWindow) {
+  // convert input into map of workspace->spectra
+  QMultiMap<QString, std::set<int>> spectraByWorkspace;
+  const std::set<int> wsIndices = [&indexList]() {
+    std::set<int> indexSet;
+    for (const auto &index : indexList) {
+      indexSet.insert(index);
+    }
+    return indexSet;
+  }();
+  for (const auto &wsName : wsNames) {
+    spectraByWorkspace.insert(wsName, wsIndices);
+  }
+
+  // Pass to the overloaded method
+  return plotSubplots(spectraByWorkspace, distr, errs, plotWindow);
 }
 
 Table *MantidUI::createTableFromBins(
diff --git a/MantidPlot/src/Mantid/MantidUI.h b/MantidPlot/src/Mantid/MantidUI.h
index bfbbf44ee5d..c7f642c7dc8 100644
--- a/MantidPlot/src/Mantid/MantidUI.h
+++ b/MantidPlot/src/Mantid/MantidUI.h
@@ -223,12 +223,17 @@ public:
   MultiLayer *
   plotSubplots(const QMultiMap<QString, std::set<int>> &toPlot,
                MantidQt::DistributionFlag distr = MantidQt::DistributionDefault,
-               bool errs = false);
+               bool errs = false, MultiLayer *plotWindow = nullptr);
 
   MultiLayer *
   plotSubplots(const QMultiMap<QString, int> &toPlot,
                MantidQt::DistributionFlag distr = MantidQt::DistributionDefault,
-               bool errs = false);
+               bool errs = false, MultiLayer *plotWindow = nullptr);
+
+  MultiLayer *
+  plotSubplots(const QStringList &wsNames, const QList<int> &indexList,
+               MantidQt::DistributionFlag distr = MantidQt::DistributionDefault,
+               bool errs = false, MultiLayer *plotWindow = nullptr);
 
 public slots:
   // Create a 1d graph form specified MatrixWorkspace and index
diff --git a/MantidPlot/src/qti.sip b/MantidPlot/src/qti.sip
index 5d42359e132..97803375bfa 100644
--- a/MantidPlot/src/qti.sip
+++ b/MantidPlot/src/qti.sip
@@ -713,7 +713,7 @@ public:
 
   bool insertCurve(Table*, const QString&, int style = 1, int startRow = 0, int endRow = -1);
   bool insertCurve(Table*, const QString&, const QString&, int style = 1, int startRow = 0, int endRow = -1);
-  bool insertCurve(QString, int, bool=false, Graph::CurveType style = Graph::Unspecified);
+  bool insertCurve(QString, int, bool=false, Graph::CurveType style = Graph::Unspecified, bool=false);
   void insertCurve(Graph*, int);
   bool addCurves(Table*, SIP_PYTUPLE, int=0, double=1, int=3, int=0, int=-1);
 %MethodCode
@@ -915,6 +915,7 @@ public:
   Graph* layer(int num);
   Graph* addLayer(int = 0, int = 0, int = 0, int = 0);
   void insertCurve(MultiLayer* ml, int i);
+  void setCommonAxisScales();
   void setCols(int);
   void setRows(int);
   void setName(const QString&);
@@ -1486,6 +1487,10 @@ public:
   MultiLayer* plotMDList(const QList<QString>& wsNames, const int plotAxis,
     const Mantid::API::MDNormalization normalization, const bool showErrors, MultiLayer* window = NULL,
     bool clearWindow = false);
+    
+  MultiLayer *plotSubplots(const QList<QString>& wsNames, const QList<int> &indexList, 
+                           MantidQt::DistributionFlag distr = MantidQt::DistributionDefault, 
+                           bool errs = false, MultiLayer *plotWindow = NULL);
 
   MultiLayer* mergePlots(MultiLayer*, MultiLayer*);
   void convertToWaterfall(MultiLayer*);
diff --git a/docs/source/images/multilayer_3.9.png b/docs/source/images/multilayer_3.9.png
new file mode 100644
index 00000000000..fbab36c8344
Binary files /dev/null and b/docs/source/images/multilayer_3.9.png differ
diff --git a/docs/source/release/v3.9.0/ui.rst b/docs/source/release/v3.9.0/ui.rst
index 55bb52f5082..b325d9c1958 100644
--- a/docs/source/release/v3.9.0/ui.rst
+++ b/docs/source/release/v3.9.0/ui.rst
@@ -26,6 +26,14 @@ Instrument View
 Plotting Improvements
 #####################
 - Fixed a bug where left and right Y axes went out of sync when a fit was run.
+- Exposed the ``plotSubplots`` command to Python. This creates a tiled (multilayer) plot with one workspace per tile.
+
+.. figure:: ../../images/multilayer_3.9.png
+   :class: screenshot
+   :width: 550px
+   :align: right
+
+   plotSubplots image
 
 Algorithm Toolbox
 #################
['MantidPlot/src/Mantid/MantidUI.cpp', 'MantidPlot/src/Mantid/MantidUI.h', 'MantidPlot/pymantidplot/__init__.py', 'MantidPlot/src/qti.sip', 'docs/source/release/v3.9.0/ui.rst']