Newer
Older
"""============================================================================
Federico Montesino Pouzols
committed
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
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
Federico Montesino Pouzols
committed
the plotSpectrum function of the traditional mantidplot module. This is
Federico Montesino Pouzols
committed
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])
Federico Montesino Pouzols
committed
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
========================
Different types of plots
========================
The plot() function provides a unified interface to different types of
plots, including specific graphs of spectra, bins, multidimensional
workspaces, etc. These specific types of plots are explained in the
next sections. plot() makes a guess as to what tool to use to plot a
workspace. For example, if you pass an MD workspace it will make an MD
plot. But you can request a specific type of plot by specifying a
keyword argument ('tool'). The following tools (or different types of
plots) are supported:
+------------------------+------------------------------------------------------------+-----------------------+
| Tool | tool= parameter values (all are equivalent aliases) | Old similar function |
+========================+============================================================+=======================+
| plot spectra (default) | 'plot_spectrum', 'spectrum', 'plot_sp', 'sp' | plotSpectrum |
+------------------------+------------------------------------------------------------+-----------------------+
| plot bins | 'plot_bin', 'bin' | plotBin |
+------------------------+------------------------------------------------------------+-----------------------+
| plot MD | 'plot_md', 'md' | plotMD |
+------------------------+------------------------------------------------------------+-----------------------+
The last column of the table lists the functions that produce similar
plots in the traditional MantidPlot Python plotting interface. For the
time being this module only supports these types of specific
plots. Note that the traditional plotting interface of MantidPlot
provides support for many more specific types of plots. These or
similar ones will be added in this module in future releases:
* plot2D
* plot3D
* plotSlice
* instrumentWindow
* waterFallPlot
* mergePlots
* stemPlot
Federico Montesino Pouzols
committed
Plot spectra using workspace objects and workspace names
--------------------------------------------------------
It is also possible to pass workspace names to plot, as in the
Federico Montesino Pouzols
committed
following example where we plot a few spectra:
Federico Montesino Pouzols
committed
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
.. 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
Federico Montesino Pouzols
committed
that choice more explicit by specifying the 'tool' argument. In this
case we use 'plot_spectrum' (which also has shorter aliases:
'spectrum', or simply 'sp' as listed in the table above):
Federico Montesino Pouzols
committed
.. code-block:: python
plot(['MAR11060', loq], [800, 900], tool='plot_spectrum')
Federico Montesino Pouzols
committed
plot(['MAR11060', loq], [801, 901], tool='sp')
Federico Montesino Pouzols
committed
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
Federico Montesino Pouzols
committed
'plot_bin' (or equivalent 'bin'), like this:
Federico Montesino Pouzols
committed
.. 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
Federico Montesino Pouzols
committed
the value 'plot_md' (or 'md' as a short alias), like this:
Federico Montesino Pouzols
committed
.. 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.
Federico Montesino Pouzols
committed
=========================
Federico Montesino Pouzols
committed
Changing style properties
Federico Montesino Pouzols
committed
=========================
Federico Montesino Pouzols
committed
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
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 | None/"None" 'o', 'v', '^', '<', '>', 's', '*', |
| | 'h', '|', '_' |
Federico Montesino Pouzols
committed
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
+------------+---------------------------------------------------------+
|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 <http://www.gnu.org/licenses/>.
#
# File change history is stored at: <https://github.com/mantidproject/mantid>.
# Code Documentation is available at: <http://doxygen.mantidproject.org>
from __future__ import (absolute_import, division,
Federico Montesino Pouzols
committed
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))
try:
from mantid.simpleapi import CreateWorkspace
except ImportError:
def CreateWorkspace(*args, **kwargs):
raise RuntimeError("CreateWorkspace function not available")
import mantidplot
from six import string_types
Federico Montesino Pouzols
committed
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
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 = self._graph.activeLayer()
l.export(name);
Federico Montesino Pouzols
committed
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
@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
Federico Montesino Pouzols
committed
Returns :: True if the argument is a python or numpy list
"""
return isinstance(arg, list) or isinstance(arg, np.ndarray)
Federico Montesino Pouzols
committed
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, string_types) and mtd.doesExist(arg) and isinstance(mtd[arg], IMDWorkspace))
Federico Montesino Pouzols
committed
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
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
Federico Montesino Pouzols
committed
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
Federico Montesino Pouzols
committed
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
Federico Montesino Pouzols
committed
Returns :: Workspace
Federico Montesino Pouzols
committed
alg = AlgorithmManager.create("CreateWorkspace")
alg.setChild(True)
Federico Montesino Pouzols
committed
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)
Federico Montesino Pouzols
committed
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
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 = {
None: _qti.PlotSymbol.NoSymbol, "None": _qti.PlotSymbol.NoSymbol,
Federico Montesino Pouzols
committed
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
'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