NAME
PDL::Graphics::Prima - an interactive plotting widget and library for PDL and Prima
SIMPLE SYNOPSIS
use PDL::Graphics::Prima::Simple;
use PDL;
# --( Super simple line and symbol plots )--
# Generate some data - a sine curve
my $x = sequence(100) / 20;
my $y = sin($x);
# Draw x/y pairs. Default x-value are sequential:
line_plot($y); line_plot($x, $y);
circle_plot($y); circle_plot($x, $y);
triangle_plot($y); triangle_plot($x, $y);
square_plot($y); square_plot($x, $y);
diamond_plot($y); diamond_plot($x, $y);
X_plot($y); X_plot($x, $y);
cross_plot($y); cross_plot($x, $y);
asterisk_plot($y); asterisk_plot($x, $y);
# Sketch the sine function for x initially from 0 to 10:
func_plot(0 => 10, \&PDL::sin);
# --( Super simple histogram )--
# PDL hist method returns x/y data
hist_plot($y->hist);
my ($bin_centers, $heights) = $y->hist;
hist_plot($bin_centers, $heights);
# Even simpler, if of limited use:
hist_plot($heights);
# --( Super simple matrix plots )--
# Generate some data - a wavy pattern
my $image = sin(sequence(100)/10)
+ sin(sequence(100)/20)->transpose;
# Generate a grayscale image:
matrix_plot($image); # smallest is white
imag_plot($image); # smallest is black
# Set the x and y coordinates for the image boundaries
# left, right, bottom, top
matrix_plot([ 0, 1 ], [ 0, 2 ], $image);
imag_plot( [ 0, 1 ], [ 0, 2 ], $image);
# --( More complex plots )--
# Use the more general 'plot' function for
# multiple DataSets and more plotting features:
my $colors = pal::Rainbow()->apply($x);
plot(
-lines => ds::Pair($x, $y
, plotType => ppair::Lines
),
-color_squares => ds::Pair($x, $y + 1
, colors => $colors,
, plotType => ppair::Squares(filled => 1)
),
x => { label => 'Time' },
y => { label => 'Sine' },
);
WIDGET SYNOPSIS
use PDL;
use Prima qw(Application);
use PDL::Graphics::Prima;
my $t_data = sequence(6) / 0.5 + 1;
my $y_data = exp($t_data);
my $wDisplay = Prima::MainWindow->create(
text => 'Graph Test',
size => [300, 300],
);
$wDisplay->insert('Plot',
-function => ds::Func(\&PDL::exp, color => cl::Blue),
-data => ds::Pair($t_data, $y_data, color => cl::Red),
pack => { fill => 'both', expand => 1},
);
run Prima;
IF YOU ARE NEW
If you are new to PDL::Graphics::Prima, you should begin by reading the documentation for PDL::Graphics::Prima::Simple. This module provides a simplified interface for quickly dashing off a few plots and offers stepping stones to create more complex plots. Depending on your plotting needs, you may not need anything more complicated than PDL::Graphics::Prima::Simple. However, PDL::Graphics::Prima offers much greater flexibility and interactivity than the options available in the Simple interface, so once you feel comfortable, you should come back to this manual page and learn how to create and utilize Plot widgets in conjunction with the Prima GUI toolkit.
DESCRIPTION
PDL::Graphics::Prima is a plotting library for 2D data visualization. The core of this library is a Plot widget that can be incorporated into Prima applications. Although the library is capable of producing nice looking static figures, its killer feature is the GUI environment in which it belongs. Prima provides an array of useful interactive widgets and a simple but powerful event-based programming model. PDL::Graphics::Prima provides a sophisticated plotting library within this GUI framework, letting you focus on what you want to visualize rather than the details of how you would draw it. These tools allow you to build interactive data visualization and analysis applications with sophisticated plotting and intuitive user interaction in only a few hundred lines of code.
Like any other widget, a Plot widget can be constructed using the parent widget's insert method. PDL::Graphics::Prima actually defines the bulk of its functionality in the Prima::Plot package, so that you can simply say:
$parent->insert(Plot =>
place => {
x => 0, relwidth => 0.5, anchor => 'sw',
y => 0, relheight => 0.5,
},
-data => ds::Pair($t_data, $y_data, color => cl::Red),
... etc ...
);
Prima::Plot (i.e. PDL::Graphics::Prima) is a descendant of the Prima::Widget class, so everything that you can do with widgets you can do with plots, including specifying event callbacks such as mouse and keyboard interaction. You can specify the means for placing the plot within a larger parent widget using basic geometry management, or the Tk-like pack or place specifiers. In fact, Prima allows any widget to serve as the container for other widgets, so you can insert other widgets (i.e. other plots) into a plot. This serves as a simple mechanism for creating sub-plots, though beware: sub-plots are not yet correctly rendered in raster or postscript file output.
If you want to add new content to a plot or remove content from a plot, you do this by manipulating the dataSet collection. Axis minima, maxima, scaling, and labels are handled by axis objects which you obtain through the x and y axis accessors. You set and manipulate the title via the title accessor method.
From the standpoint of basic plot object structure, that's about it!
Properties
PDL::Graphics::Prima has a number of properties that you can specify in the constructor and later change through accessor methods.
title
Sets or gets the string with the figure's title text. To remove an already set title, specify an empty string or the undefined value. Changing this issues a ChangeTitle event.
titleSpace
Sets or gets the number of pixels that would be allocated for the title, if the title is set. Changing this issues a ChangeTitle event, even if the title text is not presently being displayed.
Note: This is a fairly lame mechanism for specifying the space needed for the title. Expect this to change, for the better, in the future. Please drop me a note if you use this and need any such changes to be backwards compatible.
x, y
Obtains the object that controls the settings for the x- or y-axis. For example:
# Set the x-min to -10 and the y-max to auto-scaling
$plot->x->min(-10);
$plot->y->max(lm::Auto);
Actually, these accessors are not hard-coded into the plot library. Rather, these are the default names of the axes. Any object of type Prima::Component (which is any object in the Prima object heierarchy) that has a name can be accessed from the parent by using the component's name as a method on the parent. That is, you can change the name of the axis and use the new name:
# Rename the x-axis; be sure it starts with "x", though
$plot->x->name('xfoo');
# Change the x-axis' minimum value
$plot->xfoo->min(-10);
# This croaks:
$plot->x->max(20);
This is a feature of Prima. Eventually, when multiple x- and y-axes are allowed, this will allow us to transparently access them by name just like we access the single x- and y-axes by name at the moment.
dataSets
This is the means by which you add new content to your plot (apart from placing sub-figures in there, of course). This either sets or returns the collection of DataSets. The DataSets are held in a tied anonymous hash that you directly manipulate. In order to add a new DataSet, you can simply modify the anonymous hash in place using standard Perl hash manipulation functions and techniques. For example:
# Add a new DataSet
$plot->dataSets->{new_data} = ds::Pair(
$x, $y, plotType => ppair::Squares
);
# Remove a DataSet
delete $plot->dataSets->{model};
# Clear the DataSets
%{$plot->dataSets} = ();
Since the hash is actually tied, DataSets that you add will be validated as you add them.
METHODS
PDL::Graphics::Prima provides a number of methods. Most of these focuse on generating images of the plot.
get_image
Returns a Prima::Image of the plot with same dimensions as the plot widget.
save_to_postscript
Saves the plot with current axis limits to a postscript figure. This method takes an optional filename argument. If no filename is specified, it pops-up a dialog box to ask the user where and under what name they want to save the postscript figure.
This functionality will likely be merged into save_to_file, though this method will remain for backwards compatibility.
save_to_file
Saves the plot to a raster image file. This method takes an optional filename argument, deducing the format (and applicable codec) from the filename. If no filename is specified, it creates a dialog box asking the user where and under what name they want to save the file.
copy_to_clipboard
Copies the plot with current axis limits as a bitmap image to the clipboard. The resulting clipboard entry is suitable for pasting into applications that know how to handle bitmap images such as LibreOffice or gpaint on Linux, Microsoft Office or Windows Paint on Windows.
Events
You can send notifications and hook callbacks for the following events:
ChangeTitle
Called when the title or titleSpace gets changed
Replot
Called when the widget needs to replot "real soon", but not immediately. Immediate replot requests should go in the form of "Paint" events. In order to prevent the system from getting bogged down by too many paint requests, replotting kicks off a timer that issues the paint requests after a brief period (defaults to 30 milliseconds).
ChangeData
Called when the dataSet container changes (not the datasets themselves, but the whole container).
RESPONSIBILITIES
The Plot object itself has to coordinate a number of sub-systems in order to get a functioning plot. As these responsiblities are not always clear even to their author, here is a list of what the plot is responsible for handling.
- knowing the plot title
-
Although the axes are responsible for knowing the axis labels, the plot itself is responsible for knowing the plot title and managing the space necessary for it.
- initiating drawing operations
-
The drawing of the axes, data, and plot title are all coordinated, ultimately, by the plot object.
- managing the dataset container
-
The plot does not manage the datasets directly, but it owns the dataset container that is responsible for providing an API to the container.
- handling user interaction
-
All user interaction (which at the moment is limited to mouse interaction) is handled by the plot object. Drag and zoom events ultimately lead to changes in the axes' minima and maxima (the axes are responsible for these pieces of information), but these changes are initiated through the plot object.
- file generation and clipboard interaction
-
Requests for raster and postscript output as well as copying the image to the clipboard are the responsibility of the plot.
- initiating autoscaling
-
Autoscaling for either of the axes is initiated by a call to the plot object's
compute_min_max_for
, which computes the minima and maxima for a given axis. Most of the calculations necessary for this operation are performed by the dataset and the underlying plotTypes, but they are coordinated and synthesized by the plot object.
Many things are not the responsiblity of the plot object but are instead handled by other objects, which are themselves usually held by the plot.
- tick and axis details
-
All axis and tick details, including minima and maxima, scaling, tick mark locations, axis labels, and conversion from real x and y data values to pixel offsets are handled by the axis objects. (The scaling, actually, is managed by the scaling object or class that is held by the axis.)
- managing datasets or plotTypes
-
The datasets are managed by the dataset collection hashref defined in PDL::Graphics::Prima::DataSet. Any access to the datasets (outside of declaring them directly in the constructor, which is a special-case) is managed through the dataSet collection. Any access to the specific plot types are themselves handled by the datasets themselves.
TODO
This is not a perfect plotting library. Here are some of the particularly annoying issues with it, which I hope to resolve. This is part warning to you, gentle reade, and part task list for me.
Proper drawing of child widgets. Although subplots and insets can be added to plot widget, they cannot be drawn to output files like postscript or raster formats until this functionality is available.
If Prima had an SVG output, I could easily add it as a figure output option.
I have had it on my list for a while to add the facilities to turn off drawing operations, temporarily, so that adding a large number of dataSets can be done more quickly. This would require some sort of interface such as
$plot->autoupdate(0);
... add datasets ...
$plot->autoupdate(1);
I have hit substantial performance problems when adding over 20 datasets. The actual drawing of those datasets and mouse interation is fine, but the process of just adding them to the plot can be quite sluggish.
There is essentially no way to tweak the title font, and the means for specifying the title space is stupid and nearly useless.
The exact pixel position of the left margin depends on the size of the y-tick labels, which can change during the process of zooming in or out. This means mouse scroll-wheel action doesn't work exactly as advertised. Well, it does, now that I've hedged my advertisement. Still, tracking the previous time of a scroll wheel click and the previous x/y location could make it work flawlessly.
There is no way to indicate by-hand where the viewport should be. It is always calculated from the requirements of the tick labels. There is no way to control the padding on the right side of the plot, either; it is fixed. All of these should be tweakable.
Singular names => scalars, plural names => piddles is not consistent across the board. At least not with all of the plotTypes. This can be fixed by changing singular keys to plurals and quietly accepting singulars for backwards compatibility, but it hasn't happened yet.
Multiple axes. In the constructor, any property that starts with x would be an x-axis (x1, x2, etc). You would have to specify an axes with a dataset, though the default would be the first axis when sorted asciibetically. Axes would have properties regarding if they are drawn on the top, the bottom, both, etc, and whether their tick labels are drawn on the top, bottom, etc.
I am very proud of the automatic scaling. Unfortunately, it's complicated and not yet well documented. Also, it could be even more awesome. It needs to allow for negative pixel paddings as well as "extra" pixel padding. This would simply effect how collate_min_max_for_many works and should be a snap to implement. For what it's worth, the collation code should probably be rewritten in C.
Automatic scaling should allow for 'next widest tick' in addition to the current super-tight bounds that it calculates. This would make hard-copy figures much, much nicer.
SEE ALSO
Both the Perl Data Language and the Prima GUI Toolkit are amazing and this module would have no reason for existence without both of them.
This module serves as the motivation for PDL::Drawing::Prima, and also would be unable to function with any efficiency without it.
Other 2D plotting options include PDL::Graphics::PGPLOT, PDL::Graphics::PLplot, PDL::Graphics::Gnuplot. There may be a few others. For my part, I also wrote PDL::Graphics::Asymptote, though it is more of a toy than these other libraries. Search CPAN for more.
For 3D plotting, see PDL's own PDL::Graphics::TriD, as well as PDL::Graphics::Gnuplot and the low-level bindings in PDL::Graphics::PLplot.
AUTHOR
David Mertens (dcmertens.perl@gmail.com)
ADDITIONAL MODULES
Here is the full list of modules in this distribution:
- PDL::Graphics::Prima
-
Defines the Plot widget for use in Prima applications
- PDL::Graphics::Prima::Axis
-
Specifies the behavior of axes (but not the scaling)
- PDL::Graphics::Prima::DataSet
-
Specifies the behavior of DataSets
- PDL::Graphics::Prima::Limits
-
Defines the lm:: namespace
- PDL::Graphics::Prima::Palette
-
Specifies a collection of different color palettes
- PDL::Graphics::Prima::PlotType
-
Defines the different ways to visualize your data
- PDL::Graphics::Prima::ReadLine
-
Encapsulates all interaction with the Term::ReadLine family of modules.
- PDL::Graphics::Prima::Scaling
-
Specifies different kinds of scaling, including linear and logarithmic
- PDL::Graphics::Prima::Simple
-
Defines a number of useful functions for generating simple and not-so-simple plots
LICENSE AND COPYRIGHT
Portions of this module's code are copyright (c) 2011 The Board of Trustees at the University of Illinois.
Portions of this module's code are copyright (c) 2011-2013 Northwestern University.
This module's documentation are copyright (c) 2011-2013 David Mertens.
All rights reserved.
This module is free software; you can redistribute it and/or modify it under the same terms as Perl itself.