diff --git a/Code/Mantid/MantidPlot/CMakeLists.txt b/Code/Mantid/MantidPlot/CMakeLists.txt
index be38f40c868e..cbafb0d76704 100644
--- a/Code/Mantid/MantidPlot/CMakeLists.txt
+++ b/Code/Mantid/MantidPlot/CMakeLists.txt
@@ -855,20 +855,13 @@ copy_files_to_dir ( "${PY_FILES}"
set( MTDPLOTPY_FILES
__init__.py
proxies.py
+ pyplot.py
+ qtiplot.py
)
copy_files_to_dir ( "${MTDPLOTPY_FILES}"
${CMAKE_CURRENT_SOURCE_DIR}/pymantidplot
${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/pymantidplot
MTDPLOT_INSTALL_FILES )
-
-set( FUTURE_FILES
- __init__.py
- pyplot.py
-)
-copy_files_to_dir ( "${FUTURE_FILES}"
- ${CMAKE_CURRENT_SOURCE_DIR}/pymantidplot/future
- ${CMAKE_LIBRARY_OUTPUT_DIRECTORY}/${CMAKE_CFG_INTDIR}/pymantidplot/future
- MTDPLOT_FUTURE_INSTALL_FILES )
# IPython scripts
set( IPY_FILES
@@ -887,7 +880,7 @@ copy_files_to_dir ( "${IPY_FILES}"
add_executable ( MantidPlot ${WIN_CONSOLE} MACOSX_BUNDLE ${ALL_SRC} src/main.cpp
${INC_FILES} ${QTIPLOT_C_SRC} ${UI_HDRS}
${RES_FILES} ${MANTID_RC_FILE}
- ${PYTHON_INSTALL_FILES} ${MTDPLOT_INSTALL_FILES} ${CONFIG_RESET_SCRIPT_FILE} ${MTDPLOT_FUTURE_INSTALL_FILES} ${IPYTHON_INSTALL_FILES}
+ ${PYTHON_INSTALL_FILES} ${MTDPLOT_INSTALL_FILES} ${CONFIG_RESET_SCRIPT_FILE} ${IPYTHON_INSTALL_FILES}
)
# Library dependencies
@@ -963,7 +956,7 @@ set ( MANTIDPLOT_TEST_PY_FILES
MantidPlotMdiSubWindowTest.py
MantidPlotTiledWindowTest.py
MantidPlotInputArgsCheck.py
- MantidPlotFuturePyplotGeneralTest.py
+ MantidPlotPyplotGeneralTest.py
)
if ( 0 )
@@ -1029,9 +1022,6 @@ install ( FILES ${PY_FILES} DESTINATION ${BIN_DIR} )
foreach(PY_FILE ${MTDPLOTPY_FILES} )
install ( FILES pymantidplot/${PY_FILE} DESTINATION ${BIN_DIR}/pymantidplot )
endforeach()
-foreach(FUT_PY_FILE ${FUTURE_FILES} )
- install ( FILES pymantidplot/future/${FUT_PY_FILE} DESTINATION ${BIN_DIR}/pymantidplot/future )
-endforeach()
foreach(PY_FILE ${IPY_FILES} )
install ( FILES ipython_widget/${PY_FILE} DESTINATION ${BIN_DIR}/ipython_widget )
endforeach()
diff --git a/Code/Mantid/MantidPlot/mantidplot.py b/Code/Mantid/MantidPlot/mantidplot.py
index e710b1a0e074..ee4980b5b44c 100644
--- a/Code/Mantid/MantidPlot/mantidplot.py
+++ b/Code/Mantid/MantidPlot/mantidplot.py
@@ -10,3 +10,15 @@
from pymantidplot import *
except ImportError:
raise ImportError('Could not import mantidplot (when trying to import pymantidplot). Something is broken in this installation, please check.')
+
+try:
+ # import pyplot and also bring it into the standard MantidPlot namespace
+ import pymantidplot.pyplot
+ from pymantidplot.pyplot import *
+except ImportError:
+ raise ImportError('Could not import pymantidplot.pyplot. Something is broken in this installation, please check.')
+
+try:
+ import pymantidplot.qtiplot
+except ImportError:
+ raise ImportError('Could not import pymantidplot.qtiplot. Something is broken in this installation, please check.')
diff --git a/Code/Mantid/MantidPlot/pymantidplot/__init__.py b/Code/Mantid/MantidPlot/pymantidplot/__init__.py
index f08c88a65037..d583f5491c59 100644
--- a/Code/Mantid/MantidPlot/pymantidplot/__init__.py
+++ b/Code/Mantid/MantidPlot/pymantidplot/__init__.py
@@ -183,23 +183,6 @@ def newTiledWindow(name=None):
else:
return new_proxy(proxies.TiledWindowProxy, _qti.app.newTiledWindow, name)
-#-----------------------------------------------------------------------------
-# Intercept qtiplot "plot" command and forward to plotSpectrum for a workspace
-def plot(source, *args, **kwargs):
- """Create a new plot given a workspace, table or matrix.
-
- Args:
- source: what to plot; if it is a Workspace, will
- call plotSpectrum()
-
- Returns:
- A handle to the created Graph widget.
- """
- if hasattr(source, '_getHeldObject') and isinstance(source._getHeldObject(), QtCore.QObject):
- return new_proxy(proxies.Graph,_qti.app.plot, source._getHeldObject(), *args, **kwargs)
- else:
- return plotSpectrum(source, *args, **kwargs)
-
#----------------------------------------------------------------------------------------------------
def plotSpectrum(source, indices, error_bars = False, type = -1, window = None, clearWindow = False):
"""Open a 1D Plot of a spectrum in a workspace.
diff --git a/Code/Mantid/MantidPlot/pymantidplot/pyplot.py b/Code/Mantid/MantidPlot/pymantidplot/pyplot.py
new file mode 100644
index 000000000000..3e98e3858f4c
--- /dev/null
+++ b/Code/Mantid/MantidPlot/pymantidplot/pyplot.py
@@ -0,0 +1,1527 @@
+"""============================================================================
+New Python command line interface for plotting in Mantid (a la matplotlib)
+============================================================================
+
+The idea behind this new module is to provide a simpler, more
+homogeneous command line interface (CLI) to the Mantid plotting
+functionality. This new interface is meant to resemble matplotlib as
+far as possible, and to provide a more manageable, limited number of
+plot options.
+
+The module is at a very early stage of development and provides
+limited functionality. This is very much work in progress at the
+moment. The module is subject to changes and feedback is very much
+welcome!
+
+Simple plots can be created and manipulated with a handul of
+commands. See the following examples.
+
+Plot an array (python list)
+---------------------------
+
+.. code-block:: python
+
+ # plot array
+ plot([0.1, 0.3, 0.2, 4])
+ # plot x-y
+ plot([0.1, 0.2, 0.3, 0.4], [1.2, 1.3, 0.2, 0.8])
+
+Plot an array with a different style
+------------------------------------
+
+The plot commands that are described here accept a list of options
+(kwargs) as parameters passed by name. With these options you can
+modify plot properties, such as line styles, colors, axis scale,
+etc. The following example illustrates the use of a few options. You
+can refer to the list of options provided further down in this
+document. In principle, any combination of options is supported, as
+long as it makes sense!
+
+.. code-block:: python
+
+ a = [0.1, 0.3, 0.2, 4]
+ plot(a)
+ import numpy as np
+ y = np.sin(np.linspace(-2.28, 2.28, 1000))
+ plot(y, linestyle='-.', marker='o', color='red')
+
+If you have used the traditional Mantid command line interface in
+Python you will probably remember the plotSpectrum, plotBin and plotMD
+functions. These are supported in this new interface as shown in the
+following examples.
+
+Plot a Mantid workspace
+-----------------------
+
+You can pass one or more workspaces to the plot function. By default
+it will plot the spectra of the workspace(s), selecting them by the
+indices specified in the second argument. This behavior is similar to
+he plotSpectrum function of the traditional mantidplot module. This is
+a simple example that produces plots of spectra:
+
+.. code-block:: python
+
+ # first, load a workspace. You can do this with a Load command or just from the GUI menus
+ ws = Load("/path/to/MAR11060.raw", OutputWorkspace="foo")
+ # 1 spectrum plot
+ plot(ws, 100)
+ # 3 spectra plot
+ plot(ws, [100, 101, 102])
+
+Plot spectra using workspace objects and workspace names
+--------------------------------------------------------
+
+It is also possible to pass workspace names to plot, as in the
+following example:
+
+.. code-block:: python
+
+ # please make sure that you use the right path and file name
+ mar = Load('/path/to/MAR11060.raw', OutputWorkspace="MAR11060")
+ plot('MAR11060', [10,100,500])
+ plot(mar,[3, 500, 800])
+
+Let's load one more workspace so we can see some examples with list of
+workspaces
+
+.. code-block:: python
+
+ loq=Load('/path/to/LOQ48097.raw', OutputWorkspace="LOQ48097")
+
+The next lines are all equivalent, you can use workspace objects or
+names in the list passed to plot:
+
+.. code-block:: python
+
+ plot([mar, 'LOQ48097'], [800, 900])
+ plot([mar, loq], [800, 900])
+ plot(['MAR11060', loq], [800, 900])
+
+Here, the plot function is making a guess and plotting the spectra of
+these workspaces (instead of the bins or anything else). You can make
+that choice more explicit by specifying the 'tool' argument:
+
+.. code-block:: python
+
+ plot(['MAR11060', loq], [800, 900], tool='plot_spectrum')
+
+Alternatively, you can use the plot_spectrum command, which is
+equivalent to the plot command with the keyword argument
+tool='plot_spectrum':
+
+.. code-block:: python
+
+ plot_spectrum(['MAR11060', loq], [800, 900])
+
+Plotting bins
+-------------
+
+To plot workspace bins you can use the keyword 'tool' with the value
+'plot_bin', like this:
+
+.. code-block:: python
+
+ ws = Load('/path/to/HRP39182.RAW', OutputWorkspace="HRP39182")
+ plot(ws, [1, 5, 7, 100], tool='plot_bin')
+
+or, alternatively, you can use the plot_bin command:
+
+.. code-block:: python
+
+ plot_bin(ws, [1, 5, 7, 100], linewidth=4, linestyle=':')
+
+Plotting MD workspaces
+----------------------
+
+Similarly, to plot MD workspaces you can use the keyword 'tool' with
+the value 'plot_md', like this:
+
+.. code-block:: python
+
+ simple_md_ws = CreateMDWorkspace(Dimensions='3',Extents='0,10,0,10,0,10',Names='x,y,z',Units='m,m,m',SplitInto='5',MaxRecursionDepth='20',OutputWorkspace=MDWWorkspaceName)
+ plot(simple_md_ws, tool='plot_md')
+
+or a specific plot_md command:
+
+.. code-block:: python
+
+ plot_md(simple_md_wsws)
+
+For simplicity, these examples use a dummy MD workspace. Please refer
+to the Mantid (http://www.mantidproject.org/MBC_MDWorkspaces) for a
+more real example, which necessarily gets more complicated and data
+intensive.
+
+Changing style properties
+-------------------------
+
+You can modify the style of your plots. For example like this (for a
+full list of options currently supported, see below).
+
+.. code-block:: python
+
+ lines = plot(loq, [100, 104], tool='plot_spectrum', linestyle='-.', marker='*', color='red')
+
+Notice that the plot function returns a list of lines, which
+correspond to the spectra lines. At present the lines have limited
+functionality. Essentially, the data underlying these lines can be
+retrieved as follows:
+
+.. code-block:: python
+
+ lines[0].get_xdata()
+ lines[0].get_ydata()
+
+If you use plot_spectrum, the number of elements in the output lines
+should be equal to the number of bins in the corresponding
+workspace. Conversely, if you use plot_bin, the number of elements in
+the output lines should be equal to the number of spectra in the
+workspace.
+
+To modify the figure, you first need to obtain the figure object
+that represents the figure where the lines are displayed. Once you do
+so you can for example set the title of the figure like this:
+
+.. code-block:: python
+
+ fig = lines[0].figure()
+ fig.suptitle('Example figure title')
+
+Other properties can be modified using different functions, as in
+matplotlib's pyplot. For example:
+
+.. code-block:: python
+
+ title('Test plot of LOQ')
+ xlabel('ToF')
+ ylabel('Counts')
+ ylim(0, 8)
+ xlim(1e3, 4e4)
+ xscale('log')
+ grid('on')
+
+By default, these functions manipulate the current figure (the last or
+most recently shown figure). You can also save the current figure into
+a file like this:
+
+.. code-block:: python
+
+ savefig('example_saved_figure.png')
+
+where the file format is guessed from the file extension. The same
+extensions as in the MantidPlot figure export dialog are supported,
+including jpg, png, tif, ps, and svg.
+
+The usage of these functions very similar to the matlab and/or
+pyplot functions with the same names. The list of functions
+currently supported is provided further below.
+
+Additional options supported as keyword arguments (kwargs):
+-----------------------------------------------------------
+
+There is a couple of important plot options that are set as keyword
+arguments:
+
+
++------------+------------------------+
+|Option name | Values supported |
++============+========================+
+|error_bars | True, False (default) |
++------------+------------------------+
+|hold | on, off |
++------------+------------------------+
+
+error_bars has the same meaning as in the traditional mantidplot plot
+functions: it defines whether error bars should be added to the
+plots. hold has the same behavior as in matplotlib and pyplot. If the
+value of hold is 'on' in a plot command, the new plot will be drawn on
+top of the current plot window, without clearing it. This makes it
+possible to make plots incrementally.
+
+For example, one can add two spectra from a workspace using the
+following command:
+
+.. code-block:: python
+
+ lines = plot(loq, [100, 102], linestyle='-.', color='red')
+
+But similar results can be obtained by plotting one of the spectra by
+a first command, and then plotting the second spectra in a subsequent
+command with the hold parameter enabled:
+
+.. code-block:: python
+
+ lines = plot(loq, 100, linestyle='-.', color='red')
+ lines = plot(loq, 102, linestyle='-.', color='blue', hold='on')
+
+After the two commands above, any subsequent plot command that passes
+hold='on' as a parameter would add new spectra into the same plot. An
+alternative way of doing this is explained next. Note however that
+using the hold property to combine different types of plots
+(plot_spectrum, plot_bin, etc.) will most likely produce useless
+results.
+
+Multi-plot commands
+-------------------
+
+In this version of pyplot there is limited support for multi-plot
+commands (as in pyplot and matlab). For example, you can type commands
+like the following:
+
+.. code-block:: python
+
+ plot(ws, [100, 101], 'r', ws, [200, 201], 'b', tool='plot_spectrum')
+
+This command will plot spectra 100 and 101 in red and spectra 200 and
+201 in blue on the same figure. You can also combine different
+workspaces, for example:
+
+.. code-block:: python
+
+ plot(ws, [100, 101], 'r', mar, [50, 41], 'b', tool='plot_spectrum')
+
+
+Style options supported as keyword arguments
+--------------------------------------------
+
+Unless otherwise stated, these options are in principle supported in
+all the plot variants. These options have the same (or as closed as
+possible) meaning as in matplotlib.
+
++------------+---------------------------------------------------------+
+|Option name | Values supported |
++============+=========================================================+
+|linewidth | real values |
++------------+---------------------------------------------------------+
+|linestyle | '-', '--', '-.' '.' |
++------------+---------------------------------------------------------+
+|marker | 'o', 'v', '^', '<', '>', 's', '*', 'h', '|', '_' |
++------------+---------------------------------------------------------+
+|color | color character or string ('b', 'blue', 'g', 'green', |
+| | 'k', 'black', 'y', 'yellow', 'c', 'cyan', 'r', 'red'. |
+| | 'm', 'magenta', etc.). RGB colors are not supported at |
+| | the moment. |
++------------+---------------------------------------------------------+
+
+Modifying the plot axes
+-----------------------
+
+You can modify different properties of the plot axes via functions, as
+seen before. This includes the x and y axis titles, limits and scale
+(linear or logarithmic). For example:
+
+.. code-block:: python
+
+ ylabel('Counts')
+ ylim(0, 8)
+ yscale('log')
+
+An alternative is to use equivalent methods provided by the Figure and
+Axes objects. For this you first need to retrieve the figure and axes
+where a plot (or line) has been shown.
+
+.. code-block:: python
+
+ lines = plot(mar,[3, 500, 800])
+ fig = lines[0].figure()
+ all_ax = fig.axes() # fig.axes() returns in principle a list
+ ax = all_ax[0] # but we only use one axes
+ ax.set_ylabel('Counts')
+ ax.set_xlabel('ToF')
+ ax.set_ylim(0, 8)
+ ax.set_xlim(1e2, 4e4)
+ ax.set_xscale('log')
+
+Functions that modify plot properties
+-------------------------------------
+
+Here is a list of the functions supported at the moment. They offer
+the same functionality as their counterparts in matplotlib's
+pyplot.
+
+- title
+- xlabel
+- ylabel
+- ylim
+- xlim
+- axis
+- xscale
+- yscale
+- grid
+- savefig
+
+This is a limited list of functions that should be sufficient for
+basic plots. These functions are presently provided as an example of
+this type of interface, and some of them provide functionality similar
+or equivalent to several of the keyword arguments for plot commands
+detailed in this documentation. Some others produce results equivalent
+to the more object oriented methods described above. For example, the
+function xlabel is equivalent to the method set_xlabel applied on the
+Axes object for the current figure.
+
+This module is by default imported into the standard MantidPlot
+namespace. You can use the functions and classes included here without
+any prefix or adding this module name prefix (pymantidplot.pyplot), as
+in the following example:
+
+.. code-block:: python
+
+ # Two equivalent lines:
+ pymantidplot.pyplot.plot([1, 3, 2])
+ plot([1, 3, 2])
+
+Note that the plot() function of this module has replaced the
+traditional plot() function of MantidPlot which has been moved into a
+package called qtiplot. To use it you can do as follows:
+
+.. code-block:: python
+
+ pymantidplot.qtiplot.plot('MAR11060', [800, 801])
+ # or if you prefer shorter prefixes:
+ import pymantidplot.qtiplot as qtiplt
+ qtiplt.plot('MAR11060', [800, 801])
+
+
+Below is the reference documentation of the classes and functions
+included in this module.
+
+"""
+# Copyright © 2014-2015 ISIS Rutherford Appleton Laboratory, NScD
+# Oak Ridge National Laboratory & European Spallation Source
+#
+# This file is part of Mantid.
+# Mantid is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 3 of the License, or
+# (at your option) any later version.
+#
+# Mantid is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see .
+#
+# File change history is stored at: .
+# Code Documentation is available at:
+
+try:
+ import _qti
+except ImportError:
+ raise ImportError('The \'mantidplot\' and \'pymantidplot.pyplot\' modules can only be used from within MantidPlot.')
+
+import numpy as np
+from PyQt4 import Qt, QtGui, QtCore
+from mantid.api import (IMDWorkspace as IMDWorkspace, MatrixWorkspace as MatrixWorkspace, AlgorithmManager as AlgorithmManager, AnalysisDataService as ADS)
+from mantid.api import mtd
+# return __is_workspace(arg) or (mantid.api.mtd.doesExist(arg) and isinstance(mantid.api.mtd[arg], mantid.api.IMDWorkspace))
+from mantid.simpleapi import CreateWorkspace as CreateWorkspace
+import mantidplot
+
+print ("You are loading '" + __name__ + "', which is an experimental module." +
+"""
+Please note: this module is at a very early stage of development and
+provides limited functionality. It is work in progress and is subject
+to change. Feedback is very much welcome! Please let us know any wishes
+and suggestions.""")
+
+class Line2D():
+ """
+ A very minimal replica of matplotlib.Line.Line2D. The true Line2D
+ is a sublcass of matplotlib.artist and provides tons of
+ functionality. At the moment this just provides get_xdata(),
+ get_ydata(), and figure() methods. It also holds its Graph
+ object and through it it would be possible to provide
+ additional selected functionality. Keep in mind that providing
+ GUI line/plot manipulation functionality would require a proxy
+ for this class.
+ """
+
+ def __init__(self, graph, index, x_data, y_data, fig=None):
+ self._graph = graph
+ self._index = index # will (may) be needed to change properties of this line
+ self._xdata = x_data
+ self._ydata = y_data
+ self._fig = fig
+
+ def get_xdata(self):
+ return self._xdata
+
+ def get_ydata(self):
+ return self._ydata
+
+ def figure(self):
+ return self._fig
+
+class Axes():
+ """
+ A very minimal replica of matplotlib.axes.Axes. The true Axes is a
+ sublcass of matplotlib.artist and provides tons of functionality.
+ At the moment this just provides a few set methods for properties
+ such as labels and axis limits.
+ """
+
+ """Many plot manipulation functions that are provided in
+ matplolib through Axes objects, for example to manipulate the x/y
+ ticks, are not currently supported. Objects of this class hold
+ their Figure object. Presently every figure has a single Axes
+ object, and there is no support for multiple axes (as in
+ fig.add_axes() or fix.axes()).
+ """
+
+ def __init__(self, fig, xscale='linear', yscale='linear'):
+ self._fig = fig
+ # state of x and y scale kept here. C++ Graph.isLog() not yet exposed
+ self._xscale = xscale
+ self._yscale = yscale
+
+ def axis(self, lims):
+ """
+ Set the boundaries or limits of the x and y axes
+
+ @param lims :: list or vector specifying min x, max x, min y, max y
+ """
+ l = __last_fig()._graph.activeLayer()
+ if 4 != len(lims):
+ raise ValueError("Error: 4 real values are required for the x and y axes limits")
+ l.setScale(*lims)
+
+ def set_xlabel(self, lbl):
+ """
+ Set the label or title of the x axis
+
+ @param lbl :: x axis lbl
+ """
+ l = self.get_figure()._graph.activeLayer()
+ l.setXTitle(lbl)
+
+ def set_ylabel(self, lbl):
+ """
+ Set the label or title of the y axis
+
+ @param lbl :: y axis lbl
+ """
+ l = self.get_figure()._graph.activeLayer()
+ l.setYTitle(lbl)
+
+ def set_xlim(self, xmin, xmax):
+ """
+ Set the boundaries of the x axis
+
+ @param xmin :: minimum value
+ @param xmax :: maximum value
+ """
+ l = self.get_figure()._graph.activeLayer()
+ l.setAxisScale(2, xmin, xmax)
+
+ def set_ylim(self, ymin, ymax):
+ """
+ Set the boundaries of the y axis
+
+ @param ymin :: minimum value
+ @param ymax :: maximum value
+ """
+ l = self.get_figure()._graph.activeLayer()
+ l.setAxisScale(0, ymin, ymax)
+
+ def set_xscale(self, scale_str):
+ """
+ Set the type of scale of the x axis
+
+ @param scale_str :: either 'linear' for linear scale or 'log' for logarithmic scale
+ """
+ if 'log' != scale_str and 'linear' != scale_str:
+ raise ValueError("You need to specify either 'log' or 'linear' type of scale for the x axis." )
+
+ l = self.get_figure()._graph.activeLayer()
+ if scale_str == 'log':
+ if 'log' == self._yscale:
+ l.logLogAxes()
+ else:
+ l.logXLinY()
+ elif scale_str == 'linear':
+ if 'log' == self._yscale:
+ l.logYlinX()
+ else:
+ l.linearAxes()
+ self._xscale = scale_str
+
+ def set_yscale(self, scale_str):
+ """
+ Set the type of scale of the y axis
+
+ @param scale_str :: either 'linear' for linear scale or 'log' for logarithmic scale
+ """
+ if 'log' != scale_str and 'linear' != scale_str:
+ raise ValueError("You need to specify either 'log' or 'linear' type of scale for the y axis." )
+
+ l = self.get_figure()._graph.activeLayer()
+ if scale_str == 'log':
+ if 'log' == self._xscale:
+ l.logLogAxes()
+ else:
+ l.logYlinX()
+ elif scale_str == 'linear':
+ if 'log' == self._xscale:
+ l.logXLinY()
+ else:
+ l.linearAxes()
+ self._yscale = scale_str
+
+ def get_figure(self, ):
+ """
+ Get the figure where this Axes object is included
+
+ Returns :: figure object for the figure that contains this Axes
+ """
+ return self._fig
+
+
+class Figure():
+ """
+ A very minimal replica of matplotlib.figure.Figure. This class is
+ here to support manipulation of multiple figures from the command
+ line.
+ """
+
+ """For the moment this is just a very crude wrapper for Graph
+ (proxy to qti Multilayer), and it is here just to hide (the rather
+ obscure) Graph from users.
+ """
+ # Holds the set of figure ids (integers) as they're being created and/or destroyed
+ __figures = {}
+ # Always increasing seq number, not necessarily the number of figures in the dict
+ __figures_seq = 0
+
+ def __init__(self, num):
+ if isinstance(num, int):
+ # normal matplotlib use, like figure(2)
+ missing = -1
+ fig = Figure.__figures.get(num, missing)
+ if missing == fig:
+ self._graph = Figure.__empty_graph()
+ Figure.__figures[num] = self
+ if num > Figure.__figures_seq:
+ Figure.__figures_seq = num
+ self._axes = Axes(self)
+ else:
+ if None == fig._graph._getHeldObject():
+ # has been destroyed!
+ self._graph = Figure.__empty_graph()
+ self._axes = Axes(self)
+ else:
+ self._graph = fig._graph
+ self._axes = fig._axes
+ elif isinstance(num, mantidplot.proxies.Graph):
+ if None == num._getHeldObject():
+ # deleted Graph!
+ self._graph = Figure.__empty_graph()
+ else:
+ self._graph = num
+ num = Figure.__make_new_fig_number()
+ Figure.__figures[num] = self
+ self._axes = Axes(self)
+ else:
+ raise ValueError("To create a Figure you need to specify a figure number or a Graph object." )
+
+ def suptitle(self, title):
+ """
+ Set a title for the figure
+
+ @param title :: title string
+ """
+ l = self._graph.activeLayer()
+ l.setTitle(title)
+
+ def axes(self):
+ """
+ Obtain the list of axes in this figure.
+
+ Returns :: list of axes. Presently only one Axes object is supported
+ and this method returns a single object list
+ """
+ return [self._axes]
+
+ def savefig(self, name):
+ """
+ Save current plot into a file. The format is guessed from the file extension (.eps, .png, .jpg, etc.)
+
+ @param name :: file name
+ """
+ if not name:
+ raise ValueError("Error: you need to specify a non-empty file name")
+ l = _graph.activeLayer()
+ l.saveImage(name);
+
+ @classmethod
+ def fig_seq(cls):
+ """ Helper method, returns the current sequence number for figures"""
+ return cls.__figures_seq
+
+ @classmethod
+ def __make_new_fig_number(cls):
+ """ Helper method, creates and return a new figure number"""
+ num = cls.__figures_seq
+ avail = False
+ while not avail:
+ missing = -1
+ fig = cls.__figures.get(num, missing)
+ if missing == fig:
+ avail = True # break
+ else:
+ num += 1
+ cls.__figures_seq = num
+ return num
+
+ @staticmethod
+ def __empty_graph():
+ """Helper method, just create a new Graph with an 'empty' plot"""
+ lines = plot([0])
+ return lines._graph
+
+
+def __empty_fig():
+ """Helper function, for the functional interface. Makes a blank/empty figure"""
+ lines = plot([0]) # for the very first figure, this would generate infinite recursion!
+ return Figure(lines[0]._graph)
+
+# TODO/TOTHINK: no 'hold' function support for now. How to handle multi-plots with different types/tools? Does it make sense at all?
+__hold_status = False
+
+__last_shown_fig = None
+
+def __last_fig():
+ """
+ Helper function, especially for the functional interface.
+ Avoid using it inside the plot_spectrum, plot_bin, etc. as there's risk of infinite recursion
+ Returns :: last figure, creating new one if there was none
+ """
+ global __last_shown_fig
+ if not __last_shown_fig:
+ f = __empty_fig()
+ __last_shown_fig = f
+ return __last_shown_fig
+
+def __update_last_shown_fig(g):
+ """
+ Helper function, especially for the functional interface.
+ @param g :: graph object
+ Returns :: new last fig
+ """
+ global __last_shown_fig
+ __last_shown_fig = Figure(g)
+ return __last_shown_fig
+
+def __is_array(arg):
+ """
+ Is the argument a python or numpy list?
+ @param arg :: argument
+
+ Returns :: True if the argument is a python or numpy list
+ """
+ return isinstance(arg, list) or isinstance(arg, np.ndarray)
+
+def __is_array_or_int(arg):
+ """
+ Is the argument a valid workspace index/indices, which is to say:
+ Is the argument an int, or python or numpy list?
+ @param arg :: argument
+
+ Returns :: True if the argument is an integer, or a python or numpy list
+ """
+ return isinstance(arg, int) or __is_array(arg)
+
+
+def __is_registered_workspace_name(arg):
+ """"
+ Check whether the argument passed is the name of a registered workspace
+
+ @param arg :: argument (supposedly a workspace name)
+
+ Returns :: True if arg is a correct workspace name
+ """
+ return (isinstance(arg, basestring) and mtd.doesExist(arg) and isinstance(mtd[arg], IMDWorkspace))
+
+def __is_valid_single_workspace_arg(arg):
+ """"
+ Check whether the argument passed can be used as a workspace input. Note that this differs from
+ __is_workspace() in that workspace names are also accepted. Throws ValueError with informative
+ message if arg is not a valid workspace object or name.
+
+ @param arg :: argument (supposedly one workspace, possibly given by name)
+
+ Returns :: True if arg can be accepted as a workspace
+ """
+ if __is_workspace(arg) or __is_registered_workspace_name(arg):
+ return True
+ else:
+ return False
+
+def __is_valid_workspaces_arg(arg):
+ """"
+ Check whether the argument passed can be used as a workspace(s) input. Note that this differs from
+ __is_workspace() in that lists of workspaces and workspace names are also accepted.
+
+ @param arg :: argument (supposedly one or more workspaces, possibly given by name)
+
+ Returns :: True if arg can be accepted as a workspace or a list of workspaces
+ """
+ if __is_valid_single_workspace_arg(arg):
+ return True
+ else:
+ if 0 == len(arg):
+ return False
+ for name in arg:
+ # name can be a workspace name or a workspace object
+ try:
+ __is_valid_single_workspace_arg(name)
+ except:
+ raise ValueError("This parameter passed in a list of workspaces is not a valid workspace: " + str(name))
+ return True
+
+def __is_data_pair(a, b):
+ """
+ Are the two arguments passed (a and b) a valid data pair for plotting, like in plot(x, y) or
+ plot(ws, [0, 1, 2])?
+ @param a :: first argument passed (supposedly array or workspace(s))
+ @param b :: second argument (supposedly an array of values, or indices)
+
+ Returns :: True if the arguments can be used to plot something is an integer, or a python or numpy list
+ """
+ res = (__is_array(a) and __is_array(b)) or (__is_valid_workspaces_arg(a) and __is_array_or_int(b))
+ return res
+
+def __is_workspace(arg):
+ """
+ Is the argument a Mantid MatrixWorkspace?
+ @param arg :: argument
+
+ Returns :: True if the argument a MatrixWorkspace
+ """
+ return isinstance(arg, MatrixWorkspace)
+
+def __is_array_of_workspaces(arg):
+ """
+ Is the argument a sequence of Mantid MatrixWorkspaces?
+ @param arg :: argument
+
+ Returns :: True if the argument is a sequence of MatrixWorkspace
+ """
+ return __is_array(arg) and len(arg) > 0 and __is_workspace(arg[0])
+
+
+def __create_workspace(x, y, name="__array_dummy_workspace"):
+ """
+ Create a workspace. Also puts it in the ADS with __ name
+ @param x :: x array
+ @param y :: y array
+ @param name :: workspace name
+
+ Returns :: Workspace
+ """
+ alg = AlgorithmManager.create("CreateWorkspace")
+ alg.setChild(True)
+ alg.initialize()
+ # fake empty workspace (when doing plot([]), cause setProperty needs non-empty data)
+ if [] == x:
+ x = [0]
+ if [] == y:
+ y = [0]
+ alg.setProperty("DataX", x)
+ alg.setProperty("DataY", y)
+ name = name + "_" + str(Figure.fig_seq())
+ alg.setPropertyValue("OutputWorkspace", name)
+ alg.execute()
+ ws = alg.getProperty("OutputWorkspace").value
+ ADS.addOrReplace(name, ws) # Cannot plot a workspace that is not in the ADS
+ return ws
+
+
+def __list_of_lines_from_graph(g, first_line=0):
+ """
+ Produces a python list of line objects, with one object per line plotted on the passed graph
+ Note: at the moment these objects are of class Line2D which is much simpler than matplotlib.lines.Line2D
+ This function should always be part of the process of creating a new figure/graph, and it guarantees
+ that this figure being created is registered as the last shown figure.
+
+ @param g :: graph (with several plot layers = qti Multilayer)
+ @param first_line :: index to start from (useful for hold='on', multi-plots, etc.)
+
+ Returns :: List of line objects
+ """
+ if None == g:
+ raise ValueError("Got empty Graph object, cannot get its lines." )
+ # assume we use a single layer
+ active = g.activeLayer()
+ res = []
+ for i in range(first_line, active.numCurves()):
+ x_data = []
+ y_data = []
+ d = active.curve(i).data()
+ for i in range(0, active.curve(i).data().size()):
+ x_data.append(d.x(i))
+ y_data.append(d.y(i))
+ res.append(Line2D(g, i, x_data, y_data))
+
+ fig = __update_last_shown_fig(g)
+ for lines in res:
+ lines._fig = fig
+
+ return res;
+
+def __matplotlib_defaults(l):
+ """
+ Tries to (approximately) mimic the default plot properties of a pylab.plot()
+ @param l :: layer (plot) from a mantidplot Graph object
+
+ Returns :: nothing, just modifies properties of the layer passed
+ """
+ if None == l:
+ raise ValueError("Got empty Layer object, cannot modify its properties." )
+ l.removeLegend()
+ for i in range(0, l.numCurves()):
+ l.setCurveLineColor(i, __color_char_to_color_idx['b'])
+ l.setTitle(' ')
+ l.setXTitle(' ')
+ l.setYTitle(' ')
+
+__marker_to_plotsymbol = {
+ 'o': _qti.PlotSymbol.Ellipse, 'v': _qti.PlotSymbol.DTriangle, '^': _qti.PlotSymbol.UTriangle,
+ '<': _qti.PlotSymbol.LTriangle, '>': _qti.PlotSymbol.RTriangle, 's': _qti.PlotSymbol.Rect,
+ '*': _qti.PlotSymbol.Star1, 'h': _qti.PlotSymbol.Hexagon, '|': _qti.PlotSymbol.VLine,
+ '_': _qti.PlotSymbol.HLine
+}
+
+"""Contains all the supported line styles"""
+__linestyle_to_qt_penstyle = {
+ '-': QtCore.Qt.SolidLine, '--': QtCore.Qt.DashLine,
+ '-.': QtCore.Qt.DashDotLine, ':': QtCore.Qt.DotLine
+} # other available: Qt.DashDotDotLine, Qt.CustomDashLine
+
+def __apply_linestyle(graph, linestyle, first_line=0):
+ """
+ Sets the linestyle of lines/curves of the active layer of the graph passed
+
+ @param graph :: mantidplot graph (figure)
+ @param linestyle :: linestyle string
+ @param first_line :: index of first line to which the linestyle will apply
+ (useful when in hold mode / adding lines)
+
+ Returns :: nothing, just modifies the line styles of the active layer of the graph passed
+ """
+ global __linestyle_to_qt_penstyle
+ wrong = 'inexistent'
+ penstyle = __linestyle_to_qt_penstyle.get(linestyle, wrong)
+ if wrong == penstyle:
+ raise ValueError("Wrong linestyle given, unrecognized: " + linestyle)
+ l = graph.activeLayer()
+ for i in range(first_line, l.numCurves()):
+ l.setCurveLineStyle(i, penstyle)
+
+# beware this is not Qt.Qt.color_name (black, etc.)
+__color_char_to_color_idx = {
+ 'k': 0, 'r': 1, 'g': 2, 'b': 3, 'c': 4, 'm': 5, 'y': 18,
+ 'black': 0, 'red': 1, 'green': 2, 'blue': 3, 'cyan': 4, 'magenta': 5, 'orange': 6,
+ 'purple': 7, 'darkGreen': 8, 'darkBlue': 9, 'brown': 10, 'gray': 17, 'yellow': 18
+}
+
+def __apply_line_color(graph, c, first_line=0):
+ """
+ Sets the color of curves of the active layer of the graph passed
+
+ @param graph :: mantidplot graph (figure)
+ @param c :: color string
+ @param first_line :: index of first line to which the color will apply
+ (useful when in hold mode / adding lines)
+
+ Returns :: nothing, just modifies the line styles of the active layer of the graph passed
+ """
+ inex = 'inexistent'
+ col_idx = __color_char_to_color_idx.get(c, inex)
+ if inex == col_idx:
+ col_idx = QtGui.QColor(c)
+ l = graph.activeLayer()
+ for i in range(first_line, l.numCurves()):
+ l.setCurveLineColor(i, col_idx) # beware this is not Qt.Qt.black, but could be faked with QtGui.QColor("orange")
+
+def __apply_marker(graph, marker, first_line=0):
+ """
+ Sets the marker of curves of the active layer of the graph passed
+
+ @param graph :: mantidplot graph (figure)
+ @param marker :: line marker character
+ @param first_line :: index of first line to which the color will apply
+ (useful when in hold mode / adding lines)
+
+ Returns :: nothing
+ """
+ wrong = 'inexistent'
+ sym_code = __marker_to_plotsymbol.get(marker, wrong)
+ if wrong == sym_code:
+ raise ValueError("Warning: unrecognized marker: " + str(marker))
+ sym = _qti.PlotSymbol(sym_code, QtGui.QBrush(), QtGui.QPen(), QtCore.QSize(5,5))
+ l = graph.activeLayer()
+ for idx in range(first_line, l.numCurves()):
+ l.setCurveSymbol(idx, sym)
+
+def __is_marker(char):
+ """ Is it a marker character
+ @param char :: suspected marker character coming from a linestyle string
+ Returns :: True if it's a marker character
+ """
+ inex = 'inexistent'
+ m = __marker_to_plotsymbol.get(char, inex)
+ return m != inex
+
+__linestyle_to_qt_penstyle = {
+ '-': QtCore.Qt.SolidLine, '--': QtCore.Qt.DashLine,
+ '-.': QtCore.Qt.DashDotLine, ':': QtCore.Qt.DotLine
+} # other available: Qt.DashDotDotLine, Qt.CustomDashLine
+
+def __is_linestyle(stl, i):
+ """
+ Check if we have a linestyle string in string s at position i
+ @param stl :: input (style) string, for example: '-.g', 'r', ':b'
+ @param i :: index where to start checking in string s
+
+ Returns :: 0 if no linestyle string is identified, length of the string (1 or 2) otherwise
+ """
+ global __linestyle_to_qt_penstyle
+
+ if len(stl) <= i:
+ return 0
+
+ if len(stl) > i+1:
+ if '-' == stl[i+1] or '.' == stl[i+1]:
+ # can check 2 chars
+ wrong = 'inexistent'
+ penstyle = __linestyle_to_qt_penstyle.get(stl[i:i+2], wrong)
+ if wrong != penstyle:
+ return 2
+
+ if '-'==stl[i] or ':'==stl[i]:
+ return 1
+ else:
+ return 0
+
+def __apply_plot_args(graph, first_line, *args):
+ """
+ Applies args, like '-r' etc.
+ @param graph :: a graph (or figure) that can contain multiple layers
+ @param first_line :: first line to which the options will apply (useful when in hold mode / adding lines)
+ @param args :: plot arguments
+
+ Returns :: nothing, just uses kwargs to modify properties of the layer passed
+ """
+ if None==graph or len(args) < 1 or ((),) == args:
+ return
+
+ for a in args:
+ if isinstance(a, basestring):
+ # this will eat characters as they come, without minding much the past/previous characters
+ # users can chain as many modifiers as they wish. It could be modified to be more strict/picky
+ i = 0
+ while i < len(a):
+ linestyle_len = __is_linestyle(a,i)
+ if linestyle_len > 0:
+ __apply_linestyle(graph, a[i:i+linestyle_len], first_line)
+ i += linestyle_len
+ elif __is_marker(a[i]):
+ __apply_marker(graph, a[i:], first_line)
+ i += 1
+ elif a[i].isalpha():
+ __apply_line_color(graph, a[i], first_line)
+ i += 1
+ else:
+ # TOTHINK - error here? like this? sure? or just a warning?
+ raise ValueError("Unrecognized character in input string: " + str(a[i]))
+ else:
+ raise ValueError("Expecting style string, but got an unrecognized input parameter: " + str(a) + ", of type: " + str(type(a)))
+
+def __apply_plot_kwargs(graph, first_line=0, **kwargs):
+ """
+ Applies kwargs
+ @param graph :: a graph (or figure) that can contain multiple layers
+
+ Returns :: nothing, just uses kwargs to modify properties of the layer passed
+ """
+ if None==graph or None==kwargs or ((),) == kwargs:
+ return
+
+ for key in kwargs:
+ if 'linestyle' == key:
+ __apply_linestyle(graph, kwargs[key])
+
+ elif 'linewidth' == key:
+ l = graph.activeLayer()
+ for i in range(first_line, l.numCurves()):
+ l.setCurveLineWidth(i, kwargs[key])
+
+ elif 'color' == key:
+ __apply_line_color(graph, kwargs[key], first_line)
+
+ elif 'marker' == key:
+ __apply_marker(graph, kwargs[key], first_line)
+
+def __is_multiplot_command(*args, **kwargs):
+ """
+ Finds out if the passed *args make a valid multi-plot command. At the same time, splits the
+ multi-plot command line into individual plot commands.
+
+ @param args :: curve data and options.
+ @param kwargs :: plot keyword options
+
+ Returns :: tuple: (boolean: whether it is a multiplot command, list of single plot commands as tuples)
+ """
+ # A minimum multi-plot command would be plot(x, y, z, w) or plot(ws1, idx1, ws2, idx2)
+ nargs = len(args)
+ # this will be a list with the sequence of individual plots (a tuples, each describing a single plot)
+ plots_seq = []
+ if nargs < 4:
+ return (False, [])
+ i = 0
+ while i < nargs:
+ a = []
+ b = []
+ style = ''
+ if (nargs-i) >= 3:
+ if __is_data_pair(args[i], args[i+1]):
+ a = args[i]
+ b = args[i+1]
+ i += 2
+ else:
+ return (False, []);
+ # can have style string, but don't get confused with single workspace name strings!
+ if (not __is_registered_workspace_name(args[i])) and isinstance(args[i], basestring):
+ style = args[i]
+ i += 1
+ plots_seq.append((a,b,style))
+
+ elif (nargs-i) >= 2:
+ if __is_data_pair(args[i], args[i+1]):
+ a = args[i]
+ b = args[i+1]
+ i += 2
+ else:
+ return (False, [])
+ plots_seq.append((a, b, ''))
+
+ elif (nargs-i) > 0:
+ raise ValueError("Not plottable. I do not know what to do with this last parameter: " + args[i] + ", of type " + str(type(args)))
+
+ return (i == nargs, plots_seq)
+
+def __process_multiplot_command(plots_seq, **kwargs):
+ """
+ Make one plot at a time when given a multi-plot command.
+
+ @param plots_seq :: list of individual plot parameters
+ @param kwargs :: plot style options
+
+ Returns :: the list of curves included in the plot
+ """
+ lines = []
+ if len(plots_seq) >= 1:
+ if not 'hold' in kwargs:
+ kwargs['hold'] = 'off'
+ lines = plot(*(plots_seq[0]), **kwargs)
+ for i in range(1, len(plots_seq)):
+ kwargs['hold'] = 'on'
+ lines.extend(plot(*(plots_seq[i]), **kwargs))
+ return lines
+
+def __translate_hold_kwarg(**kwargs):
+ """
+ Helper function to translate from hold='on'/'off' kwarg to a True/False value for the
+ mantidplot window and window error_bars
+
+ @param kwargs :: keyword arguments passed to a plot function, this function only cares about hold. Any
+ value different from 'on' will be considered as 'off'
+
+ Returns :: tuple with a couple of values: True/False value for window, and True/False for clearWindow,
+ to be used with plotSpectrum, plotBin, etc.
+ """
+ # window and clearWindow
+ window_val = None
+ clearWindow_val = False
+ hold_name = 'hold'
+ missing_off = -1
+ str_val = kwargs.get(hold_name, missing_off)
+ if str_val != missing_off and str_val == 'on':
+ if None == __last_shown_fig:
+ window_val = None
+ else:
+ window_val = __last_fig()._graph
+ clearWindow_val = False
+
+ return window_val, clearWindow_val
+
+def __translate_error_bars_kwarg(**kwargs):
+ """
+ Helper function to translate from error_bars=True/False kwarg to a True/False value for the
+ mantidplot error_bars argument
+
+ @param kwargs :: keyword arguments passed to a plot function. This function only cares about 'error_bars'.
+ Any value different from 'True' will be considered as 'False'
+
+ Returns :: True/False value for error_bars, to be used with plotSpectrum, plotBin, etc.
+
+ """
+ # error_bars param
+ bars_val = False
+ bars_name = 'error_bars'
+ missing_off = -1
+ str_val = kwargs.get(bars_name, missing_off)
+ if str_val != missing_off and str_val == 'True':
+ bars_val = True
+
+ return bars_val
+
+def __plot_as_workspace(*args, **kwargs):
+ """
+ plot spectrum via qti plotting framework to plot a workspace.
+
+ @param args :: curve data and options.
+ @param kwargs :: plot line options
+
+ Returns :: List of line objects
+ """
+ return plot_spectrum(*args, **kwargs)
+
+def __plot_as_workspaces_list(*args, **kwargs):
+ """
+ Plot a series of workspaces
+ @param args :: curve data and options.
+ @param kwargs :: plot line options
+
+ Returns :: List of line objects
+ """
+ # mantidplot.plotSpectrum can already handle 1 or more input workspaces.
+ return __plot_as_workspace(*args, **kwargs)
+
+
+def __plot_as_array(*args, **kwargs):
+ """
+ Plot from an array
+ @param args :: curve data and options.
+ @param kwargs :: plot line options
+
+ Returns :: the list of curves (1) included in the plot
+ """
+ y = args[0]
+ idx_style = len(args) # have to guess if we get plot(x,'r'), or plot(x, y, 'r') or no style string
+ if len(args) > 1:
+ if __is_array(args[1]):
+ ws = __create_workspace(y, args[1])
+ idx_style = 2
+ elif isinstance(args[1], basestring):
+ x = range(0, len(y), 1) # 0 to n, incremented by 1.
+ ws = __create_workspace(x, y)
+ # have to assume that args[1] is a style string
+ idx_style = 1
+ else:
+ raise ValueError("Inputs are of type: " + str(type(args)) + ". Not plottable." )
+ else:
+ x = range(0, len(y), 1)
+ ws = __create_workspace(x, y)
+
+ lines = __plot_as_workspace(ws, [0], *args[idx_style:], **kwargs)
+ graph = None
+ if len(lines) > 0:
+ graph = lines[0]._graph
+ else:
+ raise Exception("Could not plot a workspace: " + ws)
+ # something to improve: if the C++ Graph class provided a plot1D that doesn't do show(), so that
+ # we could modify properties behind the scene and at the end do the show(). Con: do we really need
+ # to load the qti layer with more methods because of outer layers like here?
+ if 0 == len(kwargs):
+ __matplotlib_defaults(graph.activeLayer())
+ return __list_of_lines_from_graph(graph)
+
+def __plot_with_tool(tool, *args, **kwargs):
+ bin_tool_name = 'plot_bin'
+ spectrum_tool_name = 'plot_spectrum'
+ md_tool_name = 'plot_md'
+
+ if bin_tool_name == tool or spectrum_tool_name == tool:
+ if len(args) < 2:
+ raise ValueError("To plot using %s as a tool you need to give at least two parameters"%tool)
+
+ if bin_tool_name == tool:
+ return plot_bin(args[0], args[1], *args[2:], **kwargs)
+ elif md_tool_name == tool:
+ return plot_md(args[0], *args[1:], **kwargs)
+ elif spectrum_tool_name == tool:
+ return plot_spectrum(args[0], args[1], *args[2:], **kwargs)
+ # here you would add slice/spectrum/instrument viewer, etc. and maybe you'll want to put them in a dict
+ else:
+ raise ValueError("Unrecognized tool specified: '" + tool + ";. Cannot plot this. ")
+
+def __plot_with_best_guess(*args, **kwargs):
+ y = args[0]
+ if __is_array(y):
+ if __is_array_of_workspaces(y):
+ return __plot_as_workspaces_list(*args, **kwargs)
+ else:
+ return __plot_as_array(*args, **kwargs)
+ else:
+ # mantidplot.plotSpectrum can handle workspace names (strings)
+ return __plot_as_workspace(*args, **kwargs)
+
+def plot_bin(workspaces, indices, *args, **kwargs):
+ """
+ X-Y plot of the bin counts in a workspace.
+
+ Plots one or more bin, selected by indices, using spectra numbers as x-axis and bin counts for
+ each spectrum as y-axis.
+
+ @param workspaces :: workspace or list of workspaces (both workspace objects and names accepted)
+ @param indices :: indices of the bin(s) to plot
+
+ Returns :: the list of curves included in the plot
+ """
+ # Find optional params to plotBin
+ bars_val = __translate_error_bars_kwarg(**kwargs)
+ window_val, clearWindow_val = __translate_hold_kwarg(**kwargs)
+
+ # to change properties on the new lines being added
+ first_line = 0
+ if None != window_val:
+ first_line = window_val.activeLayer().numCurves()
+
+ graph = mantidplot.plotBin(workspaces, indices, error_bars=bars_val, type=-1, window=window_val, clearWindow=clearWindow_val)
+
+ __apply_plot_args(graph, first_line, *args)
+ __apply_plot_kwargs(graph, first_line, **kwargs)
+
+ return __list_of_lines_from_graph(graph, first_line)
+
+
+def plot_md(workspaces, *args, **kwargs):
+ """
+ X-Y plot of an MDWorkspace.
+
+ @param workspaces :: workspace or list of workspaces (both workspace objects and names accepted)
+
+ Returns :: the list of curves included in the plot
+ """
+ # Find optional params to plotBin
+ bars_val = __translate_error_bars_kwarg(**kwargs)
+ window_val, clearWindow_val = __translate_hold_kwarg(**kwargs)
+
+ # to change properties on the new lines being added
+ first_line = 0
+ if None != window_val:
+ first_line = window_val.activeLayer().numCurves()
+
+ graph = mantidplot.plotMD(workspaces, normalization=mantidplot.DEFAULT_MD_NORMALIZATION, error_bars=bars_val, window=window_val, clearWindow=clearWindow_val)
+
+ __apply_plot_args(graph, first_line, *args)
+ __apply_plot_kwargs(graph, first_line, **kwargs)
+
+ return __list_of_lines_from_graph(graph, first_line)
+
+
+def plot_spectrum(workspaces, indices, *args, **kwargs):
+ """X-Y Plot of spectra in a workspace.
+
+ Plots one or more spectra, selected by indices, using bin boundaries as x-axis
+ and the spectra values in each bin as y-axis.
+
+ @param workspaces :: workspace or list of workspaces (both workspace objects and names accepted)
+ @param indices :: indices of the spectra to plot, given as a single integer or a list of integers
+
+ Returns :: the list of curves included in the plot
+
+ """
+ # Find optional params to plotSpectrum
+ bars_val = __translate_error_bars_kwarg(**kwargs)
+ window_val, clearWindow_val = __translate_hold_kwarg(**kwargs)
+
+ # to change properties on the new lines being added
+ first_line = 0
+ if None != window_val:
+ first_line = window_val.activeLayer().numCurves()
+
+ graph = mantidplot.plotSpectrum(workspaces, indices, error_bars=bars_val, type=-1, window=window_val, clearWindow=clearWindow_val)
+
+ __apply_plot_args(graph, first_line, *args)
+ __apply_plot_kwargs(graph, first_line, **kwargs)
+
+ return __list_of_lines_from_graph(graph, first_line)
+
+
+def plot(*args, **kwargs):
+ """
+ Plot the data in various forms depending on what arguments are passed. Currently supported
+ inputs: arrays (as Python lists or numpy arrays) and workspaces (by name or workspace objects).
+
+ @param args :: curve data and options
+ @param kwargs :: plot line options
+
+ Returns :: the list of curves included in the plot
+
+ args can take different forms depending on what you plot. You can plot:
+
+ * a python list or array (x) for example like this: plot(x)
+
+ * a workspace (ws) for example like this: plot(ws, [100,101]) # this will plot spectra 100 and 101
+
+ * a list of workspaces (ws, ws2, ws3, etc.) for example like this: plot([ws, ws2, ws3], [100,101])
+
+ * workspaces identified by their names: plot(['HRP39182', 'MAR11060.nxs'], [100,101])
+
+ You can also pass matplotlib/pyplot style strings as arguments, for example: plot(x, '-.')
+
+ As keyword arguments (kwargs) you can specify multiple
+ parameters, for example: linewidth, linestyle, marker, color.
+
+ An important keyword argument is tool. At the moment the
+ following values are supported:
+
+ * plot_spectrum (default for workspaces)
+ * plot_bin
+ * plot_md
+
+ Please see the documentation of this module (use help()) for more details.
+
+ """
+ nargs = len(args)
+ if nargs < 1:
+ raise ValueError("You did not pass any argument. You must provide data to plot.")
+
+ # TOTHINK: should there be an exception if it's plot_md (tool='plot_md')
+ (is_it, plots_seq) = __is_multiplot_command(*args, **kwargs)
+ if is_it:
+ return __process_multiplot_command(plots_seq, **kwargs)
+ elif len(args) > 3:
+ raise ValueError("Could not interpret the arguments passed. You passed more than 3 positional arguments but this does not seem to be a correct multi-plot command. Please check your command and make sure that the workspaces given are correct.")
+
+ # normally guess; exception if e.g. a parameter tool='plot_bin' is given
+ try:
+ tool_val = kwargs['tool']
+ del kwargs['tool']
+ return __plot_with_tool(tool_val, *args, **kwargs)
+ except KeyError:
+ return __plot_with_best_guess(*args, **kwargs)
+
+
+#=============================================================================
+# Functions, for pyplot / old matlab style manipulation of figures
+#=============================================================================
+
+def xlim(xmin, xmax):
+ """
+ Set the boundaries of the x axis
+
+ @param xmin :: minimum value
+ @param xmax :: maximum value
+ """
+ l = __last_fig()._graph.activeLayer()
+ l.setAxisScale(2, xmin, xmax)
+
+def ylim(ymin, ymax):
+ """
+ Set the boundaries of the y axis
+
+ @param ymin :: minimum value
+ @param ymax :: maximum value
+ """
+ l = __last_fig()._graph.activeLayer()
+ l.setAxisScale(0, ymin, ymax)
+
+def xlabel(lbl):
+ """
+ Set the label or title of the x axis
+
+ @param lbl :: x axis lbl
+ """
+ l = __last_fig()._graph.activeLayer()
+ l.setXTitle(lbl)
+
+def ylabel(lbl):
+ """
+ Set the label or title of the y axis
+
+ @param lbl :: y axis lbl
+ """
+ l = __last_fig()._graph.activeLayer()
+ l.setYTitle(lbl)
+
+def title(title):
+ """
+ Set title of the active plot
+
+ @param title :: title string
+ """
+ l = __last_fig()._graph.activeLayer()
+ l.setTitle(title)
+
+def axis(lims):
+ """
+ Set the boundaries or limits of the x and y axes
+
+ @param lims :: list or vector specifying min x, max x, min y, max y
+ """
+ l = __last_fig()._graph.activeLayer()
+ if 4 != len(lims):
+ raise ValueError("Error: 4 real values are required for the x and y axes limits")
+ l.setScale(*lims)
+
+def yscale(scale_str):
+ """
+ Set the type of scale of the y axis
+
+ @param scale_str :: either 'linear' for linear scale or 'log' for logarithmic scale
+ """
+ ax = __last_fig()._axes
+ ax.set_yscale(scale_str)
+
+def xscale(scale_str):
+ """
+ Set the type of scale of the x axis
+
+ @param scale_str :: either 'linear' for linear scale or 'log' for logarithmic scale
+ """
+ ax = __last_fig()._axes
+ ax.set_xscale(scale_str)
+
+def grid(opt='on'):
+ """
+ Enable a grid on the active plot (horizontal and vertical)
+
+ @param title :: 'on' to enable
+ """
+ l = __last_fig()._graph.activeLayer()
+ if None == opt or 'on' == opt:
+ l.showGrid()
+ elif 'off' == opt:
+ # TODO is there support for a 'hideGrid' in qti? Apparently not.
+ print "Sorry, hiding/disabling grids is currenlty not supported"
+
+def figure(num=None):
+ """
+ Return Figure object for a new figure or an existing one (if there is any
+ with the number passed as parameter).
+
+ @param num :: figure number (optional). If empty, a new figure is created.
+ """
+ if not num:
+ return __empty_fig()
+ else:
+ if num < 0:
+ raise ValueError("The figure number must be >= 0")
+
+ return Figure(num)
+
+def savefig(name):
+ """
+ Save current plot into a file. The format is guessed from the file extension (.eps, .png, .jpg, etc.)
+
+ @param name :: file name
+ """
+ if not name:
+ raise ValueError("Error: you need to specify a non-empty file name")
+ l = __last_fig()._graph.activeLayer()
+ l.saveImage(name);
diff --git a/Code/Mantid/MantidPlot/pymantidplot/qtiplot.py b/Code/Mantid/MantidPlot/pymantidplot/qtiplot.py
new file mode 100644
index 000000000000..da3c599d2ca2
--- /dev/null
+++ b/Code/Mantid/MantidPlot/pymantidplot/qtiplot.py
@@ -0,0 +1,34 @@
+"""
+MantidPlot module with functions specific to the traditional,
+qti-based MantidPlot Python plotting interface
+
+As with other MantidPlot modules, this has to run from within MantidPlot
+
+"""
+# Require MantidPlot
+try:
+ import _qti
+except ImportError:
+ raise ImportError('The "mantidplot.qti" module can only be used from within MantidPlot.')
+
+import pymantidplot
+
+#-----------------------------------------------------------------------------
+# Intercept qtiplot "plot" command and forward to plotSpectrum for a workspace
+#
+# This function has been moved inside qtiplot when pymantidplot.pyplot (which
+# has another plot() function) was imported into the standard MantidPlot namespace
+def plot(source, *args, **kwargs):
+ """Create a new plot given a workspace, table or matrix.
+
+ Args:
+ source: what to plot; if it is a Workspace, will
+ call plotSpectrum()
+
+ Returns:
+ A handle to the created Graph widget.
+ """
+ if hasattr(source, '_getHeldObject') and isinstance(source._getHeldObject(), QtCore.QObject):
+ return pymantidplot.proxies.new_proxy(proxies.Graph,_qti.app.plot, source._getHeldObject(), *args, **kwargs)
+ else:
+ return pymantidplot.plotSpectrum(source, *args, **kwargs)
diff --git a/Code/Mantid/MantidPlot/test/MantidPlotPyplotGeneralTest.py b/Code/Mantid/MantidPlot/test/MantidPlotPyplotGeneralTest.py
new file mode 100644
index 000000000000..23ac68d43e1c
--- /dev/null
+++ b/Code/Mantid/MantidPlot/test/MantidPlotPyplotGeneralTest.py
@@ -0,0 +1,229 @@
+"""General tests for the basic interface of mantidplot.pyplot
+
+Tests correct creation of output lines from plots (with correct
+Figure, Graph, etc. data), and proper handling (exception) of wrong
+input parameters. Tests plotting of normal arrays and workspaces with the following tools ('tool' kwarg): plot_spectrum, plot_bin, plot_
+
+"""
+import mantidplottests
+from mantidplottests import *
+import time
+import numpy as np
+from PyQt4 import QtGui, QtCore
+
+# =============== Create fake workspaces to plot =======================
+X1 = np.linspace(0,10, 100)
+Y1 = 1000*(np.sin(X1)**2) + X1*10
+X1 = np.append(X1, 10.1)
+
+X2 = np.linspace(2,12, 100)
+Y2 = 500*(np.cos(X2/2.)**2) + 20
+X2 = np.append(X2, 12.10)
+
+X = np.append(X1, X2)
+Y = np.append(Y1, Y2)
+E = np.sqrt(Y)
+
+# this one has 2 spectra
+WorkspaceName2D = 'fake ws'
+CreateWorkspace(OutputWorkspace=WorkspaceName2D, DataX=list(X), DataY=list(Y), DataE=list(E), NSpec=2,
+ UnitX="TOF", YUnitLabel="Counts", WorkspaceTitle="Test/faked data Workspace, 2 spectra")
+
+sec_X3 = np.linspace(2,12, 100)
+sec_Y3 = 200*(np.tan(sec_X3/2.4)**2) + 15
+sec_X3 = np.append(sec_X3, 12.10)
+
+sec_X = np.append(X, sec_X3)
+sec_Y = np.append(Y, sec_Y3)
+sec_E = np.power(sec_Y, 0.6)
+
+# this one has 3 spectra
+SecondWorkspaceName2D = 'another fake ws'
+CreateWorkspace(OutputWorkspace=SecondWorkspaceName2D, DataX=list(sec_X), DataY=list(sec_Y), DataE=list(sec_E), NSpec=3,
+ UnitX="TOF", YUnitLabel="Counts", WorkspaceTitle="Test/faked data Workspace, 3 spectra")
+
+# plot_md needs an MD workspace with a single non-integrated dimension
+MDWWorkspaceName = 'mdw'
+mdSignal = np.sin(range(0,100,1))
+errInput = mdSignal/20.5
+CreateMDHistoWorkspace(Dimensionality="1", Names='x', Units='m', Extents='0,10', NumberOfBins=len(mdSignal), SignalInput=mdSignal, ErrorInput=errInput, OutputWorkspace=MDWWorkspaceName)
+
+class MantidPlotPyplotGeneralTest(unittest.TestCase):
+
+ def setUp(self):
+ self.g = None
+
+ def tearDown(self):
+ """Clean up by closing the created window """
+ windows = self.g
+ if not self.g:
+ return
+ if type(self.g) != list:
+ windows = [self.g]
+ for window in windows:
+ self.close_win_by_graph(window)
+
+ def close_win_by_graph(self, g):
+ if None != g:
+ g.confirmClose(False)
+ g.close()
+ QtCore.QCoreApplication.processEvents()
+
+ def test_nothing(self):
+ return True
+
+ def check_output_lines(self, lines, expected_len):
+ """ Check that the lines returned by a plot are correctly built """
+ self.assertTrue(expected_len==len(lines))
+ for i in range(0, len(lines)):
+ self.assertTrue(isinstance(lines[i], Line2D))
+ self.assertTrue(isinstance(lines[i].figure(), Figure))
+ self.assertTrue(isinstance(lines[i]._graph, proxies.Graph))
+ self.assertTrue(isinstance(lines[i].figure()._graph, proxies.Graph))
+
+ def close_win(self, lines):
+ if len(lines) > 0:
+ self.close_win_by_graph(lines[0]._graph)
+
+ def test_plot_spectrum_ok(self):
+ lines_spec = plot_spectrum(WorkspaceName2D, [0, 1])
+ self.check_output_lines(lines_spec, 2)
+ self.close_win(lines_spec)
+
+ tool_names = ['plot_spectrum', 'plot_sp', 'spectrum', 'sp']
+ for tname in tool_names:
+ lines = plot(WorkspaceName2D, [0, 1], tool=tname)
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ if self.assertTrue(len(lines) == len(lines_spec)):
+ for i in range(0, len(lines)):
+ self.assertEqual(lines[i].get_xdata(), lines_spec[i].get_xdata())
+ self.assertEqual(lines[i].get_ydata(), lines_spec[i].get_ydata())
+
+ def test_plot_bin_ok(self):
+ lines_bin = plot_bin(WorkspaceName2D, [0, 1, 2])
+ self.check_output_lines(lines_bin, 3)
+ self.close_win(lines_bin)
+
+ tool_names = ['plot_bin', 'bin']
+ for tname in tool_names:
+ lines = plot(WorkspaceName2D, [0, 1, 2], tool=tnames)
+ self.check_output_lines(lines, 3)
+ self.close_win(lines)
+
+ if self.assertTrue(len(lines) == len(lines_bin)):
+ for i in range(0, len(lines)):
+ self.assertEqual(lines[i].get_xdata(), lines2_bin[i].get_xdata())
+ self.assertEqual(lines[i].get_ydata(), lines2_bin[i].get_ydata())
+
+ def test_lines_get_data(self):
+ y = [0.2, 0.5, 0.1, 0.6]
+ # note this assumes that plot will make a dummy workspace using 0,1,2... as X
+ x = range(0, len(y), 1)
+
+ lines = plot(y)
+ self.check_output_lines(lines, 1)
+ # and here also check the values
+ if 1==len(lines):
+ self.assertEqual(lines[0].get_xdata(), x)
+ self.assertEqual(lines[0].get_ydata(), y)
+ self.close_win(lines)
+
+ def test_plot_md_ok(self):
+ lines = plot_md(MDWWorkspaceName)
+ self.assertEqual(len(lines), 1)
+ self.close_win(lines)
+
+ tool_names = ['plot_md', 'md']
+ for tnames in tool_names:
+ lines = plot(MDWWorkspaceName, tool='plot_md')
+ self.assertEqual(len(lines), 1)
+ self.close_win(lines)
+
+ # now see what happens with non-md workspaces
+ try:
+ self.assertRaises(ValueError, plot(WorkspaceName2D, tool='plot_md'), "won't see this")
+ except:
+ print "Failed, as it should"
+
+ try:
+ self.assertRaises(ValueError, plot_md(WorkspaceName2D), "won't see this")
+ except:
+ print "Failed, as it should"
+
+ def test_plot_array_ok(self):
+ val = [] # empty data, will be replaced with a dummy (0,0) and generate a 'point' line
+ lines = plot(val)
+ self.check_output_lines(lines, 1)
+ self.close_win(lines)
+
+ def test_plot_with_more_functions(self):
+ lines = plot(WorkspaceName2D, [0,1], tool='plot_spectrum', linewidth=2, linestyle='--', marker='v')
+ xlim(0, 1)
+ ylim(0, 1)
+ xlabel('X foo label')
+ ylabel('Y bar label')
+ title('baz title')
+ axis([0, 1, 0, 1])
+ grid('off')
+ grid('on')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ def test_plot_with_style_args(self):
+ # note that these tests also use various aliases for the tool name 'plot_spectrum'
+ # Not adding all the possible combinations here, as this suite is becoming time consuming
+ lines = plot(WorkspaceName2D, [0,1], '--g', tool='plot_spectrum')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ lines = plot(WorkspaceName2D, [0,1], 'y:>', tool='plot_sp')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ def test_plot_with_style_args_and_kwargs(self):
+ lines = plot(WorkspaceName2D, [0,1], '-.m', tool='spectrum')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ lines = plot(WorkspaceName2D, [0,1], 'r-.>', tool='sp')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ def test_plot_with_kwargs(self):
+ lines = plot(WorkspaceName2D, [0,1], tool='plot_spectrum', linewidth=3, linestyle='-.', marker='v')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ lines = plot(WorkspaceName2D, [0,1], tool='plot_sp', linewidth=3, linestyle='-.', marker='v')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ lines = plot(SecondWorkspaceName2D, [0,1, 2], tool='sp', linewidth=3, linestyle='-.', marker='v')
+ self.check_output_lines(lines, 3)
+ self.close_win(lines)
+
+ def test_wrong_kwargs(self):
+ # funny kwargs should have no big consequences
+ lines = plot(WorkspaceName2D, [0,1], tool='plot_spectrum', linewidth=3, linestyle='-.', marker='v', funny_foo='bar', funny_baz='qux')
+ self.check_output_lines(lines, 2)
+ self.close_win(lines)
+
+ def test_multi_plot_commands(self):
+ lines = plot(WorkspaceName2D, [0,1], SecondWorkspaceName2D, [0, 1, 2])
+ self.check_output_lines(lines, 5)
+ self.close_win(lines)
+
+ lines = plot(WorkspaceName2D, [0,1], SecondWorkspaceName2D, [0, 1, 2])
+ self.check_output_lines(lines, 5)
+ self.close_win(lines)
+
+ # this one mixes up positional and kw args
+ try:
+ self.assertRaises(ValueError, plot(WorkspaceName2D, [0,1], WorkspaceName2D, tool='plot_spectrum'), "wont see this")
+ except:
+ print "Failed, as it should"
+
+# Run the unit tests
+mantidplottests.runTests(MantidPlotPyplotGeneralTest)