disipyl Tutorial

Author: Paul M. Magwene
Email: paul.magwene@yale.edu

Overview

The following tutorial leads the user through the creation of disipyl plots using the interactive Python interpreter. I also provide some longer examples in which I show the user how to derive specialized classes and functions to suit particular plotting needs.

You may find it useful to open another browser window with the auto-generated documentation. Doing so will help you gain familiarity with the function and classes which make up disipyl.

The tutorial assumes you've already installed disipyl (and DISLIN) somewhere where the Python interpretter can find it (see the section on installation for more details). I also assume the modules Numeric and RandomArray (both part of the NumPy package) are available

Conventions

Throughout the tutorial we'll be creating and manipulating disipyl objects both from the Python interactive interpretter and in module files. Both interactive sessions and module code will be setoff with grey boxes in a fixed font (assuming your browser supports CSS). Interactive sessions will be distinguished from module code by the presence of the interpretter prompt symbol (>>>).

For example, the following indicates an interactive session (the lines without the prompts show what values [if any] the interpetter returns).

>>> print "hello, python world!"
hello, python world!
>>>

This example shows a hypothetical function and class in a module:

## test.py

def helloFunction(name):
    print "Hello %s, it's nice to meet you!" % name
    

class AClass:
    def __init__(self):
        print "AClass created"
        
    def __del__(self):
        print "AClass destroyed"

Getting Started

Fire up the Python interpretter (see the documentation which came with you Python distribution if you don't know how to do that).

We'll check that everything is ready to go by running some demos. Type the following:

>>> from disipyl import demos
>>> demos.showdemos()

A series of example plots will begin to appear. After each plot appears the program will wait until you hit the return key or click on the plot window with your right-mouse button before continuing.

To see a similar set of demos using the Tkinter GUI, type the following at the command-line from within the the disipyl directory (this assumes that the python interpretter is in your path):

$ python tkdisipyl.py

A small window with two buttons will appear, offering you a choice between the 2D and 3D demos. You can play around with both sets of demos. The 3D demos are particularly interesting because they provide a set of navigation buttons (e.g., rotate left, zoom in, etc.) so you can interactively explore the 3D plots.

Creating Plots

The Parts of a Plot

Every valid drawing in disipyl consists of at least one instance of each of the following classes (or their descendants):

  1. Canvas
  2. PlotObject
  3. AxisSystem

All three of these classes are primarily container classes - they maintain a list of the subobjects which they are responsible for, and call each of these subojects to draw themselves in turn. For example, a PlotObject instance must be assigned a valid AxisSystem (in the .axes attribute). Similarly an AxisSystem should have two or more Axis objects (.xaxis, .yaxis, and sometimes a .zaxis).

All disipyl objects are ultimately derived from the Object class, defined in the file pxdislin.py. All classes derived from Object maintain a private attribute (currently a list) called __options__ which is used to track and maintain the different types of options which affect how the object is drawn. This dictionary of options is accessed in a somewhat roundabout way - options are set using the __call__ method of the class, options are retrieved using the standard __getattr__ function. We can examine all the options of an object by using the __call__ method without any argument. To see how this works in action examine the following code:

>>> from disipyl import pxdislin
>>> message = pxdislin.Text(text='hello, disipyl world!')
>>> message()

*BEGIN OBJECT: Text
  TeX = 0
  color = fore
  font = default
  fonttype = hardware
  height = 36
  justify = left
  position = (0, 0)
  rotation = 0
  text = 'hello, disipyl world!'
  ucoords = 1
*END OBJECT: Text

>>> message(color = 'red', height = 48)
>>> message.color
'red'
>>>

After importing the appropriate module, we created a Text object to which we gave the name message. We then invoked the __call__ method (message()) without any arguments, which returned a printed message showing the various options for this object. We invoked the __call__ method a second time with keyword arguments. These keyword arguments corresponded to the options we wished to set. Finally, we examined a single option (message.color) using the standard attribute calling mechanism.

Note, that options can be set using either the normal attribute calling mechanism (.attribute) or via the __call__ method, but you should always prefer the __call__ method because only then can disipyl ensure that the options you are setting are relevant for the given object.

Simple Plot Creation

The first plot we will create will be a simple scatter plot. The module, disipyl.plots, provides a class, ScatterPlot, which provides reasonable default values for scatter plots. Once we've created our plot, it's easy to refine it to get it just right.

Let's create the scatter plot:

>>> from disipyl import plots
>>> s = plots.ScatterPlot(range(10),range(10))
>>> s.draw()

After importing the plots module we created a ScatterPlot object to which we assigned the variable name 's' (when working in the interactive interpretter it's most convenient to use short variable names; when creating module files you should use more descriptive names).

The plot we've just created isn't very exciting. It's a simple scatter plot where the x- and y-values are identical. We're going to spice up the plot, but first let's explore the plot object a little:

>>> s()

*BEGIN OBJECT: ScatterPlot
  TeXmode = 1
  defaultcanvastype = disipyl.pxdislin.Canvas
  symbols = <disipyl.pxdislin.SymbolGroup instance at 014197FC>
*END OBJECT: ScatterPlot

Any disipyl object derived from the base class Object will display it's options when called without any arguments. The ScatterPlot has relatively few options of its own. The option TeXmode specifies whether TeX style formatting of equations is available. The option defaultcanvastype specifies the type of Canvas object the plot should generate if it needs to draw itself (as opposed to being draw as a sub-object of a Canvas).

When a ScatterPlot object is created the initial set of data is represented by a SymbolGroup object. A SymbolGroup is simply a collection of individual Symbol objects with similar options (color, shape, etc.). The reason that the ScatterPlot object has relatively few options is that it is primarily a container object, as noted above. When we call the draw method for the ScatterPlot object it in turn invokes the draw methods of each of it's sub-objects.

Finding out about Object options

What are the valid settings for the various options? In version of disipyl previous to 0.6 you just had to browse the source code to find out. In version 0.6 and higher there is a new method for querying objects about their options. This mechanism is accessed through the info() method, as shown below:

>>> s.info('TeXmode')
Specifies whether the TeX mode should be used for text objects.
Since this doesn't adversely affect anything else, it's probably
simplest to leave this on.
Valid: 0 (off) or 1 (on)
------- 
Default: 1

>>> s.info('defaultcanvastype')
Specifies the default Canvas type (Canvas or its descendants)
which is created when a plot finds itself without a canvas object
to draw on.
Valid: Any object derived from Canvas
------- 
Default: Canvas

>>> s.info('symbols')
Undocumented option.
>>> s.info('foo')
No such option for ScatterPlot

As you can see, the argument to the info() method is a string with the name of the option you want to know more about. If the option is currently undocumented, then you are informed of this and you'll have to browse into the source code to figure out what the option does (if it's not obvious from the name). Eventually I hope to have all the options will be documented.

If you're creating new disipyl classes youreself you can specify a file which holds the option documentation by specifying the INFOMODULE attribute in a call to setoptions. For example, in the file pxdislin the setdefaults method of the Page object is defined as so:

# snippet from pxdislin.py
# from defintion of class Page

    def set_defaults(self):
        self.setoptions(
            fillcolor = None,
            border = 0,
            width = 3000,
            height = 2200,
            scale = 1,
            scalingmode = 'down',
            INFOMODULE = "optioninfo.page",
        )

The INFOMODULE attribute points to a Python module optioninfo.page (the file page.py in the subdirectory optioninfo). Simply change this value to an appropriate value for your new class.

Jazzing up the Plot

Now we're ready to add some pizzaz to this plot.

>>> s.symbols(color='red', size=35, shape='fill triangle')
>>> s.axes.xaxis(name='X-Axis', nameheight=40)
>>> s.axes.yaxis(name='Y-Axis', nameheight=40)
>>> s.draw()

This code changes the color, size and shape of the symbols used to represent the scatter of the data, and adds some axis labels.

What if we want to then add some more data to the plot? For example, we might want to add some more symbols representing another set of observations. This is relatively easy to do:

>>> group2 = pxdislin.SymbolGroup(range(10),range(9,-1,-1),color='blue', shape='fill circle',size=35)
>>> s.add(group2)
>>> s.draw()

Finally, we'll center and square-up the graph so that the units are the same length in the x- and y-directions. We'll also add a title:

>>> s.axes(squared=1,centered=1,grids=1,ngridlines=(2,2))
>>> s.title = pxdislin.Title(text="A Simple Graph",offset=-200)
>>> s.draw()

Interacting with a Plot

disipyl 0.6 introduces a new mechanism by which you can interact with a 2D plot - the explore() method. When you use the explore() method with an instance of any class derived from PlotObject you can click on the plot with mouse button one, and the plot coordinates will be recorded. Click mouse button two to stop recording. After the plot exits the x- and y-coordinates which you clicked will be returned as a tuple containing two lists. Here's an example (in which I clicked the plot three times)::

>>> s = plots.ScatterPlot(range(10), range(10))
>>> s.explore()
([1.2486993823422159, 3.4129315248323104, 5.5969735725168217], [2.2377216588389341, 4.4729876516275269, 7.018894839788552])

Currently the possibilties of plot interaction are very basic, but I will include derived clases with more functionality in future releases (and you can write such classes yourself - see the source code for PlotObject.draw() and PlotObject.explore() in pxdislin.py and the function getMouseClicks in pydislin.py)

Creating a 3D Plot

We're now going to use disipyl to create a 3-D plot of the function: f(x,y) = Imag(Log(x+iy)). There's two ways to do this - the easy way and the hard way. We'll start with the easy way, to show you just how quickly one can create a plot in disipyl. Then for completeness we'll do it the hard way.

The easy way

>>> import cmath 
>>> from disipyl import plots
>>> def func(x,y): 
        if x==0 and y==0:
            return 0
        z = cmath.log(complex(x,y)) 
        return z.imag
 
>>> easy = plots.SurfacePlot(func,-2,2,0.5,-2,2,0.5) # set function to plot and axis limits
>>> easy.axes(lengths3D=(2,2,1))
>>> easy.surface(topcolor=50,bottomcolor=200)
>>> easy.title = pxdislin.Title(text="$f(x,y)=Im(\log(x+iy))$") 
>>> easy.draw()
Most of the above is self-explanatory. The only additional operations performed after creating the plot were to change the relative scaling by which each of the axes is draw (by setting the lengths3D option), to set the colors used to draw the top and bottom of the surface object, and to create a title.

The hard way

>>> hard = plots.Plot3D()
>>> surface = pxdislin3D.FunctionSurface(func, 0.3,0.3,3,3)
>>> surface(topcolor=50,bottomcolor=200)
>>> hard.add(surface)
>>> hard.axes.limits(-2,2,-2,2,-3,3)
>>> hard.axes(lengths3D=(2,2,1))
>>> hard.title = pxdislin.Title(text="$f(x,y)=Im(\log(x+iy))$")
>>> hard.draw()

As you can see, the "hard way" isn't all that hard. The only real difference is that we had to create our own FunctionSurface object (in the easy example the SurfacePlot object created it for you), and we had to set our own axis limits..

If you are using the IDLE development environment you can use the Diddle classes in tkdisipyl.py to interactively explore your plot.

>>> from disipyl import tkdisipyl
>>> d = tkdisipyl.Diddle(hard)

When you create a Diddle object a Tk window should pop up containing the plot and a number of navigation buttons. Try rotating, tilting and zooming in on the plot.

Creating a New Plot Class

The dispyl package comes with a fairly good variety of "pre-canned" plot types. However, sooner or later you'll want to make a specialized plot for which none of the supplied classes is appropriate. In such cases it's desirable to derive your own specialized plot class.

Most specialized plots can be derived from either the Plot2D or Plot3D classes. If you have very specialized needs you may need to derive your class from the PlotObject. The files plots.py and mlabplots.py are filled with examples of specialized plot classes.

For More Information

If you want to learn more about how to create disipyl plots, check out the demo plots in demos.py. I'll also try and add more examples to this tutorial as development continues, so please check the disipyl website for updates.