X-Window Tk widget driver

Author
M. C. Shepherd, 1997.

Supported device
Restricted to Tcl/Tk (Tk4.0 and above) applications on workstations running the X Window System.

Device type code
/XTK

Compatibility
So far, the driver has been tested by the author on a Sparc running Solaris 2.5.1 and the following combinations of Tcl and Tk: No changes were needed to upgrade between the above versions, so it is probably safe to assume that all intervening versions will work as well. Future incompatibilities will be accommodated as needed and distributed with later versions of PGPLOT.

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.

Device name specification
Tk PGPLOT widgets are created with a Tcl command called 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.

Default view surface dimensions
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.

Resolution
Depends on monitor.

Multiple device capability
Applications are allowed to create multiple PGPLOT Tk widgets and open them simultaneously to PGPLOT. If you do this, be sure to reduce the default number of colors allocated by each widget by using the -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.

Color capability
Colors are allocated from the colormap of the top-level window of the application. Colormaps of types 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.

Input capability
The standard Tk event binding mechanism can be used to select for any combination of keyboard and button press events received by a PGPLOT Tk widget. To facilitate combining this with PGPLOT, PGPLOT Tk widgets provide Tcl commands for converting between widget X coordinates and PGPLOT world coordinates. They also provide facilities for augmenting the X cursor with rubber-band cursors. These facilities are described later.

Cursor input can also be acquired via cpgband() and cpgcurs(), but these functions are not recommended because they block the Tk event loop.

Scroll capability
There are two types of scrolling available.

Tk window scrolling.
The widget window will be smaller than the PGPLOT view surface if:
  • 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.

PGPLOT Scroll area
The PGPLOT scroll-area option is supported. See the PGPLOT documentation on cpgscrl() for details.

Associated files
tkpgplot.h
This header file is intended for use by the C part of a Tcl/Tk application. It includes prototypes of the package-initialization function of the PGPLOT Tk widget and a few optional convenience functions for communicating with the widget directly via C.
libtkpgplot.a
This library file contains the Tk PGPLOT widget and its PGPLOT driver. The main PGPLOT library only contains a stub version of the driver, so it is essential to present 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
The include file for the C PGPLOT wrapper library should also be included in all files that make calls to the PGPLOT library from C.
libcpgplot.a and libpgplot.a
The PGPLOT C wrapper library and the FORTRAN PGPLOT library.

Configuration
As is customary in Tk, PGPLOT widgets can be configured both when they are created, and thereafter by using the path name of the widget as a command. For example, the following Tcl command creates a PGPLOT widget called .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 2
Having 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:

-background color
-bg color
Set or change the background color of the widget. Note that this also changes the color associated with PGPLOT color index 0. This command takes a standard Tk color specification as its only argument. The default foreground color is black.
-foreground color
-fg color
Set or change the color that is associated with PGPLOT color index 1. This command takes a standard Tk color specification as its only argument. The default foreground color is white.
-cursor tk-cursor-specification
This changes the cursor image that will be displayed when the cursor is within the PGPLOT widget. It takes a standard Tk cursor specification as its only argument. The default cursor is the cursor of the parent widget. This does not affect any rubber-band cursor that has been established.
-borderwidth width
-bd width
This sets the width of the 3D border that is drawn around the widget. The single argument is a standard Tk width specification. The default width is zero, which results in no border being drawn.
-relief raised|sunken|flat|ridge|groove
This specifies how the 3D border is to appear. It takes a single argument naming one of the standard Tk reliefs. The default is raised.
-height height
This specifies the height of the PGPLOT window displayed in the widget. It takes a standard Tk width specification as its only argument. The default is 256 pixels.
-width width
This specifies the width of the PGPLOT window displayed in the widget. It takes a standard Tk width specification as its only argument. The default is 256 pixels.
-highlightbackground color
This sets the color of the widget's highlight border when the widget does not have the keyboard input focus. It takes a standard Tk color specification as its sole argument. The default color is grey, chosen to match the default background color of most standard Tk widgets.
-highlightcolor color
This sets the color of the widget's highlight border when the widget has the keyboard input focus. It takes a standard Tk color specification as its sole argument. The default color is white, such that it stands out against the default black background of the PGPLOT window.
-highlightthickness width
This sets the width of the widget's highlight border. The single argument is a standard Tk width specification. By default the highlight thickness is zero. If you intend to adopt Tk's default click-to-focus input model and you intend to select for keypress events in the widget then be sure to set this thickness to a finite width, such as 2. Otherwise users will not be able to tell when the widget has the keyboard input focus.
-takefocus
This selects whether the widget will be given the keyboard input focus when Tab or Shift-Tab is pressed in a neighboring widget. The default is 0, which means that the widget will not receive the input focus unless it is explicitly set using the Tk 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]}
        
-xscrollcommand prefix
Whenever the visible part of the PGPLOT view surface underlying a PGPLOT widget is changed, either by resizing the widget or by scrolling horizontally with the 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}
        
-yscrollcommand prefix
This is the Y-axis equivalent of the -xscrollcommand configuration option.
-mincolors
This specifies the smallest acceptable number of colors before a widget switches to monochrome. The default is 2. If fewer colors than this number are available, a black and white colormap will be adopted and grey-scale images will be dithered. This option is only consulted when a widget is created. Changes made thereafter using the configure command will be ignored.
-maxcolors
This specifies the number of colors that the widget will attempt to allocate for itself. The default is 100. On computers with 8-bit frame-buffers, colors are a scarce resource so be sure to set this number as low as possible. For example, if you don't intend to use 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.

Other widget commands.
After a PGPLOT widget has been created, the following commands can be applied to that widget, using the normal Tk syntax of:
  widget_path_name command_name arguments...
    
configure option arguments...
This allows widget configuration options to be changed after a PGPLOT widget has been created. The supported options and their arguments have already been described in the Configuration section.
cget option
This allows the current value of the specified configuration option to be queried. The return string is formatted in the same fashion as the arguments of the corresponding "pathName configure option" command.
xview ...
This is used to scroll the widget horizontally. It can take any of the following forms, as used by Tk scrollbar widgets.
xview moveto fraction
Where fraction is the fractional width of the PGPLOT view surface that lies to the left of the left edge of the widget.
xview scroll increment units
Move the view surface increment pixels to the right. Negative values move the view surface to the left.
xview scroll increment pages
Move the view surface increment widget-widths to the right. Negative values move the view surface to the left.
yview ...
This is used to scroll the widget vertically. It takes the same forms as described for the xview command except that positive increments are defined as moving the widget downwards instead of to the right.
setcursor mode x y ci
This is used to augment the normal X-window cursor of a PGPLOT widget with one of a selection of rubber-band cursors. The mode argument selects the type of cursor to use and the x and y arguments are used to set the origin of cursors that have anchor points. The ci argument is a PGPLOT color index, and is used to specify the color used to draw the cursor. The mode argument must be one of the following names:
  • norm
    No cursor augmentation.
  • line
    A line drawn between the specified anchor point and the position of the cursor.
  • rect
    An open rectangle with vertices at the specified anchor point and the position of the cursor.
  • yrng
    Two horizontal lines extending the width of the view surface, one passes through the Y coordinate of the specified anchor point and the other passes through the cursor. This was designed for selecting the end of a Y-axis range whose start has already been selected.
  • xrng
    Two vertical lines extending the height of the view surface, one passes through the x coordinate of the specified anchor point and the other passes through the cursor.
  • hline
    A horizontal line that passes through the cursor and extends the width of the view surface. This was designed for selecting the start of a Y-axis range.
  • vline
    A vertical line that passes through the cursor and extends the height of the view surface. This was designed for selecting the start of an X-axis range.
  • cross
    A crosshair cursor that passes through the cursor and extends the width and height of the view surface.
Note that this command will have no effect if the widget has not been opened to PGPLOT.

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.

clrcursor
This is used to remove any cursor augmentation that was previously established with the setcursor command.
world ...
This returns the floating point PGPLOT world coordinates that correspond to specified integer X-window coordinates (such as those reported in the %x and %y fields of Tk event bindings). It can take any of the following forms:
world x xoffset
This returns the X-axis PGPLOT world coordinate that corresponds to an offset of xoffset pixels in from the left edge of the PGPLOT window.
world y yoffset
This returns the Y-axis PGPLOT world coordinate that corresponds to an offset of yoffset pixels in from the top edge of the PGPLOT window.
world xy xoffset yoffset
This returns the PGPLOT X and Y-axis world coordinate pair that corresponds to an offset of xoffset pixels in from the left edge of the PGPLOT window and an offset of yoffset pixels in from the top edge of the PGPLOT window.
Note that if the widget is not open to PGPLOT, 0.0 will be returned for all pixel coordinates.
pixel ...
This returns the integer X-window coordinates that correspond to specified floating point PGPLOT world coordinates. It can take any of the following forms:
pixel x worldx
This returns the horizontal X-window pixel coordinate that corresponds to the given X-axis world coordinate.
pixel y worldy
This returns the vertical X-window pixel coordinate that corresponds to a given Y-axis world coordinate.
pixel xy worldx worldy
This returns the X-window pixel-coordinate pair that corresponds to the given world coordinates.
Note that if the widget is not open to PGPLOT, 0 will be returned for all world coordinates.
id
This returns the integer id that was returned by 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.

Handling widget resizes.
When an application is resized by the user, some or all of its nested widgets are resized to fit the new area. This means that a PGPLOT widget can get resized at unpredictable times, whereas PGPLOT assumes that the size of its view surface remains unchanged between the start of one page and the start of the next. To cope with this, 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:

  1. One way is to provide the widget with scrollbars, so that the user can interactively change which part of the PGPLOT window is visible. For example:
      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}
     
  2. The other is to establish a Tk event binding to redraw the plot whenever the user resizes the window. Provided that your redraw command calls 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}
    

Cursor facilities
The standard PGPLOT cursor functions (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.

Tcl button-press example
  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.

Tcl range-selection example

  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.

Tcl cursor-readout example
  # 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.

Private colormaps
The Tk 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:
  1. Set the value of the pgplot -maxcolors configuration option to as small a value as you actually need.
  2. Allocate a private colormap for the top level widget of your application.

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.

How to use the driver from C
The most efficient way to use the Tk PGPLOT driver is to handle user-interaction using a Tcl/Tk script and delegate drawing operations to Tcl-callable C functions. This section gives a complete example to illustrate how this is done. If you want to try this code, extract the following C-code fragments (eg. using cut and paste), assemble them into a .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.

C example program
  #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 -ldl
The 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.

Tcl example script

# 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.tcl
If 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 new
In addition, if you just run the program without specifying a script file:
  demo
then 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.tcl
or
  pgtkdemo pgtkdemo.tcl -colormap new
if 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.

Frequently asked questions and answers
The following is a collection of problems that you might initially encounter and suggestions for how to resolve them.

Why does the first plot that I draw appear to have the wrong size?
You probably attempted to plot in the widget before it had been displayed for the first time, so the 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

How do I stop a pgplot widget from complaining about there being insufficient colors?
See the private-colormaps section.

Martin Shepherd (mcs·astro.caltech.edu).