The canvas widget is a general-purpose widget that you can program to display a variety of objects including arcs, images, lines, ovals, polygons, rectangles, text, and embedded windows.
Canvas widgets display objects such as lines and images, and each object can have bindings that respond to user input, or be animated under program control. The objects can be labeled with tags, and the tags can be configured with display attributes and event bindings. This chapter describes all the predefined canvas object types. Chapter 50 outlines the C programming interface for creating new canvas objects.
The coordinate space of the canvas has 0, 0 at the top left corner. Larger X coordinates are to the right, and larger Y coordinates are downward. The position and possibly the size of a canvas object is determined by a set of coordinates. Different objects are characterized by different numbers of coordinates. For example, text objects have two coordinates, x1 y1
, that specify their anchor point. A line can have many pairs of coordinates that specify the end points of its segments. The coordinates are set when the object is created, and they can be updated later with the coords
operation. By default, coordinates are in pixels. Append a coordinate with one of the following letters to change the units:
The tk scale
command, which is described on page 669, changes the mapping from pixels to other screen measures. Use it before creating the canvas.
The width
and height
attributes of the canvas determine the size of the viewable area. The scrollRegion
attribute of the canvas determines the boundaries of the canvas. Its value is four numbers that specify the upper-left and lower-right coordinates of the canvas. If you do not specify a scroll region, it defaults to the size of the viewable area. Example 37-1 creates a canvas that has a 1000 by 400 scrolling region, and a 300 by 200 viewing area. The canvas is connected to two scrollbars to provide horizontal and vertical scrolling:
Example 37-1. A large scrolling canvas
proc Scrolled_Canvas { c args } {
frame $c
eval {canvas $c.canvas
-xscrollcommand [list $c.xscroll set]
-yscrollcommand [list $c.yscroll set]
-highlightthickness 0
-borderwidth 0} $args
scrollbar $c.xscroll -orient horizontal
-command [list $c.canvas xview]
scrollbar $c.yscroll -orient vertical
-command [list $c.canvas yview]
grid $c.canvas $c.yscroll -sticky news
grid $c.xscroll -sticky ew
grid rowconfigure $c 0 -weight 1
grid columnconfigure $c 0 -weight 1
return $c.canvas
}
Scrolled_Canvas .c -width 300 -height 200
-scrollregion {0 0 1000 400}
=> .c.canvas
pack .c -fill both -expand true
The highlight thickness and border width are set to 0 in Example 37-1. Otherwise, these features occupy some of the canvas viewable area. If you want a raised border for your canvas, either use another frame, or remember to offset your positions to avoid having objects clipped by the borders.
Example 37-2 creates an object that you can drag around with the mouse. It introduces the use of tags to classify objects. In this case the movable
tag gets bindings that let you drag the item, so any item with the movable
tag shares this behavior. The example uses Scrolled_Canvas
from Example 37-1. When you use a scrolled canvas, you must map from the view coordinates reported by bindings to the canvas coordinates used to locate objects:
Example 37-2. The canvas "Hello, World!" example
proc CanvasHello {} { set can [Scrolled_Canvas .c -width 400 -height 100 -scrollregion {0 0 800 400}] pack .c -fill both -expand true # Create a text object on the canvas $can create text 50 50 -text "Hello, World!" -tag movable # Bind actions to objects with the movable tag $can bind movable <Button-1> {CanvasMark %x %y %W} $can bind movable <B1-Motion> {CanvasDrag %x %y %W} } proc CanvasMark { x y can} { global canvas # Map from view coordinates to canvas coordinates set x [$can canvasx $x] set y [$can canvasy $y] # Remember the object and its location set canvas($can,obj) [$can find closest $x $y] set canvas($can,x) $x set canvas($can,y) $y } proc CanvasDrag { x y can} { global canvas # Map from view coordinates to canvas coordinates set x [$can canvasx $x] set y [$can canvasy $y] # Move the current object set dx [expr $x - $canvas($can,x)] set dy [expr $y - $canvas($can,y)] $can move $canvas($can,obj) $dx $dy set canvas($can,x) $x set canvas($can,y) $y }
Example 37-2 creates a text
object and gives it a tag named movable
:
.c create text 50 50 -text "Hello, World!" -tag movable
The first argument after create
specifies the type, and the remaining arguments depend on the type of object being created. Each canvas object requires some coordinates, optionally followed by attribute value pairs. The coordinates can be provided as separate arguments or, beginning in Tk 8.3, as a single-argument list. The complete set of attributes for canvas objects are presented later in this chapter. A text
object needs two coordinates for its location.
The create
operation returns an ID for the object being created, which would have been 1 in this case. However, the code manipulates the canvas objects by specifying a tag instead of an object ID. A tag is a more general handle on canvas objects. Many objects can have the same tag, and an object can have more than one tag. You can define bindings on tags, and you can define attributes for tags that will be picked up by objects with those tags.
A tag name can be almost any string, but you should avoid spaces that can cause parsing problems and pure numbers that get confused with object IDs. There are two predefined tags: current
and all
. The current
tag applies to whatever object is under the mouse. The all
tag applies to all the objects on the canvas.
Many of the canvas operations take an argument that identifies objects. The value can be a tag name, or it can be the numerical object identifier returned by the create
operation. Also, beginning in Tk 8.3, you can specify (as a single argument) a logical combination of tags using the operators &&
(and), ||
(or), ^
(exclusive or), !
(not), and parenthesized subexpressions. For example, to change the fill color to red on all objects with a tag of highlight
or warning
, you could execute the following:
$can itemconfigure {highlight || warning} -fill red
To move all the objects with the tag plot1
or plot2
, but that don't also include the tag fixed
:
$can move {(plot1 || plot2) && !fixed} 50 0
Example 37-2 on page 559 defines behavior for objects with the movable
tag. Pressing button 1 starts a drag, and dragging with the mouse button down moves the object. The pathname of the canvas (%W
) is passed to CanvasMark
and CanvasDrag
so these procedures can be used on different canvases. The %x
and %y
keywords get substituted with the X and Y coordinate of the event:
$can bind movable <Button-1> {CanvasMark %x %y %W} $can bind movable <B1-Motion> {CanvasDrag %x %y %W}
The CanvasMark
and CanvasDrag
procedures let you drag the object around the canvas. Because CanvasMark
is applied to any object with the movable
tag, it must first find the object that was clicked on. First, the view coordinates are mapped into the canvas coordinates with the canvasx
and canvasy
operations:
set x [$can canvasx x] set y [$can canvasy y]
Once you do this, you can use the find
operation:
set canvas($can,obj) [$can find closest $x $y]
The actual moving is done in CanvasDrag
with the move
operation:
$can move $canvas($can,obj) $dx $dy
Try creating a few other object types and dragging them around, too:
$can create rect 10 10 30 30 -fill red -tag movable $can create line 1 1 40 40 90 60 -width 2 -tag movable $can create poly 1 1 40 40 90 60 -fill blue -tag movable
The CanvasMark
and CanvasDrag
procedures can be used with any canvas. They use the global array canvas
to keep their state, and they parameterize the indices with the canvas pathname to avoid conflict if there is more that one canvas in the application. If you get into this coding habit early, then you will find it easy to write reusable code.
Canvas tags do not work exactly like tags in the text widget. In the text widget, a tag is completely independent of the text. You can configure a text tag before it is applied to text, and the tag configuration is remembered even if you remove it from the text. A canvas tag, in contrast, must be applied to an object before you can configure it. If you configure a canvas tag that is not applied to any objects, those settings are forgotten. If you remove all the objects that share a tag, any settings associated with those tags are forgotten.
This section presents Example 37-3, which constructs a scale-like object with two sliders. The sliders represent the minimum and maximum values for some parameter. Clearly, the minimum cannot be greater than the maximum, and vice versa. The example creates three rectangles on the canvas. One rectangle forms the long axis of the slider that represents the range of possible values. The other two rectangles are markers that represent the values. Two text objects float below the markers to give the current values of the minimum and maximum.
The example introduces four canvas operations: bbox
, coords
, scale
, and move
. The bbox
operation returns the bounding box of an object or of all objects with a given tag. The coords
operation sets or queries the coordinates of an object. The scale
operation stretches an object, and the move
operation translates the position of an object.
Example 37-3. A min max scale canvas example
proc Scale2 {w min max {width {}} } { global scale2 if {$width == {}} { # Set the long dimension, in pixels set width [expr $max - $min] } # Save parameters set scale2($w,scale) [expr ($max-$min)/$width.0] set scale2($w,min) $min;# Current minimum set scale2($w,max) $max set scale2($w,Min) $min;# Lower bound to the scale set scale2($w,Max) $max set scale2($w,L) 10 set scale2($w,R) [expr $width+10] # Build from 0 to 100, then scale and move it later. # Distance between left edges of boxes is 100. # The box is 10 wide, therefore the slider is 110 long. # The left box sticks up, and the right one hangs down. canvas $w $w create rect 0 0 110 10 -fill grey -tag slider $w create rect 0 -4 10 10 -fill black -tag {left lbox} $w create rect 100 0 110 14 -fill red -tag {right rbox} $w create text 5 16 -anchor n -text $min -tag {left lnum} $w create text 105 16 -anchor n -text $max -tag {right rnum} -fill red # Stretch/shrink the slider to the right length set scale [expr ($width+10) / 110.0] $w scale slider 0 0 $scale 1.0 # move the right box and text to match new length set nx [lindex [$w coords slider] 2] $w move right [expr $nx-110] 0 # Move everything into view $w move all 10 10 # Make the canvas fit comfortably around the image set bbox [$w bbox all] set height [expr [lindex $bbox 3]+4] $w config -height $height -width [expr $width+30] # Bind drag actions $w bind left <Button-1> {Scale2Mark %W %x lbox} $w bind right <Button-1> {Scale2Mark %W %x rbox} $w bind left <B1-Motion> {Scale2Drag %W %x lbox} $w bind right <B1-Motion> {Scale2Drag %W %x rbox} }
The slider is constructed with absolute coordinates, and then it is scaled to the desired width. The alternative is to compute the coordinates based on the desired width. I have found it clearer to use numbers when creating the initial layout as opposed to using expr
or introducing more variables. The scale
operation stretches the slider bar to the correct length. The scale
operation takes a reference point, which in our case is (0, 0), and independent scale factors for the X and Y dimensions. The scale factor is computed from the width
parameter, taking into account the extra length added (10) so that the distance between the left edge of the slider boxes is $width
:
The move
operation repositions the right box and right hanging text. If the marker boxes are scaled, their shape gets distorted. The coords
operation returns a list of four numbers: x1 y1 x2 y2
. The distance to move is just the difference between the new right coordinate and the value used when constructing the slider initially. The box and text share the same tag, right
, so they are both moved with a single move
operation:
set nx [lindex [$w coords slider] 2] $w move right [expr $nx-110] 0
After the slider is constructed, it is shifted away from (0, 0), which is the upper-left corner of the canvas. The bbox
operation returns four coordinates: x1 y1 x2 y2
, that define the bounding box of the items with the given tag. In the example, y1
is zero, so y2
gives us the height of the image. The information returned by bbox
can be off by a few pixels, and the example needs a few more pixels of height to avoid clipping the text. The width is computed based on the extra length added for the marker box, the 10 pixels the whole image was shifted, and 10 more for the same amount of space on the right side:
set bbox [$w bbox all] set height [expr [lindex $bbox 3]+4] $w config -height $height -width [expr $width+30]
Bindings are defined for the box and hanging text. The general tags left
and right
are used for the bindings. This means that you can drag either the box or the text to move the slider. The pathname of the canvas is passed into these procedures so that you could have more than one double slider in your interface:
$w bind left <Button-1> {Scale2Mark %W %x lbox} $w bind right <Button-1> {Scale2Mark %W %x rbox} $w bind left <B1-Motion> {Scale2Drag %W %x lbox} $w bind right <B1-Motion> {Scale2Drag %W %x rbox}
Example 37-4. Moving the markers for the min max scale
proc Scale2Mark { w x what } { global scale2 # Remember the anchor point for the drag set scale2($w,$what) $x } proc Scale2Drag { w x what } { global scale2 # Compute delta and update anchor point set x1 $scale2($w,$what) set scale2($w,$what) $x set dx [expr $x - $x1] # Find out where the boxes are currently set rx [lindex [$w coords rbox] 0] set lx [lindex [$w coords lbox] 0] if {$what == "lbox"} { # Constrain the movement to be between the # left edge and the right marker. if {$lx + $dx > $rx} { set dx [expr $rx - $lx] set scale2($w,$what) $rx } elseif {$lx + $dx < $scale2($w,L)} { set dx [expr $scale2($w,L) - $lx] set scale2($w,$what) $scale2($w,L) } $w move left $dx 0 # Update the minimum value and the hanging text set lx [lindex [$w coords lbox] 0] set scale2($w,min) [expr int($scale2($w,Min) + ($lx-$scale2($w,L)) * $scale2($w,scale))] $w itemconfigure lnum -text $scale2($w,min) } else { # Constrain the movement to be between the # right edge and the left marker if {$rx + $dx < $lx} { set dx [expr $lx - $rx] set scale2($w,$what) $lx } elseif {$rx + $dx > $scale2($w,R)} { set dx [expr $scale2($w,R) - $rx] set scale2($w,$what) $scale2($w,R) } $w move right $dx 0 # Update the maximum value and the hanging text set rx [lindex [$w coords right] 0] set scale2($w,max) [expr int($scale2($w,Min) + ($rx-$scale2($w,L)) * $scale2($w,scale))] $w itemconfigure rnum -text $scale2($w,max) } } proc Scale2Value {w} { global scale2 # Return the current values of the double slider return [list $scale2($w,min) $scale2($w,max)] }
The Scale2Mark
procedure initializes an anchor position, scale2($w,$what)
, and Scale2Drag
uses this to detect how far the mouse has moved. The change in position, dx
, is constrained so that the markers cannot move outside their bounds. The anchor is updated if a constraint was used, and this means that the marker will not move until the mouse is moved back over the marker. (Try commenting out the assignments to scale2($w,$what)
inside the if
statement.) After the marker and hanging text are moved, the value of the associated parameter is computed based on the parameters of the scale. The Scale2Value
procedure queries the current values of the double slider.
The next several sections describe the built-in object types for the canvas: arc
, bitmap
, image
, line
, oval
, polygon
, rectangle
, text
, and window
. Each object has its own set of attributes, and some attributes are found on most or all object types. Table 37-1 lists the common item attributes found on all or most objects. All -active
and -disabled
attributes were added in Tk 8.3, as were the -state
attribute, the -offset
attribute, and those attributes related to dashes.
Table 37-1. Common canvas item attributes
-dash pattern -activedash pattern -disableddash pattern | The dash pattern of the line or outline when in the |
| The starting offset distance into the pattern provided by the |
-fill color -activefill color -disabledfill color | The color of the interior of the object when in the |
-stipple bitmap -activestipple bitmap -disabledstipple bitmap | The stipple pattern for the fill when in the |
| The stipple offset in the form x,y or side. side can be |
-outline color -activeoutline color -disabledoutline color | The color of the outline when in the |
-outlinestipple bitmap -activeoutlinestipple bitmap -disabledoutlinestipple bitmap | The stipple pattern for the outline when in the |
-width num -activewidth num -disabledwidth num | Width, in canvas coordinates, of the line or outline when in the |
|
|
| List of tags for the object. |
Every object has a -tags
attribute used to label the object with a list of symbolic names. Most objects, even text
objects, specify their color with the -fill
attribute; only the bitmap
object uses -foreground
and -background
. If the object has a border, the color of the border is specified with -outline
, and the thickness of the outline is specified with -width
. Starting in Tk 8.3, lines and objects with borders have a variety of -dash
attributes for drawing dashed lines and borders.
Tk 8.3 added the -state
attribute to the canvas widget and all canvas objects. The canvas state
attribute can be set to normal
(the default) or disabled
, which provides a default state for all objects on the canvas. If an individual canvas object's -state
attribute is the empty string (the default), then it inherits the canvas state. However, you can override the “global” canvas state for an individual object by setting its -state
attribute to normal
, disabled
, or hidden
.
An object in the normal
state is visible and any bindings defined for it are fully functional. Additionally, if the mouse is over a normal
object, it is activated and any -active*
attributes defined for the object take effect. As you would expect, an object in the hidden
state is not visible, and its bindings are inactive. An object in the disabled state is visible, but its bindings are inactive and it does not activate when the mouse is over it; additionally, any -disabled*
attributes defined for the object take effect.
Tk 8.3 introduced the ability to draw lines and object outlines using dashed lines. The primary object attribute for controlling the dash pattern is -dash
, although -activedash
and -disableddash
attributes are available for controlling the dash pattern in different object states.
Each of these attributes accepts a dash pattern as a value. One dash pattern format is a list of integers. Each element represents the number of pixels of a line segment. Only the odd segments are drawn using the -outline
color. The other segments are drawn transparent. For example the following command draws a line with 6-pixel dashes separated by 2-pixel spaces:
$c create line -dash {6 2}
The other dash pattern format is a string containing any combination of the characters shown in Table 37-2.
Table 37-2. Canvas dash pattern characters
| Dash 1/2 of the length of the following space |
| Dash equal to the length of the following space |
| Dash 1 1/2 times the length of the following space |
| Dash double the length of the following space |
| Doubles the length of the space |
For example, the dash pattern {_. ,}
is roughly equivalent to {8 4 2 8 4 4}
The main difference of the string-based syntax versus the list-based syntax is that it the string-based syntax is shape-conserving. This means that all values in the dash list are multiplied by the line width before display. This assures that “.
” is always displayed as a dot and “-” as a dash regardless of the line width.
Finally, the -dashoffset
attribute specifies the starting offset (in pixels) into the pattern provided by -dash
.
On systems that support only a limited set of dash patterns, the dash pattern is displayed as the closest dash pattern that is available. For example, on Windows the dash patterns {.}
and {,}
and {. }
and {, }
are displayed identically.
An arc is a section of an oval. The dimensions of the oval are determined by four coordinates that are its bounding box. The arc is then determined by two angles, the start
angle and the extent
. The region of the oval can be filled or unfilled, and there are three different ways to define the fill region. The pieslice
style connects the arc with the center point of the oval. The chord
style connects the two end points of the arc. The arc
style just draws the arc itself and there is no fill. Example 37-5 shows three arcs with the same bounding box but different styles and angles:
Example 37-5. Canvas arc
items
# $c is a canvas $c create arc 10 10 100 100 -start 45 -extent -90 -style pieslice -fill orange -outline black $c create arc 10 10 100 100 -start 135 -extent 90 -style chord -fill blue -outline white -width 4 $c create arc 10 10 100 100 -start 255 -extent 45 -style arc -outline black -width 3
An arc
object support all of the object attributes listed in Table 37-1. Table 37-3 lists the additional attributes supported by arc
objects.
A bitmap is a simple graphic with a foreground and background color. One bit per pixel is used to choose between the foreground and the background. If you do not specify a background color, the background bits are clear and the canvas background shows through. A canvas bitmap
item is positioned with two coordinates and an anchor position. Its size is determined by the bitmap data. The bitmap
itself is specified with a symbolic name or by the name of a file that contains its definition. If the name begins with an @
, it indicates a file name. The bitmaps built into Tk are shown in the example below. Chapter 50 outlines the C interface for registering bitmaps under a name.
Example 37-6. Canvas bitmap
items
set o [$c create bitmap 10 10 -bitmap @candle.xbm -anchor nw -background white -foreground blue] set x [lindex [$c bbox $o] 2] ;# Right edge of bitmap foreach builtin {error gray12 gray50 hourglass info questhead question warning} { incr x 20 set o [$c create bitmap $x 30 -bitmap $builtin -anchor c] set x [lindex [$c bbox $o] 2] }
A bitmap
object supports only the -state
and -tags
attributes listed in Table 37-1. Table 37-4 lists the additional attributes supported by bitmap
objects.
Table 37-4. Bitmap attributes
| Anchor: |
-background color -activebackground color -disabledbackground color | The background color (for zero bits) when in the |
-bitmap name -activebitmap name -disabledbitmap name | The bitmap to display when in the |
-foreground color -activeforeground color -disabledforeground color | The foreground color (for one bits) when in the |
The canvas image
objects use the general image mechanism of Tk. You must first define an image using the image
command, which is described in Chapter 41 in the section Bitmaps and Images. Once you have defined an image, all you need to specify for the canvas is its position, anchor point, and any tags. The size and color information is set when the image is defined. If an image is redefined, anything displaying that image automatically gets updated. Example 37-7 creates one image and puts six instances of it on a canvas:
Example 37-7. Canvas image
items
image create bitmap hourglass2 -file hourglass.bitmap -maskfile hourglass.mask -background white -foreground blue for {set x 20} {$x < 300} {incr x 20} { $c create image $x 10 -image hourglass2 -anchor nw incr x [image width hourglass2] }
An image
object supports only the -state
and -tags
attributes listed in Table 37-1. Table 37-5 lists the additional attributes supported by image
objects.
A line has two or more sets of coordinates, where each set of coordinates defines an end point of a line segment. The segments can be joined in several different styles, and the whole line can be drawn with a spline fit as opposed to straight-line segments. The next example draws a line in two steps. In the first pass, single-segment lines are drawn. When the stroke completes, these are replaced with a single line segment that is drawn with a spline curve.
Example 37-8. A canvas stroke drawing example
proc StrokeInit {} { canvas .c ; pack .c bind .c <Button-1> {StrokeBegin %W %x %y} bind .c <B1-Motion> {Stroke %W %x %y} bind .c <ButtonRelease-1> {StrokeEnd %W %x %y} } proc StrokeBegin { w x y } { global stroke catch {unset stroke} set stroke(N) 0 set stroke(0) [list $x $y] } proc Stroke { w x y } { global stroke set coords $stroke($stroke(N)) lappend coords $x $y incr stroke(N) set stroke($stroke(N)) [list $x $y] # eval gets the coordinates into individual arguments eval {$w create line} $coords {-tag segments} } proc StrokeEnd { w x y } { global stroke set coords {} for {set i 0} {$i <= $stroke(N)} {incr i} { append coords $stroke($i) " " } $w delete segments eval {$w create line} $coords {-tag line -joinstyle round -smooth true -arrow last} }
The arrow attribute adds an arrow head to the end of the stroke. If you try this example you will notice that the arrow is not always aimed as you expect. This is because there are often many points generated close together as you release the mouse button. In fact, the X and Y coordinates seen by StrokeEnd
are always the same as those seen by the last Stroke
call. If you add this duplicate point to the end of the list of points, no arrowhead is drawn at all. In practice you might want to make Stroke
filter out points that are too close together.
A line
object supports all of the attributes listed in Table 37-1 except for -offset
, the -outline
family of attributes, and the -outlinestipple
family of attributes. Remember that the -fill
attribute controls the color in which the line is drawn (not the -outline
attribute, as is common for other canvas items). Table 37-6 lists the additional attributes supported by line
objects. The capstyle
affects the way the ends of the line are drawn. The joinstyle
affects the way line segments are joined together. The capstyle
and joinstyle
attributes are from the X window system and may not be implemented on the Macintosh and Windows platforms.
Table 37-6. Line attributes
| Arrow location: |
| Three parameters that describe the shape of the arrow. |
| Line ends: |
| Line joints: |
| If |
| Number of line segments that approximate the spline. |
An oval
is defined by two sets of coordinates that define its bounding box. If the box is square, a circle is drawn. You can set the color of the interior of the oval as well as the outline of the oval. A sampler of ovals is shown in Example 37-9.
Example 37-9. Canvas oval
items
$c create oval 10 10 80 80 -fill red -width 4 $c create oval 100 10 150 80 -fill blue -width 0 $c create oval 170 10 250 40 -fill black -stipple gray12
An oval
object support all of the object attributes listed in Table 37-1. There are no additional attributes supported by oval
objects.
A polygon
is a closed shape specified by sets of points, one for each vertex of the polygon. The vertices can be connected with smooth or straight lines. Example 37-10 creates a stop sign. The picture is centered at (0, 0) and then moved fully onto the canvas:
Example 37-10. Canvas polygon
items
$c create poly 20 -40 40 -20 40 20 20 40 -20 40 -40 20 -40 -20 -20 -40 -fill red -outline white -width 5 $c create text 0 0 -text STOP -fill white -font {helvetica 18 bold} $c move all 50 50
A polygon
object support all of the object attributes listed in Table 37-1. Table 37-7 lists the additional attributes supported by polygon
objects
A rectangle
is specified with two coordinates that are its opposite corners. A rectangle can have a fill color and an outline color. If you do not specify a fill, then the background of the canvas (or other objects) shows through. If you stipple the fill, the background also shows through the clear bits of the stipple pattern. You must use a second rectangle if you want the stippled fill to completely hide what is behind it. Example 37-11 drags out a box as the user drags the mouse. All it requires is remembering the last rectangle drawn so that it can be deleted when the next box is drawn:
Example 37-11. Dragging out a box
proc BoxInit {} { canvas .c -bg white ; pack .c bind .c <Button-1> {BoxBegin %W %x %y} bind .c <B1-Motion> {BoxDrag %W %x %y} } proc BoxBegin { w x y } { global box set box($w,anchor) [list $x $y] catch {unset box($w,last)} } proc BoxDrag { w x y } { global box catch {$w delete $box($w,last)} set box($w,last) [eval {$w create rect} $box($w,anchor) {$x $y -tag box}] }
The example uses box($w,anchor)
to record the start of the box. This is a list with the X and Y coordinates. The eval
command is used so that this list can be spliced into the create rect
command. Note that as of Tk 8.3 this would not be necessary as the create rect
command can now accept a list of coordinates as a single argument.
A rectangle
object support all of the object attributes listed in Table 37-1. There are no additional attributes supported by rectangle
objects.
The canvas text
item provides yet another way to display and edit text. It supports selection, editing, and it can extend onto multiple lines. The position of a text
item is specified by one set of coordinates and an anchor position. The size of the text is determined by the number of lines and the length of each line. A new line is started if there is a newline in the text string. If a width
is specified, in screen units, then any line that is longer than this is wrapped onto multiple lines. The wrap occurs before a space character.
The editing and selection operations for text
items use indices to specify positions within a given text item. These are very similar to those used in the entry widget. Table 37-8 summarizes the indices for canvas text
items.
Table 37-8. Indices for canvas text
items
| Index of the first character. |
| Index just past the last character. |
| Index a character, where |
| Index of the character right after the insertion cursor. |
| Index of the first character in the selection. |
| Index of the last character in the selection. |
| Index of the character under the specified X and Y coordinate. |
There are several canvas operations that manipulate text items. These are similar to some of the operations of the entry widget. The dchars
and select to
operations treat the second index differently than the corresponding operations in the entry and text widget. The character at the second index is included in the operation (e.g., deleted), while in the entry and text widget it is not.
The canvas text operations are parameterized by the tag or ID of the canvas object being manipulated. If the tag refers to more than one object, then the operations apply to the first object in the display list that supports an insert cursor. The display list is described on page 581. Table 37-9 summarizes the operations on text
items. In the table $t
is a text item or tag and $c
is a canvas.
Table 37-9. Canvas operations that apply to text
items
| Deletes the characters from |
| Sets input focus to the specified item, or returns the ID of the item with the focus if it is not given. |
| Sets the insert cursor to just before |
| Returns the numerical value of |
| Inserts the string just before |
| Moves the boundary of an existing selection. |
| Clears the selection. |
| Starts a selection. |
| Returns the ID of the selected item, if any. |
| Extends the selection to the specified |
There are no default bindings for canvas text
items. Example 37-12 sets up some basic bindings for canvas text items. The <Button-1>
and <Button-2>
bindings are on the canvas as a whole. The rest of the bindings are on items with the text
tag. You must add the text
tag to text items that should share the editable text behavior. Small procedures are introduced for each binding to hide the details and any local variables needed in the operations.
The CanvasFocus
procedure uses the canvas find overlapping
operation to see if a text object has been clicked. This must be used because find closest
finds an object no matter how far away it is. It also uses the type
operation to make sure only text objects are given the focus. If you want other object types to respond to key events, you should change that.
The CanvasPaste
procedure does one of two things. It pastes the selection into the canvas item that has the focus. If no item has the focus, then a new text
item is created with the selection as its value:
Example 37-12. Simple edit bindings for canvas text
items
proc Canvas_EditBind { c } { bind $c <Button-1> {CanvasFocus %W [%W canvasx %x] [%W canvasy %y]} bind $c <Button-2> {CanvasPaste %W [%W canvasx %x] [%W canvasy %y]} bind $c <<Cut>> {CanvasTextCopy %W; CanvasDelete %W} bind $c <<Copy>> {CanvasTextCopy %W} bind $c <<Paste>> {CanvasPaste %W} $c bind text <Button-1> {CanvasTextHit %W [%W canvasx %x] [%W canvasy %y]} $c bind text <B1-Motion> {CanvasTextDrag %W [%W canvasx %x] [%W canvasy %y]} $c bind text <Delete> {CanvasDelete %W} $c bind text <Control-d> {CanvasDelChar %W} $c bind text <Control-h> {CanvasBackSpace %W} $c bind text <BackSpace> {CanvasBackSpace %W} $c bind text <Control-Delete> {CanvasErase %W} $c bind text <Return> {CanvasNewline %W} $c bind text <Any-Key> {CanvasInsert %W %A} $c bind text <Key-Right> {CanvasMoveRight %W} $c bind text <Control-f> {CanvasMoveRight %W} $c bind text <Key-Left> {CanvasMoveLeft %W} $c bind text <Control-b> {CanvasMoveLeft %W} } proc CanvasFocus {c x y} { focus $c set id [$c find overlapping [expr $x-2] [expr $y-2] [expr $x+2] [expr $y+2]] if {($id == {}) || ([$c type $id] != "text")} { set t [$c create text $x $y -text "" -tags text -anchor nw] $c focus $t $c select clear $c icursor $t 0 } } proc CanvasTextHit {c x y {select 1}} { $c focus current $c icursor current @$x,$y $c select clear $c select from current @$x,$y } proc CanvasTextDrag {c x y} { $c select to current @$x,$y } proc CanvasDelete {c} { if {[$c select item] != {}} { $c dchars [$c select item] sel.first sel.last } elseif {[$c focus] != {}} { $c dchars [$c focus] insert } } proc CanvasTextCopy {c} { if {[$c select item] != {}} { clipboard clear set t [$c select item] set text [$c itemcget $t -text] set start [$c index $t sel.first] set end [$c index $t sel.last] clipboard append [string range $text $start $end] } elseif {[$c focus] != {}} { clipboard clear set t [$c focus] set text [$c itemcget $t -text] clipboard append $text } } proc CanvasDelChar {c} { if {[$c focus] != {}} { $c dchars [$c focus] insert } } proc CanvasBackSpace {c} { if {[$c select item] != {}} { $c dchars [$c select item] sel.first sel.last } elseif {[$c focus] != {}} { set _t [$c focus] $c icursor $_t [expr [$c index $_t insert]-1] $c dchars $_t insert } } proc CanvasErase {c} { $c delete [$c focus] } proc CanvasNewline {c} { $c insert [$c focus] insert } proc CanvasInsert {c char} { $c insert [$c focus] insert $char } proc CanvasPaste {c {x {}} {y {}}} { if {[catch {selection get} _s] && [catch {selection get -selection CLIPBOARD} _s]} { return ;# No selection } set id [$c focus] if {[string length $id] == 0 } { set id [$c find withtag current] } if {[string length $id] == 0 } { # No object under the mouse if {[string length $x] == 0} { # Keyboard paste set x [expr [winfo pointerx $c] - [winfo rootx $c]] set y [expr [winfo pointery $c] - [winfo rooty $c]] } CanvasFocus $c $x $y } else { $c focus $id } $c insert [$c focus] insert $_s } proc CanvasMoveRight {c} { $c icursor [$c focus] [expr [$c index current insert]+1] } proc CanvasMoveLeft {c} { $c icursor [$c focus] [expr [$c index current insert]-1] }
Of the attributes listed in Table 37-1, text
objects support only the -fill
family of attributes, the -stipple
family of attributes, and the -state
and -tags
attributes. Table 37-10 specifies the additional attributes for text
items. Note that there are no foreground and background attributes. Instead, the fill color specifies the color for the text. It is possible to stipple the text as well. Additionally, the -width
attribute is treated differently than for other canvas objects.
Example 37-13. Using a canvas to scroll a set of widgets
proc Example37–13 { top title labels } { # Create a resizable toplevel window toplevel $top wm minsize $top 200 100 wm title $top $title # Create a frame for buttons, # Only Dismiss does anything useful set f [frame $top.buttons -bd 4] button $f.quit -text Dismiss -command "destroy $top" button $f.save -text Save button $f.reset -text Reset pack $f.quit $f.save $f.reset -side right pack $f -side top -fill x # Create a scrolling canvas frame $top.c canvas $top.c.canvas -width 10 -height 10 -yscrollcommand [list $top.c.yscroll set] scrollbar $top.c.yscroll -orient vertical -command [list $top.c.canvas yview] pack $top.c.yscroll -side right -fill y pack $top.c.canvas -side left -fill both -expand true pack $top.c -side top -fill both -expand true Scrolled_EntrySet $top.c.canvas $labels } proc Scrolled_EntrySet { canvas labels } { # Create one frame to hold everything # and position it on the canvas set f [frame $canvas.f -bd 0] $canvas create window 0 0 -anchor nw -window $f # Create and grid the labeled entries set i 0 foreach label $labels { label $f.label$i -text $label entry $f.entry$i grid $f.label$i $f.entry$i grid $f.label$i -sticky w grid $f.entry$i -sticky we incr i } set child $f.entry0 # Wait for the window to become visible and then # set up the scroll region based on # the requested size of the frame, and set # the scroll increment based on the # requested height of the widgets tkwait visibility $child set bbox [grid bbox $f 0 0] set incr [lindex $bbox 3] set width [winfo reqwidth $f] set height [winfo reqheight $f] $canvas config -scrollregion "0 0 $width $height" $canvas config -yscrollincrement $incr set max [llength $labels] if {$max > 10} { set max 10 } set height [expr $incr * $max] $canvas config -width $width -height $height } Example37–13 .ex "An example" { alpha beta gamma delta epsilon zeta eta theta iota kappa lambda mu nu xi omicron pi rho sigma tau upsilon phi chi psi omega}
A window
object supports only the -state
and -tags
attributes listed in Table 37-1. Table 37-11 lists the additional attributes supported by window
objects. Note that the -width
attribute is treated differently than for other canvas objects.
Table 37-11. Window attributes
| Anchor: |
| The height, in screen units, for the widget. If the value is an empty string (default), then the window is given whatever height it requests internally. |
| The width, in screen units, for the widget. If the value is an empty string (default), then the window is given whatever width it requests internally. |
| The name of the widget to display within the canvas. |
Table 37-12 summarizes the operations on canvas widgets. In the table, $c
is a canvas. $t
represents a numerical object ID, a canvas tag or — in Tk 8.3 or later — a logical combination of tags using the operators &&
(and), ||
(or), ^
(exclusive or), !
(not), and parenthesized subexpressions, grouped as a single argument (for example, {(plot1 || plot2) && !fixed}
). In some cases, an operation only applies to a single object. In these cases, if a tag or tag expression identifies several objects, the first object in the display list is operated on.
The canvas display list refers to the global order among canvas objects. New objects are put at the end of the display list. Objects later in the display list obscure objects earlier in the list. The term above refers to objects later in the display list.
Table 37-9 describes several of the canvas operations that only apply to text
objects. They are dchars
, focus
, index
, icursor
, insert
, and select.
Table 37-12 does not repeat those operations.
Table 37-12. Operations on a canvas
widget
Adds | |
| Adds |
| Adds |
| Adds |
| Adds |
| Adds |
| Adds |
Returns the bounding box of the items identified by the tag(s) in the form | |
Sets or queries the bindings of canvas items. | |
Maps from the X screen coordinate | |
| Maps from screen Y to canvas Y. |
| Returns the value of |
| Queries or updates the attributes of the canvas. |
Queries or modifies the coordinates of the item. As of Tk 8.3, a list of coordinates can be provided as a single argument. | |
| Creates a canvas object of the specified |
| Deletes the item(s) specified by the tag(s) or ID(s). |
| Removes the specified tags from the items identified by |
| Returns the IDs of the tags that match the |
| Returns the tags associated with the first item identified by |
| Returns the value of |
| Queries or reconfigures item |
| Moves the items identified by |
| Moves |
| Generates Postscript. Table 37-13 lists options. |
| Moves the items identified by |
| Scales the coordinates of the items identified by |
| Sets a mark for a scrolling operation. |
| Scrolls the canvas from the previous mark. |
| Returns the type of the first item identified by |
| Returns two fractions between zero and one that describes the amount of the canvas off-screen to the left and the amount of the canvas displayed. |
| Positions the canvas so that |
| Scrolls |
| Returns two fractions between zero and one that describes the amount of the canvas off screen to the top and the amount of the canvas displayed. |
| Positions the text so that |
| Scrolls |
The postscript
operation generates Postscript based on the contents of a canvas. One limitation in earlier versions of Tk is that images and embedded windows are not captured in the Postscript output. As of Tk 8.3, images are included in the generated Postscript. Also, as of Tk 8.3 for Unix and Tk 8.4.1 for Windows, embedded windows are included in the generated Postscript if they are currently displayed on the screen (that is, displayed within the canvas's viewport and not obscured by other windows).
Table 37-13 summarizes all the options for generating Postscript.
Table 37-13. Canvas postscript
options
| The channel identifier of a channel already opened for writing. The Postscript is written to that channel, and the channel is left open for further writing at the end of the operation. If |
| The index of |
|
|
| The file in which to write the Postscript. If |
| The index of |
| Height of the area to print. |
| Anchor: |
| Height of image on the output. A floating point number followed by |
| Width of image on the output. |
| The output X coordinate of the anchor point. |
| The output Y coordinate of the anchor point. |
| If true, rotates so that X axis is the long direction of the page (landscape orientation). |
| Width of the area to print. |
| Canvas X coordinate of left edge of the image. |
| Canvas Y coordinate of top edge of the image. |
You control what region of the canvas is printed with the -width
, -height
, -x
, and -y
options. You control the size and location of this in the output with the -pageanchor
, -pagex
, -pagey
, -pagewidth
, and -pageheight
options. The Postscript is written to the file named by the -file
option, to a channel already opened for writing whose channel identifier is provided by the -channel
option, or it is returned as the value of the postscript
canvas operation.
You control fonts with a mapping from X screen fonts to Postscript fonts. Define an array where the index is the name of the X font and the contents are the name and pointsize of a Postscript font.
Example 37-14 positions a number of text objects with different fonts onto a canvas. For each different X font used, it records a mapping to a Postscript font. The example has a fairly simple font mapping, and in fact the canvas would probably have guessed the same font mapping itself. If you use more exotic screen fonts, you may need to help the canvas widget with an explicit font map.
The example positions the output at the upper-left corner of the printed page by using the -pagex
, -pagey
, and -pageanchor
options. Recall that Postscript has its origin at the lower-left corner of the page.
Example 37-14. Generating Postscript from a canvas
proc Setup {} { global fontMap canvas .c pack .c -fill both -expand true set x 10 set y 10 set last [.c create text $x $y -text "Font sampler" -font fixed -anchor nw] # Create several strings in different fonts and sizes foreach family {times courier helvetica} { set weight bold switch -- $family { times { set fill blue; set psfont Times} courier { set fill green; set psfont Courier } helvetica { set fill red; set psfont Helvetica } } foreach size {10 14 24} { set y [expr 4+[lindex [.c bbox $last] 3]] # Guard against missing fonts if {[catch {.c create text $x $y -text $family-$weight-$size -anchor nw -fill $fill -font -*-$family-$weight-*-*-*-$size-*} it] == 0} { set fontMap(-*-$family-$weight-*-*-*-$size-*) [list $psfont $size] set last $it } } } set fontMap(fixed) [list Courier 12] } proc Postscript { c file } { global fontMap # Tweak the output color set colorMap(blue) {0.1 0.1 0.9 setrgbcolor} set colorMap(green) {0.0 0.9 0.1 setrgbcolor} # Position the text at the upper-left corner of # an 8.5 by 11 inch sheet of paper $c postscript -fontmap fontMap -colormap colorMap -file $file -pagex 0.i -pagey 11.i -pageanchor nw }
Table 37-14 lists the attributes for the canvas
widget. The table uses the resource name, which has capitals at internal word boundaries. In Tcl commands, the attributes are specified with a dash and are all lowercase.
Table 37-14. Canvas attribute resource names
| The normal background color. |
| The width of the border around the canvas. |
| Distance from mouse to an overlapping object. |
| Boolean. True constrains the view to the scroll region. |
| Cursor to display when mouse is over the widget. |
| Height, in screen units, of canvas display. |
| Focus highlight color when widget does not have focus. |
| Color for input focus highlight border. |
| Width of highlight border. |
| Background for area covered by insert cursor. |
| Width of cursor border. Nonzero for 3D effect. |
| Time, in milliseconds the insert cursor blinks off. |
| Time, in milliseconds the insert cursor blinks on. |
| Width of insert cursor. Default is 2. |
|
|
| Left, top, right, and bottom coordinates of the canvas. |
| Background color of selection. |
| Foreground color of selection. |
| Width of selection border. Nonzero for 3D effect. |
| The default state for canvas objects: |
| Controls focus changes from keyboard traversal. |
| Width in screen units for viewable area. |
| Tcl command prefix for horizontal scrolling. |
| Distance for one scrolling unit in the X direction. |
| Tcl command prefix for vertical scrolling. |
| Distance for one scrolling unit in the Y direction. |
The scroll region of a canvas defines the boundaries of the canvas coordinate space. It is specified as four coordinates, x1 y1 x2 y2
where (x1, y1
) is the top-left corner and (x2, y2
) is the lower-right corner. If the confine
attribute is true, then the canvas cannot be scrolled outside this region. It is OK to position canvas objects partially or totally off the scroll region; they just may not be visible. The scroll increment attributes determine how much the canvas is scrolled when the user clicks on the arrows in the scrollbar.
The closeEnough
attribute indicates how far away a position can be from an object and still be considered to overlap it. This applies to the overlapping
search criteria.
Coordinates for canvas items are stored internally as floating point numbers, so the values returned by the coords
operation will be floating point numbers. If you have a very large canvas, you may need to adjust the precision with which you see coordinates by setting the tcl_precision
variable. This is an issue if you query coordinates, perform a computation on them, and then update the coordinates. (Tcl 8.0 changed the default tcl_precision
from 6 to 12.)
The scale
operation scales the coordinates of one or more canvas items. It is not possible to scale the whole coordinate space. The main problem with this is that you can lose precision when scaling and unscaling objects because their internal coordinates are actually changed by the scale operation. For simple cases this is not a problem, but in extreme cases it can show up.
There is no resource database support built into the canvas and its items. You can, however, define resources and query them yourself. For example, you could define:
*Canvas.foreground: blue
This would have no effect by default. However, your code could look for this resource with option get
, and specify this color directly for the -fill
attribute of your objects:
set fg [option get $c foreground {}] $c create rect 0 0 10 10 -fill $fg
The main reason to take this approach is to let your users customize the appearance of canvas objects without changing your code.
The canvas implementation seems well optimized to handle lots of canvas objects. However, if an object like a line or a polygon has many points that define it, the implementation ends up scanning through these points linearly. This can adversely affect the time it takes to process mouse events in the area of the canvas containing such an item. Apparently any object in the vicinity of a mouse click is scanned to see if the mouse has hit it so that any bindings can be fired.
Example 38-5 on page 596 implements cut and paste of canvas objects. The example exchanges the logical description of canvas objects with the selection mechanism.
18.219.22.107