NWChem/contrib/python/Gnuplot.py
2021-10-30 02:11:52 +02:00

1043 lines
38 KiB
Python

#!/usr/local/bin/python -t
# $Id$
# Gnuplot.py -- A pipe-based interface to the gnuplot plotting program.
# Copyright (C) 1998 Michael Haggerty <mhagger@blizzard.harvard.edu>.
"""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 <http://www.fsf.org/copyleft/gpl.html>, or by writing to
the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
Boston, MA 02111-1307, USA.
Written by Michael Haggerty <mhagger@blizzard.harvard.edu>. Inspired
by and partly derived from an earlier version by Konrad Hinsen
<hinsen@ibs.ibs.fr>. If you find a problem or have a suggestion,
please let me know at <mhagger@blizzard.harvard.edu>. 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
<http://www.cs.dartmouth.edu/gnuplot_info.html>.
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.
'<file>' 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=<n>' -- plot that column against line number
'using=<tuple>' -- plot using a:b:c:d etc.
'using=<string>' -- plot `using <string>' (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=<tuple> -- 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])))