#!/usr/local/bin/python -t # $Id$ # Gnuplot.py -- A pipe-based interface to the gnuplot plotting program. # Copyright (C) 1998 Michael Haggerty . """A pipe-based interface to the gnuplot plotting program. This program 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 2 of the License, or (at your option) any later version. This program 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; it is available at , or by writing to the Free Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. Written by Michael Haggerty . Inspired by and partly derived from an earlier version by Konrad Hinsen . If you find a problem or have a suggestion, please let me know at . Other feedback is also welcome. For information about how to use this module, see the comments below, the documentation string for class Gnuplot, and the test code at the bottom of the file. You can run the test code by typing 'python Gnuplot.py'. You should import this file with 'import Gnuplot', not with 'from Gnuplot import *'; otherwise you will have problems with conflicting names (specifically, the Gnuplot module name conflicts with the Gnuplot class name). To obtain gnuplot itself, see . Features: o Allows the creation of two or three dimensional plots from python by piping commands to the 'gnuplot' program. o A gnuplot session is an instance of class 'Gnuplot', so multiple sessions can be open at once: 'g1 = Gnuplot.Gnuplot(); g2 = Gnuplot.Gnuplot()' o The implicitly-generated gnuplot commands can be stored to a file instead of executed immediately: 'g = Gnuplot.Gnuplot("commands.gnuplot")' The file can then be run later with gnuplot's 'load' command. Beware, however, if the plot commands depend on the existence of temporary files, because they might be deleted before you use the command file. o Can pass arbitrary commands to the gnuplot command interpreter: 'g("set pointsize 2")' o A Gnuplot object knows how to plot objects of type 'PlotItem'. Any PlotItem can have optional `title' and/or 'with' suboptions. Builtin PlotItem types: * 'Data(array1)' -- data from a Python list or NumPy array (permits additional option 'cols') * 'File("filename")' -- data from an existing data file (permits additional option 'using') * 'Func("exp(4.0 * sin(x))")' -- functions (passed as a string for gnuplot to evaluate) * 'GridData(m, x, y)' -- data tabulated on a grid of (x,y) values (usually to be plotted in 3-D) See those classes for more details. o PlotItems are implemented as objects that can be assigned to variables (including their options) and plotted repeatedly --- this also saves much of the overhead of plotting the same data multiple times. o Communication of data between python and gnuplot is via temporary files, which are deleted automatically when their associated 'PlotItem' is deleted. (Communication of commands is via a pipe.) The PlotItems currently in use by a Gnuplot object are stored in an internal list so that they won't be deleted prematurely. o Can use 'replot' method to add datasets to an existing plot. o Can make persistent gnuplot windows by using the constructor option `persist=1'. Such windows stay around even after the gnuplot program is exited. Note that only newer version of gnuplot support this option. o Plotting to a postscript file is via new 'hardcopy' method, which outputs the currently-displayed plot to either a postscript printer or to a postscript file. o There is a 'plot' command which is roughly compatible with the command from Konrad Hinsen's old 'Gnuplot.py'. Restrictions: - Relies on the numpy Python extension. This can be obtained from LLNL (See ftp://ftp-icf.llnl.gov/pub/python/README.html). If you're interested in gnuplot, you would probably also want NumPy anyway. - Probably depends on a unix-type environment. Anyone who wants to remedy this situation should contact me. - Only a small fraction of gnuplot functionality is implemented as explicit method functions. However, you can give arbitrary commands to gnuplot manually; for example: 'g = Gnuplot.Gnuplot()', 'g('set style data linespoints')', 'g('set pointsize 5')', etc. I might add a more organized way of setting arbitrary options. - There is no provision for missing data points in array data (which gnuplot would allow by specifying '?' as a data point). I can't think of a clean way to implement this; maybe one could use NaN for machines that support IEEE floating point. - There is no supported way to change the plotting options of PlotItems after they have been created. Bugs: - No attempt is made to check for errors reported by gnuplot (but they will appear on stderr). - All of these classes perform their resource deallocation when '__del__' is called. If you delete things explicitly, there will be no problem. If you don't, an attempt is made to delete remaining objects when the interpreter is exited, but this is not completely reliable, so sometimes temporary files will be left around. If anybody knows how to fix this problem, please let me know. """ __version__ = "1.1a" __cvs_version__ = "CVS version $Revision: 1.1 $" import sys, os, string, tempfile, numpy # Set after first call of test_persist(). This will be set from None # to 0 or 1 upon the first call of test_persist(), then the stored # value will be used thereafter. To avoid the test, type 1 or 0 on # the following line corresponding to whether your gnuplot is new # enough to understand the -persist option. _recognizes_persist = None # After a hardcopy is produced, we have to set the terminal type back # to `on screen'. If you are using unix, then `x11' is probably # correct. If not, change the following line to the terminal type you # use. _default_term = 'x11' # Gnuplot can plot to a printer by using `set output "| ..."' where # ... is the name of a program that sends its stdin to a printer. On # my machine the appropriate program is `lpr', as set below. On your # computer it may be something different (like `lp'); you can set that # by changing the variable below. You can also use the following # variable to add options to the print command. _default_lpr = '| lpr' def test_persist(): """Test and report whether gnuplot recognizes the option '-persist'. Test if gnuplot is new enough to know the option '-persist'. It it isn't, it will emit an error message with '-persist' in the first line. """ global _recognizes_persist if _recognizes_persist is None: g = os.popen('echo | gnuplot -persist 2>&1', 'r') response = g.readlines() g.close() _recognizes_persist = ((not response) or (string.find(response[0], '-persist') == -1)) return _recognizes_persist class OptionException(Exception): """raised for unrecognized option(s)""" pass class DataException(Exception): """raised for data in the wrong format""" pass class PlotItem: """Plotitem represents an item that can be plotted by gnuplot. For the finest control over the output, you can create the PlotItems yourself with additional keyword options, or derive new classes from PlotItem. Members: 'basecommand' -- a string holding the elementary argument that must be passed to gnuplot's `plot' command for this item; e.g., 'sin(x)' or '"filename.dat"'. 'options' -- a list of strings that need to be passed as options to the plot command, in the order required; e.g., ['title "data"', 'with linespoints']. 'title' -- the title requested (undefined if not requested). Note that `title=None' implies the `notitle' option, whereas omitting the title option implies no option (the gnuplot default is then used). 'with' -- the string requested as a `with' option (undefined if not requested) """ def __init__(self, basecommand, **keyw): self.basecommand = basecommand self.options = [] if keyw.has_key('title'): self.title = keyw['title'] del keyw['title'] if self.title is None: self.options.append('notitle') else: self.options.append('title "' + self.title + '"') if keyw.has_key('with_'): self.with_ = keyw['with_'] del keyw['with_'] self.options.append('with ' + self.with_) if keyw: raise OptionException(keyw) def command(self): """Build the 'plot' command to be sent to gnuplot. Build and return the 'plot' command, with options, necessary to display this item. """ if self.options: return self.basecommand + ' ' + string.join(self.options) else: return self.basecommand # if the plot command requires data to be put on stdin (i.e., # `plot "-"'), this method should put that data there. def pipein(self, file): pass class Func(PlotItem): """Represents a mathematical expression to plot. Func represents a mathematical expression that is to be computed by gnuplot itself, as in the example gnuplot> plot sin(x) The argument to the constructor is a string which is a expression. Example: g.plot(Func("sin(x)", with_="line 3")) or the shorthand example: g.plot("sin(x)") """ def __init__(self, funcstring, **keyw): apply(PlotItem.__init__, (self, funcstring), keyw) class AnyFile: """An AnyFile represents any kind of file to be used by gnuplot. An AnyFile represents a file, but presumably one that holds data in a format readable by gnuplot. This class simply remembers the filename; the existence and format of the file are not checked whatsoever. Note that this is not a PlotItem, though it is used by the 'File' PlotItem. Members: 'self.filename' -- the filename of the file """ def __init__(self, filename): self.filename = filename class TempFile(AnyFile): """A TempFile is a file that is automatically deleted. A TempFile points to a file. The file is deleted automatically when the TempFile object is deleted. WARNING: whatever filename you pass to this constructor **WILL BE DELETED** when the TempFile object is deleted, even if it was a pre-existing file! This is intended to be used as a parent class of TempArrayFile. """ def __del__(self): os.unlink(self.filename) def write_array(f, set, item_sep=' ', nest_prefix='', nest_suffix='\n', nest_sep=''): """Write an array of arbitrary dimension to a file. A general recursive array writer. The last four parameters allow a great deal of freedom in choosing the output format of the array. The defaults for those parameters give output that is gnuplot-readable. But using, for example, ( ',', '{', '}', ',\\n') would output an array in a format that Mathematica could read. item_sep should not contain '%' (or if it does, it should be escaped to '%%') since item_sep is put into a format string. """ if len(set.shape) == 1: (columns,) = set.shape assert columns > 0 fmt = string.join(['%s'] * columns, item_sep) f.write(nest_prefix) f.write(fmt % tuple(set.tolist())) f.write(nest_suffix) elif len(set.shape) == 2: # This case could be done with recursion, but `unroll' for # efficiency. (points, columns) = set.shape assert points > 0 assert columns > 0 fmt = string.join(['%s'] * columns, item_sep) f.write(nest_prefix + nest_prefix) f.write(fmt % tuple(set[0].tolist())) f.write(nest_suffix) for point in set[1:]: f.write(nest_sep + nest_prefix) f.write(fmt % tuple(point.tolist())) f.write(nest_suffix) f.write(nest_suffix) else: assert set.shape[0] > 0 f.write(nest_prefix) write_array(f, set[0], item_sep, nest_prefix, nest_suffix, nest_sep) for subset in set[1:]: f.write(nest_sep) write_array(f, subset, item_sep, nest_prefix, nest_suffix, nest_sep) f.write(nest_suffix) class ArrayFile(AnyFile): """A file to which, upon creation, an array is written. When an ArrayFile is constructed, it creates a file and fills it with the contents of a 2-d or 3-d numpy array in the format expected by gnuplot. Specifically, for 2-d, the file organization is for example: set[0,0] set[0,1] ... set[1,0] set[1,1] ... etc. For 3-d, it is for example: set[0,0,0] set[0,0,1] ... set[0,1,0] set[0,1,1] ... set[1,0,0] set[1,0,1] ... set[1,1,0] set[1,1,1] ... etc. The filename can be specified, otherwise a random filename is chosen. The file is NOT deleted automatically. """ def __init__(self, set, filename=None): if not filename: filename = tempfile.mktemp() f = open(filename, 'w') write_array(f, set) f.close() AnyFile.__init__(self, filename) class TempArrayFile(ArrayFile, TempFile): """An ArrayFile that is deleted automatically.""" def __init__(self, set, filename=None): ArrayFile.__init__(self, set, filename) class File(PlotItem): """A PlotItem representing a file that contains gnuplot data. File is a PlotItem that represents a file that should be plotted by gnuplot. The file can either be a string holding the filename of an existing file, or it can be anything derived from 'AnyFile'. """ def __init__(self, file, using=None, **keyw): """Construct a File object. '' can be either a string holding the filename of an existing file, or it can be an object of a class derived from 'AnyFile' (such as a 'TempArrayFile'). Keyword arguments recognized (in addition to those recognized by 'PlotItem'): 'using=' -- plot that column against line number 'using=' -- plot using a:b:c:d etc. 'using=' -- plot `using ' (allows gnuplot's arbitrary column arithmetic) Note that the 'using' option is interpreted by gnuplot, so columns must be numbered starting with 1. Other keyword arguments are passed along to PlotItem. The default 'title' for an AnyFile PlotItem is 'notitle'. """ if isinstance(file, AnyFile): self.file = file # If no title is specified, then use `notitle' for # TempFiles (to avoid using the temporary filename as the # title.) if isinstance(file, TempFile) and not keyw.has_key('title'): keyw['title'] = None elif type(file) == type(""): self.file = AnyFile(file) else: raise OptionException apply(PlotItem.__init__, (self, '"' + self.file.filename + '"'), keyw) self.using = using if self.using is None: pass elif type(self.using) == type(""): self.options.insert(0, "using " + self.using) elif type(self.using) == type(()): self.options.insert(0, "using " + string.join(map(repr, self.using), ':')) elif type(self.using) == type(1): self.options.insert(0, "using " + repr(self.using)) else: raise OptionException('using=' + repr(self.using)) class Data(File): """Allows data from memory to be plotted with Gnuplot. Takes a numeric array from memory and outputs it to a temporary file that can be plotted by gnuplot. """ def __init__(self, *set, **keyw): """Construct a Data object from a numeric array. Create a Data object (which is a type of PlotItem) out of one or more Float Python numpy arrays (or objects that can be converted to a Float numpy array). If the routine is passed one array, the last index ranges over the values comprising a single data point (e.g., [x, y, and sigma]) and the rest of the indices select the data point. If the routine is passed more than one array, they must have identical shapes, and then each data point is composed of one point from each array. I.e., 'Data(x,x**2)' is a PlotItem that represents x squared as a function of x. For the output format, see the comments in ArrayFile. The array is first written to a temporary file, then that file is plotted. Keyword arguments recognized (in addition to those recognized by PlotItem): cols= -- write only the specified columns from each data point to the file. Since cols is used by python, the columns should be numbered in the python style (starting from 0), not the gnuplot style (starting from 1). The data are immediately written to the temp file; no copy is kept in memory. """ if len(set) == 1: # set was passed as a single structure set = numpy.asarray(set, dtype=numpy.float32) else: # set was passed column by column (for example, Data(x,y)) set = numpy.asarray(set, dtype=numpy.float32) dims = len(set.shape) # transpose so that the last index selects x vs. y: set = numpy.transpose(set, (dims-1,) + tuple(range(dims-1))) if keyw.has_key('cols') and keyw['cols'] is not None: set = numpy.take(set, keyw['cols'], -1) del keyw['cols'] apply(File.__init__, (self, TempArrayFile(set)), keyw) class GridData(File): """Holds data representing a function of two variables, for use in splot. GridData represents a function that has been tabulated on a rectangular grid. It is a PlotItem, so GridData objects can be plotted by Gnuplot. The data are written to a file but not stored in memory. """ def __init__(self, data, xvals=None, yvals=None, **keyw): """GridData constructor. Arguments: 'data' -- a 2-d array with dimensions (numx,numy) 'xvals' -- a 1-d array with dimension (numx) 'yvals' -- a 1-d array with dimension (numy) 'data' is meant to hold the values of a function f(x,y) tabulated on a grid of points, such that 'data[i,j] == f(xvals[i], yvals[j])'. These data are written to a datafile as 'x y f(x,y)' triplets that can be used by gnuplot's splot command. Thus if you have three arrays in the above format and a Gnuplot instance called g, you can plot your data by typing for example: g.splot(Gnuplot.GridData(data,xvals,yvals)) If 'xvals' and/or 'yvals' are omitted, integers (starting with 0) are used for that coordinate. The data are written to a temporary file; no copy of the data is kept in memory. """ data = numpy.asarray(data, dtype=numpy.float32) assert len(data.shape) == 2 (numx, numy) = data.shape if xvals is None: xvals = numpy.arange(numx) else: xvals = numpy.asarray(xvals, dtype=numpy.float32) assert len(xvals.shape) == 1 assert xvals.shape[0] == numx if yvals is None: yvals = numpy.arange(numy) else: yvals = numpy.asarray(yvals, dtype=numpy.float32) assert len(yvals.shape) == 1 assert yvals.shape[0] == numy set = numpy.transpose( numpy.array( (numpy.transpose(numpy.resize(xvals, (numy, numx))), numpy.resize(yvals, (numx, numy)), data)), (1,2,0)) apply(File.__init__, (self, TempArrayFile(set)), keyw) def grid_function(f, xvals, yvals): """Compute a function on a grid. 'xvals' and 'yvals' should be 1-D arrays listing the values of x and y at which f should be tabulated. f should be a function taking two floating point arguments. The return value is a matrix M where M[i,j] = f(xvals[i],yvals[j]), which can for example be used in the 'GridData' constructor. Note that f is evaluated at each pair of points using a Python loop, which can be slow if the number of points is large. If speed is an issue, you are better off computing functions matrix-wise using numpy's built-in ufuncs. """ m = numpy.zeros((len(xvals), len(yvals)), dtype=numpy.float32) for xi in range(len(xvals)): x = xvals[xi] for yi in range(len(yvals)): y = yvals[yi] m[xi,yi] = f(x,y) return m class Gnuplot: """gnuplot plotting object. A Gnuplot represents a running gnuplot program and a pipe to communicate with it. It keeps a reference to each of the PlotItems used in the current plot, so that they (and their associated temporary files) are not deleted prematurely. The communication is one-way; gnuplot's text output just goes to stdout with no attempt to check it for error messages. Members: 'gnuplot' -- the pipe to gnuplot or a file gathering the commands 'itemlist' -- a list of the PlotItems that are associated with the current plot. These are deleted whenever a new plot command is issued via the `plot' method. 'debug' -- if this flag is set, commands sent to gnuplot will also be echoed to stderr. 'plotcmd' -- 'plot' or 'splot', depending on what was the last plot command. Methods: '__init__' -- if a filename argument is specified, the commands will be written to that file instead of being piped to gnuplot immediately. 'plot' -- clear the old plot and old PlotItems, then plot the arguments in a fresh plot command. Arguments can be: a PlotItem, which is plotted along with its internal options; a string, which is plotted as a Func; or anything else, which is plotted as a Data. 'hardcopy' -- replot the plot to a postscript file (if filename argument is specified) or pipe it to lpr otherwise. If the option `color' is set to true, then output color postscript. 'replot' -- replot the old items, adding any arguments as additional items as in the plot method. 'refresh' -- issue (or reissue) the plot command using the current PlotItems. '__call__' -- pass an arbitrary string to the gnuplot process, followed by a newline. 'xlabel', 'ylabel', 'title' -- set attribute to be a string. 'interact' -- read lines from stdin and send them, one by one, to the gnuplot interpreter. Basically you can type commands directly to the gnuplot command processor (though without command-line editing). 'load' -- load a file (using the gnuplot `load' command). 'save' -- save gnuplot commands to a file (using gnuplot `save' command) If any of the PlotItems is a temporary file, it will be deleted at the usual time and the save file might be pretty useless :-). 'clear' -- clear the plot window (but not the itemlist). 'reset' -- reset all gnuplot settings to their defaults and clear the current itemlist. 'set_string' -- set or unset a gnuplot option whose value is a string. '_clear_queue' -- clear the current PlotItem list. '_add_to_queue' -- add the specified items to the current PlotItem list. """ def __init__(self, filename=None, persist=0, debug=0): """Create a Gnuplot object. 'Gnuplot(filename=None, persist=0, debug=0)': Create a 'Gnuplot' object. By default, this starts a gnuplot process and prepares to write commands to it. If 'filename' is specified, the commands are instead written to that file (i.e., for later use using 'load'). If 'persist' is set, gnuplot will be started with the '-persist' option (which creates a new X11 plot window for each plot command). (This option is not available on older versions of gnuplot.) If 'debug' is set, the gnuplot commands are echoed to stderr as well as being send to gnuplot. """ if filename: # put gnuplot commands into a file: self.gnuplot = open(filename, 'w') else: if persist: if not test_persist(): raise OptionException( '-persist does not seem to be supported ' 'by your version of gnuplot!') self.gnuplot = os.popen('gnuplot -persist', 'w') else: self.gnuplot = os.popen('gnuplot', 'w') self._clear_queue() self.debug = debug self.plotcmd = 'plot' def __del__(self): self('quit') self.gnuplot.close() def __call__(self, s): """Send a command string to gnuplot. '__call__(s)': send the string s as a command to gnuplot, followed by a newline and flush. All interaction with the gnuplot process is through this method. """ self.gnuplot.write(s + "\n") self.gnuplot.flush() if self.debug: # also echo to stderr for user to see: sys.stderr.write("gnuplot> %s\n" % (s,)) def refresh(self): """Refresh the plot, using the current PlotItems. Refresh the current plot by reissuing the gnuplot plot command corresponding to the current itemlist. """ plotcmds = [] for item in self.itemlist: plotcmds.append(item.command()) self(self.plotcmd + ' ' + string.join(plotcmds, ', ')) for item in self.itemlist: item.pipein(self.gnuplot) def _clear_queue(self): """Clear the PlotItems from the queue.""" self.itemlist = [] def _add_to_queue(self, items): """Add a list of items to the itemlist, but don't plot them. 'items' is a list or tuple of items, each of which should be a 'PlotItem' of some kind, a string (interpreted as a function string for gnuplot to evaluate), or a numpy array (or something that can be converted to a numpy array). """ for item in items: if isinstance(item, PlotItem): self.itemlist.append(item) elif type(item) is type(""): self.itemlist.append(Func(item)) else: # assume data is an array: self.itemlist.append(Data(item)) def plot(self, *items): """Draw a new plot. 'plot(item, ...)': Clear the current plot and create a new 2-d plot containing the specified items. Arguments can be of the following types: 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData') -- This is the most flexible way to call plot because the PlotItems can contain suboptions. Moreover, PlotItems can be saved to variables so that their lifetime is longer than one plot command--thus they can be replotted with minimal overhead. 'string' (i.e., "sin(x)") -- The string is interpreted as 'Func(string)' (a function that is computed by gnuplot). Anything else -- The object, which should be convertible to an array, is converted to a Data() item, and thus plotted as data. If the conversion fails, an exception is raised. """ # remove old files: self.plotcmd = 'plot' self._clear_queue() self._add_to_queue(items) self.refresh() def splot(self, *items): """Draw a new three-dimensional plot. 'splot(item, ...)' -- Clear the current plot and create a new 3-d plot containing the specified items. Arguments can be of the following types: 'PlotItem' (e.g., 'Data', 'File', 'Func', 'GridData') -- This is the most flexible way to call plot because the PlotItems can contain suboptions. Moreover, PlotItems can be saved to variables so that their lifetime is longer than one plot command--thus they can be replotted with minimal overhead. 'string' (i.e., "sin(x*y)") -- The string is interpreted as a 'Func()' (a function that is computed by gnuplot). Anything else -- The object is converted to a Data() item, and thus plotted as data. Note that each data point should normally have at least three values associated with it (i.e., x, y, and z). If the conversion fails, an exception is raised. """ # remove old files: self.plotcmd = 'splot' self._clear_queue() self._add_to_queue(items) self.refresh() def replot(self, *items): """Replot the data, possibly adding new PlotItems. Replot the existing graph, using the items in the current itemlist. If arguments are specified, they are interpreted as additional items to be plotted alongside the existing items on the same graph. See 'plot' for details. """ self._add_to_queue(items) self.refresh() def interact(self): """Allow user to type arbitrary commands to gnuplot. Read stdin, line by line, and send each line as a command to gnuplot. End by typing C-d. """ sys.stderr.write("Press C-d to end interactive input\n") while 1: sys.stderr.write("gnuplot>>> ") line = sys.stdin.readline() if not line: break if line[-1] == "\n": line = line[:-1] self(line) def clear(self): """Clear the plot window (without affecting the current itemlist).""" self('clear') def reset(self): """Reset all gnuplot settings to their defaults and clear itemlist.""" self('reset') self.itemlist = [] def load(self, filename): """Load a file using gnuplot's `load' command.""" self('load "%s"' % (filename,)) def save(self, filename): """Save the current plot commands using gnuplot's `save' command.""" self('save "%s"' % (filename,)) def set_string(self, option, s=None): """Set a string option, or if s is omitted, unset the option.""" if s is None: self('set %s' % (option,)) else: self('set %s "%s"' % (option, s)) def xlabel(self, s=None): """Set the plot's xlabel.""" self.set_string('xlabel', s) def ylabel(self, s=None): """Set the plot's ylabel.""" self.set_string('ylabel', s) def title(self, s=None): """Set the plot's title.""" self.set_string('title', s) def hardcopy(self, filename=None, eps=0, color=0, enhanced=1): """Create a hardcopy of the current plot. Create a postscript hardcopy of the current plot. If a filename is specified, save the output in that file; otherwise print it immediately using lpr. If eps is specified, generate encapsulated postscript. If color is specified, create a color plot. If enhanced is specified (the default), then generate enhanced postscript. (Some old gnuplot versions do not support enhanced postscript; if this is the case set enhanced=0.) Note that this command will return immediately even though it might take gnuplot a while to actually finish working. """ if filename is None: filename = _default_lpr setterm = ['set', 'term', 'postscript'] if eps: setterm.append('eps') else: setterm.append(' ') if enhanced: setterm.append('enhanced') if color: setterm.append('color') self(string.join(setterm)) self.set_string('output', filename) self.refresh() self('set term %s' % _default_term) self.set_string('output') # The following is a command defined for compatibility with Hinson's # old Gnuplot.py module. Its use is deprecated. # When the plot command is called and persist is not available, the # plotters will be stored here to prevent their being closed: _gnuplot_processes = [] def plot(*items, **keyw): """plot data using gnuplot through Gnuplot. This command is roughly compatible with old Gnuplot plot command. It is provided for backwards compatibility with the old functional interface only. It is recommended that you use the new object-oriented Gnuplot interface, which is much more flexible. It can only plot numpy array data. In this routine an NxM array is plotted as M-1 separate datasets, using columns 1:2, 1:3, ..., 1:M. Limitations: - If persist is not available, the temporary files are not deleted until final python cleanup. """ newitems = [] for item in items: # assume data is an array: item = numpy.asarray(item, dtype=numpy.float32) dim = len(item.shape) if dim == 1: newitems.append(Data(item[:, numpy.newaxis], with_='lines')) elif dim == 2: if item.shape[1] == 1: # one column; just store one item for tempfile: newitems.append(Data(item, with_='lines')) else: # more than one column; store item for each 1:2, 1:3, etc. tempf = TempArrayFile(item) for col in range(1, item.shape[1]): newitems.append(File(tempf, using=(1,col+1), with_='lines')) else: raise DataException("Data array must be 1 or 2 dimensional") items = tuple(newitems) del newitems if keyw.has_key('file'): g = Gnuplot() # setup plot without actually plotting (so data don't appear # on the screen): g._add_to_queue(items) g.hardcopy(keyw['file']) # process will be closed automatically elif test_persist(): g = Gnuplot(persist=1) apply(g.plot, items) # process will be closed automatically else: g = Gnuplot() apply(g.plot, items) # prevent process from being deleted: _gnuplot_processes.append(g) # Demo code if __name__ == '__main__': from numpy import * import sys # A straightforward use of gnuplot. The `debug=1' switch is used # in these examples so that the commands that are sent to gnuplot # are also output on stderr. g1 = Gnuplot(debug=1) g1.title('A simple example') # (optional) g1('set style data linespoints') # give gnuplot an arbitrary command # Plot a list of (x, y) pairs (tuples or a numpy array would # also be OK): g1.plot([[0.,1.1], [1.,5.8], [2.,3.3], [3.,4.2]]) # Plot one dataset from an array and one via a gnuplot function; # also demonstrate the use of item-specific options: g2 = Gnuplot(debug=1) x = arange(10, dtype=numpy.float32) y1 = x**2 # Notice how this plotitem is created here but used later? This # is convenient if the same dataset has to be plotted multiple # times, because the data need only be written to a temporary file # once. d = Data(x, y1, title="calculated by python", with_="points pointsize 3 pointtype 3") g2.title('Data can be computed by python or gnuplot') g2.xlabel('x') g2.ylabel('x squared') # Plot a function alongside the Data PlotItem defined above: g2.plot(Func("x**2", title="calculated by gnuplot"), d) # Save what we just plotted as a color postscript file: print("\n******** Generating postscript file 'gnuplot_test1.ps' ********\n") g2.hardcopy('gnuplot_test_plot.ps', color=1) # Demonstrate a 3-d plot: g3 = Gnuplot(debug=1) # set up x and y values at which the function will be tabulated: x = arange(35)/2.0 y = arange(30)/10.0 - 1.5 # Make a 2-d array containing a function of x and y. First create # xm and ym which contain the x and y values in a matrix form that # can be `broadcast' into a matrix of the appropriate shape: xm = x[:,newaxis] ym = y[newaxis,:] m = (sin(xm) + 0.1*xm) - ym**2 g3('set style data lines') g3('set hidden') g3('set contour base') g3.xlabel('x') g3.ylabel('y') g3.splot(GridData(m,x,y)) # Delay so the user can see the plots: sys.stderr.write("Three plots should have appeared on your screen " "(they may be overlapping).\n" "Please press return to continue...\n") sys.stdin.readline() # ensure processes and temporary files are cleaned up: del g1, g2, g3, d # Enable the following code to test the old-style gnuplot interface if 0: # List of (x, y) pairs plot([(0.,1),(1.,5),(2.,3),(3.,4)]) # List of y values, file output print("\n Generating postscript file 'gnuplot_test2.ps'\n") plot([1, 5, 3, 4], file='gnuplot_test2.ps') # Two plots; each given by a 2d array x = arange(10, typecode=Float) y1 = x**2 y2 = (10-x)**2 plot(transpose(array([x, y1])), transpose(array([x, y2])))