/XTK
The driver is unlikely to work fully with versions of Tk prior to
Tk4.0. At the very least, the changed definitions of the
xview
and
yview
commands will prevent the
use of scrollbars with the widget.
pgplot
. The first argument of this command is the Tk
path-name to give the new widget. This doubles as the PGPLOT
device name of the widget.
pgplot
widgets adopt a default size of 256×256 pixels. This
can be changed via the -width
and -height
configuration
options during or after creation of the widget.
-maxcolors
configuration option. Otherwise you will run out of colors very
quickly and some of your widgets will be forced to use monochrome
colors. Also be sure that all asynchronous callbacks call
cpgslct()
to
select the appropriate widget for PGPLOT output.
PseudoColor
,
StaticColor
, GrayScale
,
StaticGray
, and TrueColor
are supported.
The value of the -maxcolors
configuration option specifies the number of colors that each
widget should attempt to allocate. This defaults to 100.
The actual number allocated will be less than this if there are
fewer color cells left in the application's color table. If fewer
colors than the value of the
-mincolors
configuration
option are available, then monochrome is used.
Note that changes to the representation of a given PGPLOT color
index will change the colors of previously drawn graphics if a
PseudoColor
or GrayScale
colormap is
being used. For other colormaps, only subsequently drawn graphics
will take on the new colors.
See the section on private colormaps for further information.
Cursor input can also be acquired via
cpgband()
and
cpgcurs()
,
but these functions are not recommended because they block the Tk
event loop.
cpgpage()
has not been called since the user resized the window to
a smaller size.
cpgpap()
has been used to request a larger physical size than that
of the widget.
In such cases a Tk scrollbar could be used to scroll the visible
area of the view surface. For this purpose the widget provides the
conventional xview
and
yview
commands and
the -xscrollcommand
and
-yscrollcommand
configuration options.
cpgscrl()
for details.
tkpgplot.h
libtkpgplot.a
libtkpgplot.a
to the linker before the
main PGPLOT library. For example, a snapshot of the link line
under Solaris would look something like:
-ltkpgplot -lcpgplot -lpgplot -lTk4.1 -ltcl7.5 -lX11 -ldl
cpgplot.h
libcpgplot.a
and libpgplot.a
.plot
, and gives it an
initial size of 10x10 cm, up to 16 colors and a 3D border width of
2 pixels.
pgplot .plot -width 10c -height 10c -maxcolors 16 -bd 2Having done this the following command would change the X-window cursor of the widget to a small cyan crosshair cursor.
.plot configure -cursor {crosshair black cyan}
The following is a list of the configurable options of PGPLOT widgets:
raised
.
focus
command. If you intend to select for
keyboard input in the widget (as opposed to mouse button
input), then be sure to reconfigure this to 1. You should then
also establish event bindings such that once the widget has
the keyboard input focus, Tab and Shift-Tab will move the
input focus in the conventional manner. For example:
pgplot .plot -takefocus 1 bind .plot <Tab> {focus [tk_focusNext %W]} bind .plot <Shift-Tab> {focus [tk_focusPrev %W]}
xview
widget command, the specified command
prefix is augmented with two extra arguments and evaluated.
The two arguments are floating point numbers which denote the
left and right edges of the visible part of the view surface. 0
refers to the leftmost edge of the view surface, and 1 refers
to the rightmost edge. This is designed for use with Tk
scrollbar widgets. For example, given a horizontal scroll bar
called .hscroll
and a PGPLOT widget called
.plot
, the following commands would connect the
scrollbar and the PGPLOT widget.
.plot configure -xscrollcommand {.hscroll set} .hscroll configure -command {.plot xview}
-xscrollcommand
configuration option.
cpgimag()
or
cpggray()
to draw images, then the default of 100 colors is likely to be
much more than you need. In this case setting
-maxcolors to 16 will give access to all 16 of PGPLOT's
pre-defined line colors, while reducing the likelihood that
either your application or somebody else's will run out of
colors. This is especially important if you intend to use more
than one PGPLOT widget within your application. This option is
only consulted when a widget is created. Changes made
thereafter using the configure
command will be ignored.
widget_path_name command_name arguments...
Also note that when the cursor is augmented, the rubber band
is redrawn every time that the PGPLOT buffer is flushed. This
happens after every call to PGPLOT functions unless sensible
use is made of
cpgbuf()
and
cpgebuf()
to
buffer sequential calls. Be sure to use these functions
properly if you don't want your PGPLOT code to be slow.
%x
and %y
fields of Tk event bindings). It can take any of the following
forms:
cpgopen()
when the widget was last connected to
PGPLOT. This can then be used with
cpgslct()
to select the widget to be the target for subsequent PGPLOT
output. If the widget has not been opened, 0 is returned.
pgplot
widgets draw to an off screen
pixmap which is kept fixed in size for the duration of each page,
and is only resized to fit the window when
cpgpage()
is called. Thus when the widget is smaller than the pixmap, only
part of the PGPLOT view surface will be visible. There are two
ways to deal with this:
pgplot .plot -maxcolors 16 scrollbar .xscroll -command {.plot xview} -orient horizontal scrollbar .yscroll -command {.plot yview} -orient vertical .plot configure -xscrollcommand {.xscroll set} .plot configure -yscrollcommand {.yscroll set}
cpgpage()
,
the new view surface will then match the current size of the
widget.
pgplot .plot -maxcolors 16 -bd 2 -relief sunken bind .plot <Configure> {your_redraw_command}
cpgband()
and
cpgcurs()
)
were not designed with event-driven programs
in mind, and they are not appropriate for use in Tk because they
block the Tk event loop. This section describes how to display
rubber-band cursors using pgplot
widget commands and
how to asynchronously respond to user input via the Tk bind command.
The Tk bind
command arranges for user-specified Tcl
code to be executed whenever a given X-window event occurs. Events
such as ButtonPress
, KeyPress
and
Motion
events have associated X and Y X-Window
coordinates at which the event occurred. These can be converted to
PGPLOT world coordinates with the world
command provided by pgplot
widgets. For example the
following code arranges that whenever any mouse button is pressed
when the pointer is in the given PGPLOT widget, a procedure
called report_button_press
is invoked. This
procedure then converts the X-window pixel coordinates to world
coordinates and reports them to stdout.
pgplot .plot -maxcolors 16 ... bind .plot <ButtonPress> {report_button_press %W %b %x %y} ... proc report_button_press {plot button xpixel ypixel} { set x [$plot world x $xpixel] set y [$plot world y $ypixel] puts stdout "You pressed mouse-button $button at X=$x Y=$y" } |
The rubber-band cursors that would normally be provided by the
cpgband()
PGPLOT function, are also available via
the setcursor
command of
pgplot
widgets. These augment the normal X Window
cursor of the widget, and remain attached to the cursor until the
clrcursor
command is
next invoked on the widget.
The following is a complex example that shows how one might
arrange for the user to select an X-axis range using rubber-band
cursors and Tk bindings. The code acts as
follows. xrange_step1
takes a PGPLOT widget and a
callback-command as its arguments. It displays a vertical line
cursor in this widget and arranges that when the user next presses
mouse-button 1, xrange_step2
will be
called. xrange_step2
changes the cursor to an X-axis
range cursor with one of its two vertical lines anchored where
the user pressed button 1. It then rebinds button 1 to invoke
xrange_finish
when the user selects the other
end of the range. xrange_finish
deletes the cursor
and the associated event binding by calling on
xrange_cancel
, then it evaluates the command
that was originally passed to xrange_step1
via its
cmd
argument and tacks on the two selected
world-coordinate limits as trailing arguments.
proc xrange_step1 {plot cmd} { $plot setcursor vline 0 0 3 bind $plot <1> "xrange_step2 %W cmd %x" } proc xrange_step2 {plot cmd x} { set xa [$plot world x $x] $plot setcursor xrng $xa 0 3 bind $plot <1> "xrange_finish %W $cmd $xa %x" } proc xrange_finish {plot xa x} { set xb [$plot world x $x] xrange_cancel $plot eval $cmd $plot $xa $xb } proc xrange_cancel {plot} { bind $plot <1> {} $plot clrcursor } |
The following is an example of how this could be used. A more complete example is provided later.
pgplot .plot -maxcolors 16 ... proc report_xrange {plot xa xb} { puts "You selected X-axis range $xa -> $xb" } xrange_step1 $plot report_xrange
Finally, the following example shows how one can provide a
continuous readout of the world coordinates of the cursor whenever
the cursor is in the target PGPLOT widget. It creates two label
widgets in which to display the X and Y coordinates, places them
left to right above a PGPLOT widget and then arranges for the
positions reported by Motion
events to be translated
to world coordinates and placed in the labels.
# Create two labels side-by-side in a frame. frame .f label .f.x -width 12 -anchor w label .f.y -width 12 -anchor w pack .f.x .f.y -side left -anchor w # Create the PGPLOT widget and place it below the labels. pgplot .plot -maxcolors 16 pack .f .plot -side top # Arrange to update the labels whenever the cursor moves # in the PGPLOT window. bind .plot <Motion> { .f.x configure -text "X=[.plot world x %x]" .f.y configure -text "Y=[.plot world y %y]" } |
Note that this example will display X=0.0 and Y=0.0 until the
widget is opened to PGPLOT. Up to that point no world coordinates
exist so the world
command returns 0.0 for all input
coordinates.
pgplot
widget allocates colors from the
colormap of the top level widget that encloses it. By default
this is also the default colormap of the screen, which is being
competed for by most of the other X-window programs that are being
displayed. To avoid running out of colors you can do two things:
pgplot
-maxcolors
configuration option to as small a value as you actually
need.
The latter option is easily available via the
-colormap
configuration argument of the Tcl/Tk
toplevel
command. This means that you can either
create your own separate top level widget with its own colormap, or
you can request a private colormap for the main window when the
application starts. For example, using the standard Tcl/Tk window
shell:
wish -colormap new
will allocate wish
its own private colormap. If you
start your application via the standard Tk_Main()
function and pass it the command-line arguments of your program
then it too will accept the -colormap
option from
the command-line.
.c
file and compile the result as described later.
The main()
function of the example program delegates
starting up Tcl/Tk to the standard Tk main function. This in
turn calls the example customization function
Demo_AppInit()
. The customization function calls
Tkpgplot_init()
to create the Tcl
pgplot
command, then creates two other new Tcl
commands. The first is simply a wrapper around
cpgopen()
.
The second is a wrapper around an example
drawing function that draws a plot of x² versus x.
#include <tk.h> #include <stddef.h> #include <stdlib.h> #include "tkpgplot.h" /* Needed solely for tkpgplot_Init() */ #include "cpgplot.h" /* Needed for cpg*() pgplot functions */ /* Prototype local functions. */ static int Demo_AppInit(Tcl_Interp *interp); static int usage_error(Tcl_Interp *interp, char *usage); static int tcl_pgopen(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); static int tcl_draw_plot(ClientData data, Tcl_Interp *interp, int argc, char *argv[]); int main(int argc, char *argv[]) { Tk_Main(argc, argv, Demo_AppInit); return 0; } static int Demo_AppInit(Tcl_Interp *interp) { if(Tcl_Init(interp) == TCL_ERROR || Tk_Init(interp) == TCL_ERROR || Tkpgplot_Init(interp) == TCL_ERROR) return TCL_ERROR; Tcl_CreateCommand(interp, "pgopen", tcl_pgopen, NULL, 0); Tcl_CreateCommand(interp, "draw_plot", tcl_draw_plot, NULL, 0); return TCL_OK; } /* * Implement the example pgopen Tcl command. This takes a single * PGPLOT device specification argument and returns the * corresponding PGPLOT id for use with cpgslct(). */ static int tcl_pgopen(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { char result[20]; int id; /* * Make sure that the right number of arguments have been provided. */ if(argc != 2) return usage_error(interp, "pgopen device"); /* * Attempt to open the PGPLOT device specified in argv[1]. */ id = cpgopen(argv[1]); if(id <= 0) { Tcl_AppendResult(interp, "Unable to open device: ", argv[1], NULL); return TCL_ERROR; }; /* * Turn off new-page prompting. */ cpgask(0); /* * Return the PGPLOT id of the device via the Tcl result string. */ sprintf(result, "%d", id); Tcl_AppendResult(interp, result, NULL); return TCL_OK; } /* * Implement the example Tcl draw_plot command. This takes three * arguments. The id of the PGPLOT device to plot to, the leftmost * X-axis value to display and the rightmost X-axis value to * display. */ static int tcl_draw_plot(ClientData data, Tcl_Interp *interp, int argc, char *argv[]) { double xa, xb; /* The X-axis range to plot */ int id; /* The PGPLOT id of the target device */ int i; /* * Make sure that the right number of arguments have been provided * and that the PGPLOT id makes sense. */ if(argc != 4 || (id=atoi(argv[1])) <= 0) return usage_error(interp, "draw_plot id xmin xmax"); /* * Decode the two X-axis world-coordinate limits. */ if(Tcl_GetDouble(interp, argv[2], &xa) == TCL_ERROR || Tcl_GetDouble(interp, argv[3], &xb) == TCL_ERROR) return TCL_ERROR; /* * Select the PGPLOT device and draw the plot. */ cpgslct(id); cpgpage(); cpgswin(xa, xb, 0.0, 1.0); cpgsci(1); cpgbox("BCNST", 0, 0, "BCNST", 0, 0); cpgsci(2); cpgmove(0.0, 0.0); for(i=1; i<100; i++) { float x = i/100.0; cpgdraw(x, x*x); } return TCL_OK; } /* * The final function is just a utility function for reporting * command-usage errors and returning the standard Tcl error code. */ static int usage_error(Tcl_Interp *interp, char *usage) { Tcl_AppendResult(interp, "Usage: ", usage, NULL); return TCL_ERROR; } |
Having assembled the above C fragments into a single file called
demo.c
the file can be compiled and linked (on
a Sun running Solaris 2.5.1) as follows:
cc -c -O demo.c -I/usr/local/pgplot -I/usr/local/include f77 -o demo demo.o -ltkpgplot -lcpgplot -lpgplot -ltk4.1 -ltcl7.5 -lX11 -lnsl -lsocket -ldlThe actual compilation and linking steps will differ from machine to machine. However note that the main PGPLOT library is written in FORTRAN. For this reason it is usually easiest to link the program using a fortran compiler as shown above. This is easier than having to figure out what FORTRAN support libraries to cite when linking with a C compiler.
Having compiled and linked the example program, we now need a Tcl script to show how to use the commands that it implements. The following script creates a PGPLOT widget, opens it to PGPLOT and draws the example plot in it. It also arranges that whenever the user resizes the application, the plot will be redrawn to take advantage of the new size.
# Give the application window a title. wm title . {Example plot} # Show a usage label. label .usage -text {Mouse-button 1 selects range, 2 cancels, 3 unzooms} .usage configure -bd 2 -relief groove -bg yellow pack .usage -side top -anchor c -fill x # Create a PGPLOT widget. pgplot .plot -maxcolors 4 -width 500 -height 500 pack .plot -side top -expand true -fill both # Open the widget to PGPLOT. pgopen .plot/xtk # Arrange for the plot to be redrawn whenever the user resizes it. bind .plot <Configure> {draw_plot [%W id] 0.0 1.0} # Reproduce the previously explained range-selection functions. proc xrange_step1 {plot cmd} { $plot setcursor vline 0 0 3 bind $plot <1> "xrange_step2 %W $cmd %x" } proc xrange_step2 {plot cmd x} { set xa [$plot world x $x] $plot setcursor xrng $xa 0 3 bind $plot <1> "xrange_finish %W $cmd $xa %x" } proc xrange_finish {plot cmd xa x} { set xb [$plot world x $x] xrange_cancel $plot eval $cmd $plot $xa $xb } proc xrange_cancel {plot} { bind $plot <1> {} $plot clrcursor } # This function is called to draw a zoomed version of the plot. proc zoom_plot {plot xa xb} { draw_plot [$plot id] $xa $xb start_zoom $plot } # This procedure sets up the cursor to allow users to zoom # in on the plot. proc start_zoom {plot} { xrange_step1 $plot zoom_plot bind $plot <2> "start_zoom $plot" bind $plot <3> {draw_plot [%W id] 0.0 1.0} } # Start the ball rolling. start_zoom .plot |
To try the above script, cut and paste it into a file called demo.tcl and run it by typing:
demo demo.tclIf the
pgplot
widget complains that there aren't
sufficient colors left in the default colormap, then rerun the
demo with a private colormap as follows:
demo demo.tcl -colormap newIn addition, if you just run the program without specifying a script file:
demothen you will be given a
wish
shell prompt at which
you can type Tcl/Tk commands, including those defined by the
example program.
A more complicated demonstration program comes with the PGPLOT distribution and is automatically compiled if the Tk driver is selected. To run it, change to the pgplot installation directory and type:
pgtkdemo pgtkdemo.tclor
pgtkdemo pgtkdemo.tcl -colormap newif it complains about there being insufficient colors. Note that this demo uses different resize strategies for its two PGPLOT widgets. The topmost
pgplot
widget displays a
grey-scale image and is slow to redraw, so this has been given
scrollbars. If you resize the application to a smaller size, then you
will be able to use the scroll bars to see any part of the partially
obscured image. If you want to replot the image with the same size as
the shrunk widget, simply select an image function from the option
menu. The other pgplot
widget displays a line graph which
is quick to redraw, so when the application is resized by the user,
the graph is redrawn to take advantage of the new size.
pgplot
widget created a view surface that had the default, or
specified size of the widget rather than the size that the
widget was subsequently given by the Tk geometry manager. To
ensure that the widget has been displayed and given its final
size before drawing into it, place the following Tcl command
before your first plotting command:
update idletasks
pgplot
widget from complaining
about there being insufficient colors?