NAME
Tk::AbstractCanvas - Canvas with abstract center, zoom, and rotate
methods
VERSION
This documentation refers to version 1.0.56BHMOt of Tk::AbstractCanvas,
which was released on Sat Jun 11 17:22:24:55 2005.
SYNOPSIS
#!/usr/bin/perl -w
use strict;
use Tk;
use Tk::AbstractCanvas;
my $mwin = Tk::MainWindow->new();
my $acnv = $mwin->AbstractCanvas()->pack(
'-expand' => 1,
'-fill' => 'both');
#$acnv->invertY(1); # uncomment for inverted y-axis
$acnv->controlNav(1); # super Control Navigation
$acnv->rectToPoly(1);
#$acnv->ovalToPoly(1);
my $rect = $acnv->createRectangle( 7, 8, 24, 23,
'-fill' => 'red');
my $oval = $acnv->createOval( 23, 24, 32, 27,
'-fill' => 'green');
my $line = $acnv->createLine( 0, 1, 31, 32,
'-fill' => 'blue',
'-arrow' => 'last');
my $labl = $mwin->Label('-text' => 'Hello AbstractCanvas! =)');
my $wind = $acnv->createWindow(15, 16, '-window' => $labl);
$acnv->CanvasBind('<Button-1>' => sub {
$acnv->zoom(1.04); } );
$acnv->CanvasBind('<Button-2>' => sub {
$acnv->rotate($rect, 5);
$acnv->rotate($wind, 5); # should do nothing because can't
$acnv->rotate($oval, -5); # rotate about center
$acnv->rotate($line, -5);
} );
$acnv->CanvasBind('<Button-3>' => sub {
$acnv->zoom(0.97); } );
$acnv->viewAll();
MainLoop();
DESCRIPTION
AbstractCanvas provides an alternative to a Tk::Canvas object which
abstracts the coordinates of objects drawn onto the Canvas. This allows
the entire Canvas to be zoomed or rotated. Rotations modify the
coordinates that the original object was placed at but zooming the whole
canvas does not.
Tk::AbstractCanvas is derived from the excellent modules Tk::WorldCanvas
by Joseph Skrovan <Joseph@Skrovan.Com> (which was itself based on a
version by Rudy Albachten <Rudy@Albachten.Com>) and Tk::RotCanvas by Ala
Qumsieh <AQumsieh@CPAN.Org>.
2DO
- abstract full canvas rotations like zoom
- What else does AbstractCanvas need?
USAGE
DESCRIPTION
This module is a wrapper around the Canvas widget that maps the user's
coordinate system to the now mostly hidden coordinate system of the
Canvas widget. There is an option to make the abstract coordinates
y-axis increase in the upward direction.
*AbstractCanvas* is meant to be a useful alternative to a regular
Canvas. Typically, you should call $abstractcanvas->viewAll() (or
$abstractcanvas->viewArea(@box)) before calling MainLoop().
Most of the *AbstractCanvas* methods are the same as the *Canvas*
methods except that they accept and return abstract coordinates instead
of widget coordinates.
*AbstractCanvas* also adds a new rotate() method to allow rotation of
canvas objects by arbitrary angles.
NEW METHODS
*$abstractcanvas*->zoom(*zoom factor*)
Zooms the display by the specified amount. Example:
$abstractcanvas->CanvasBind('<i>' => sub {$abstractcanvas->zoom(1.25)});
$abstractcanvas->CanvasBind('<o>' => sub {$abstractcanvas->zoom(0.8)});
# If you are using the 'Scrolled' constructor as in:
my $abstractcanvas = $main->Scrolled('AbstractCanvas', -scrollbars => 'nw', ... )
# you want to bind the key-presses to the 'AbstractCanvas' Subwidget of Scrolled.
my $scrolled_canvas = $abstractcanvas->Subwidget('abstractcanvas');
# note the lowercase 'abstractcanvas' above
$scrolled_canvas->CanvasBind('<i>' => sub {$scrolled_canvas->zoom(1.25)});
$scrolled_canvas->CanvasBind('<o>' => sub {$scrolled_canvas->zoom(0.8)});
# If you don't like the scrollbars taking the focus when you
# <ctrl>-tab through the windows, you can:
$abstractcanvas->Subwidget('xscrollbar')->configure(-takefocus => 0);
$abstractcanvas->Subwidget('yscrollbar')->configure(-takefocus => 0);
*$abstractcanvas*->center(*x, y*)
Centers the display around abstract coordinates x, y. Example:
$abstractcanvas->CanvasBind('<2>' =>
sub {
$abstractcanvas->CanvasFocus;
$abstractcanvas->center($abstractcanvas->eventLocation);
}
);
*$abstractcanvas*->centerTags([-exact => {0 | 1}], *TagOrID, [TagOrID,
...]*)
Centers the display around the center of the bounding box containing
the specified TagOrID's without changing the current magnification
of the display.
'-exact => 1' will cause the canvas to be scaled twice to get an
accurate bounding box. This will be expensive if the canvas contains
a large number of objects.
*$abstractcanvas*->eventLocation()
Returns the abstract coordinates (x, y) of the last Xevent.
*$abstractcanvas*->panAbstract(*dx, dy*)
Pans the display by the specified abstract distances. panAbstract is
not meant to replace the xview/yview panning methods. Most user
interfaces will want the arrow keys tied to the xview/yview panning
methods (the default bindings), which pan in widget coordinates.
If you do want to change the arrow key-bindings to pan in abstract
coordinates using panAbstract you must disable the default arrow
key-bindings.
Example:
$mainwindow->bind('AbstractCanvas', '<Up>' => "");
$mainwindow->bind('AbstractCanvas', '<Down>' => "");
$mainwindow->bind('AbstractCanvas', '<Left>' => "");
$mainwindow->bind('AbstractCanvas', '<Right>' => "");
$abstractcanvas->CanvasBind( '<Up>' => sub {$abstractcanvas->panAbstract(0, 100);});
$abstractcanvas->CanvasBind( '<Down>' => sub {$abstractcanvas->panAbstract(0, -100);});
$abstractcanvas->CanvasBind( '<Left>' => sub {$abstractcanvas->panAbstract(-100, 0);});
$abstractcanvas->CanvasBind('<Right>' => sub {$abstractcanvas->panAbstract( 100, 0);});
This is not usually desired, as the percentage of the display that
is shifted will be dependent on the current display magnification.
*$abstractcanvas*->invertY([new_value])
Returns the state of whether the abstract coordinates y-axis is
inverted. The default of this value is 0. An optional parameter can
be supplied to set the value.
I guess all these options are not so private now that they have
accessors. I'm not sure where better to store them than privateData
though.
*$abstractcanvas*->rectToPoly([new_value])
Returns the state of whether created rectangles should be
auto-converted to polygons (so that they can be rotated about their
center by the rotate() method). The default of this value is 0. An
optional parameter can be supplied to set the value.
*$abstractcanvas*->ovalToPoly([new_value])
Returns the state of whether created ovals should be auto-converted
to polygons (so that they can be rotated about their center by the
rotate() method). The default of this value is 0. An optional
parameter can be supplied to set the value.
*$abstractcanvas*->controlNav([new_value])
Returns the state of whether special Control+MouseButton bindings
are set. When true, Control-Button-1 then dragging the mouse rotates
the whole AbstractCanvas, 2 pans, and 3 zooms. The default of this
value is 0 but this option is very useful if you don't need
Control-Button bindings for some other purpose. An optional
parameter can be supplied to set the value.
*$abstractcanvas*->controlNavBusy([new_value])
Returns the state of whether special Control+MouseButton actions are
busy. An optional parameter can be supplied to set the value.
*$abstractcanvas*->controlZoomScale([new_value])
Returns the value of the special controlNav zoom scale (activated by
Control-Button-3). The default value is -0.001. The zoom function
takes the distance dragged in pixels across the positive x and y
axes scaled by the zoom scale to determine the zoom result. If you
make the scale positive, it will invert the directions which zoom in
and out. If you make the number larger (e.g., -0.003 or 0.003),
zooming will become more twitchy. If you make the number smaller
(e.g., -0.0007 or 0.0007), zooming will happen more smoothly. An
optional parameter can be supplied to set the value.
*$abstractcanvas*->controlRotScale([new_value])
Returns the value of the special controlNav rotation scale
(activated by Control-Button-1). The default value is -0.3. The zoom
function takes the distance dragged in pixels across the positive x
and y axes scaled by the rotation scale to determine the rotation
result. If you make the scale positive, it will invert the
directions which rotate positive or negative degrees. If you make
the number larger (e.g., -0.7 or 0.7), rotations will become more
twitchy. If you make the number smaller (e.g., -0.07 or 0.07),
rotations will happen more smoothly. An optional parameter can be
supplied to set the value.
*$abstractcanvas*->controlScale([new_value])
Returns the underlying scale value of the abstract canvas relative
to the underlying canvas. An optional parameter can be supplied to
set the value although the zoom function should almost always be
employed instead of manipulating the scale through this accessor.
*$abstractcanvas*->eventX([new_value])
Returns the x-coordinate of where the last special
Control+MouseButton event occurred. An optional parameter can be
supplied to set the value.
*$abstractcanvas*->eventY([new_value])
Returns the y-coordinate of where the last special
Control+MouseButton event occurred. An optional parameter can be
supplied to set the value.
*$abstractcanvas*->rotate(*TagOrID, angle* ?,*x, y*?)
This method rotates the object identified by TagOrID by an angle
*angle*. The angle is specified in *degrees*. If a coordinate is
specified, then the object is rotated about that point. Otherwise,
the object is rotated about its center of mass if that is possible.
*$abstractcanvas*->pixelSize()
Returns the width (in abstract coordinates) of a pixel (at the
current magnification).
*$abstractcanvas*->rubberBand(*{0|1|2}*)
Creates a rubber banding box that allows the user to graphically
select a region. rubberBand is called with a step parameter '0',
'1', or '2'. '0' to start a new box, '1' to stretch the box, and '2'
to finish the box. When called with '2', the specified box is
returned (x1, y1, x2, y2)
The band color is set with the *AbstractCanvas* option '-bandColor'.
The default color is 'red'
Example, specify a region to delete:
$abstractcanvas->configure(-bandColor => 'purple');
$abstractcanvas->CanvasBind('<3>' => sub {$abstractcanvas->CanvasFocus;
$abstractcanvas->rubberBand(0)
});
$abstractcanvas->CanvasBind('<B3-Motion>' => sub {$abstractcanvas->rubberBand(1)});
$abstractcanvas->CanvasBind('<ButtonRelease-3>' => sub {my @box = $abstractcanvas->rubberBand(2);
my @ids = $abstractcanvas->find('enclosed', @box);
foreach my $id (@ids) {$abstractcanvas->delete($id)}
});
# Note: '<B3-ButtonRelease>' will be called for any ButtonRelease!
# You should use '<ButtonRelease-3>' instead.
# If you want the rubber band to look smooth during panning and
# zooming, add rubberBand(1) update calls to the appropriate key-bindings:
$abstractcanvas->CanvasBind( '<Up>' => sub {$abstractcanvas->rubberBand(1);});
$abstractcanvas->CanvasBind( '<Down>' => sub {$abstractcanvas->rubberBand(1);});
$abstractcanvas->CanvasBind( '<Left>' => sub {$abstractcanvas->rubberBand(1);});
$abstractcanvas->CanvasBind('<Right>' => sub {$abstractcanvas->rubberBand(1);});
$abstractcanvas->CanvasBind('<i>' => sub {$abstractcanvas->zoom(1.25); $abstractcanvas->rubberBand(1);});
$abstractcanvas->CanvasBind('<o>' => sub {$abstractcanvas->zoom(0.8); $abstractcanvas->rubberBand(1);});
This box avoids the overhead of bounding box calculations that can
occur if you create your own rubberBand outside of *AbstractCanvas*.
*$abstractcanvas*->viewAll([-border => number])
Displays at maximum possible zoom all objects centered in the
*AbstractCanvas*. The switch '-border' specifies, as a percentage of
the screen, the minimum amount of white space to be left on the
edges of the display. Default '-border' is 0.02.
*$abstractcanvas*->viewArea(x1, y1, x2, y2, [-border => number]))
Displays at maximum possible zoom the specified region centered in
the *AbstractCanvas*.
*$abstractcanvas*->viewFit([-border => number], *TagOrID*, [*TagOrID*,
...])
Adjusts the AbstractCanvas to display all of the specified tags. The
'-border' switch specifies (as a percentage) how much extra
surrounding space should be shown.
*$abstractcanvas*->getView()
Returns the rectangle of the current view (x1, y1, x2, y2)
*$abstractcanvas*->widgetx(*x*)
*$abstractcanvas*->widgety(*y*)
*$abstractcanvas*->widgetxy(*x, y*)
Convert abstract coordinates to widget coordinates.
*$abstractcanvas*->abstractx(*x*)
*$abstractcanvas*->abstracty(*y*)
*$abstractcanvas*->abstractxy(*x, y*)
Convert widget coordinates to abstract coordinates.
CHANGED METHODS
Abstract coordinates are supplied and returned to AbstractCanvas
methods instead of widget coordinates unless otherwise specified.
(i.e., These methods take and return abstract coordinates: center,
panAbstract, viewArea, find, coords, scale, move, bbox, rubberBand,
eventLocation, pixelSize, and create*)
*$abstractcanvas*->bbox([-exact => {0 | 1}], *TagOrID*, [*TagOrID*,
...])
'-exact => 1' is only needed if the TagOrID is not 'all'. It will
cause the canvas to be scaled twice to get an accurate bounding box.
This will be expensive if the canvas contains a large number of
objects.
Neither setting of exact will produce exact results because the
underlying canvas bbox method returns a slightly larger box to
insure that everything is contained. It appears that a number close
to '2' is added or subtracted. The '-exact => 1' zooms in to reduce
this error.
If the underlying canvas bbox method returns a bounding box that is
small (high error percentage) then '-exact => 1' is done
automatically.
*$abstractcanvas*->scale(*'all', xOrigin, yOrigin, xScale, yScale*)
Scale should not be used to 'zoom' the display in and out as it will
change the abstract coordinates of the scaled objects. Methods zoom,
viewArea, and viewAll should be used to change the scale of the
display without affecting the dimensions of the objects.
VIEW AREA CHANGE CALLBACK
*Tk::AbstractCanvas* option '-changeView' can be used to specify a
callback for a change of the view area. This is useful for updating a
second AbstractCanvas which is displaying the view region of the first
AbstractCanvas.
The callback subroutine will be passed the coordinates of the displayed
box (x1, y1, x2, y2). These arguments are added after any extra
arguments specifed by the user calling 'configure'.
Example:
$abstractcanvas->configure(-changeView => [\&changeView, $abstractcanvas2]);
# viewAll if AbstractCanvas2 widget is resized.
$abstractcanvas2->CanvasBind('<Configure>' => sub {$abstractcanvas2->viewAll});
{
my $viewBox;
sub changeView {
my ($canvas2, @coords) = @_;
$canvas2->delete($viewBox) if $viewBox;
$viewBox = $canvas2->createRectangle(@coords, -outline => 'orange');
}
}
SCROLL REGION NOTES
(1) The underlying *Tk::Canvas* has a '-confine' option which is set to
'1' by default. With '-confine => 1' the canvas will not allow the
display to go outside of the scroll region causing some methods to not
work accurately. For example, the 'center' method will not be able to
center on coordinates near to the edge of the scroll region; 'zoom out'
near the edge will zoom out and pan towards the center.
*Tk::AbstractCanvas* sets '-confine => 0' by default to avoid these
problems. You can change it back with:
$abstractcanvas->configure(-confine => 1);
(2) '-scrollregion' is maintained by *AbstractCanvas* to include all
objects on the canvas. '-scrollregion' will be adjusted automatically as
objects are added, deleted, scaled, moved, etc. (You can create a static
scrollregion by adding a border rectangle to the canvas.)
(3) The bounding box of all objects is required to set the scroll
region. Calculating this bounding box is expensive if the canvas has a
large number of objects. So for performance reasons these operations
will not immediately change the bounding box if they potentially shrink
it:
coords
delete
move
scale
Instead they will mark the bounding box as invalid, and it will be
updated at the next zoom or pan operation. The only downside to this is
that the scrollbars will be incorrect until the update.
If these operations increase the size of the box, changing the box is
trivial and the update is immediate.
ROTATION LIMITATIONS
As it stands, the module can only rotate the following object types
about their centers:
* Lines
* Polygons
* Rectangles (if rectToPoly(1) is called)
* Ovals (if ovalToPoly(1) is called)
All other object types (bitmap, image, arc, text, and window) can only
be rotated about another point. A warning is issued if the user tries to
rotate one of these object types about their center. Hopefully, more
types will be able to center-rotate in the future.
ROTATION DETAILS
To be able to rotate rectangles and ovals, this module is capable of
intercepting any calls to create(), createRectangle(), and createOval()
to change them to polygons. The user should not be alarmed if type()
returns *polygon* when a *rectangle* or *oval* was expected.
Additionally, if you call coords() on a polygonized object, expect to
have to manipulate all the additionally generated coordinates.
CHANGES
Revision history for Perl extension Tk::AbstractCanvas:
- 1.0.56BHMOt Sat Jun 11 17:22:24:55 2005
* original version
INSTALL
Please run:
`perl -MCPAN -e "install Tk::AbstractCanvas"`
or uncompress the package and run the standard:
`perl Makefile.PL; make; make test; make install`
or if you don't have `make` but Module::Build is installed
`perl Build.PL; perl Build; perl Build test; perl Build install`
LICENSE
Most source code should be Free! Code I have lawful authority over is
and shall be! Copyright: (c) 2005, Pip Stuart. Copyleft : This software
is licensed under the GNU General Public License (version 2). Please
consult the Free Software Foundation (http://FSF.Org) for important
information about your freedom.
AUTHORS
Pip Stuart (*Pip@CPAN.Org*)
AbstractCanvas is derived from code by: Joseph Skrovan
(*Joseph@Skrovan.Com*) Rudy Albachten (*Rudy@Albachten.Com*) Ala Qumsieh
(*AQumsieh@CPAN.Org*)