The canvas widget is a general-purpose widget for displaying graphical objects: lines, rectangles, circles, but also images and even other widgets. Furthermore it can be instructed to run commands in response to mouse clicks or keystrokes, thus making it suitable for interactive use - you could build a program for interactively drawing diagrams for instance.
Let us start with some short examples:
pack [canvas .c] .c create oval 100 100 200 200 -fill green .c create rectangle 120 150 180 200 -outline red -width 3
This creates a canvas widget with two graphical objects:
A green circle (oval) with a black border
A rectangle with a thick red border
Both the circle and the rectangle are created using two points — (100,100) and (200,200) for the circle and (120,150) and (180,200) for the rectangle. The pair of points defines the (rectangular) space that the objects is drawn in. As the two points (100,100) and (200,200) define a square, the result is a circle. In general it would be an ellipse. For instance:
.c create oval 100 100 200 300 -fill green
Here are two more objects:
.c create line 10 50 50 10 120 100 .c create line {10 50 50 10 120 100} -smooth 1
Each line has three points, but the second line is drawn with the smoothing option, making it a smooth curve rather than a series of line pieces. Also note that the coordinates are given as a list, rather than individual arguments. This form is very useful if you have a large number of points or if the number of points varies:
# # Polygons need not to be simple, as demonstrated by this pentagram # set angle [expr {3.1415926 * 4.0 / 5.0}] set coords {} for { set i 0 } { $i < 5 } { incr i } { set x [expr {cos($i*$angle)}] set y [expr {sin($i*$angle)}] lappend coords [expr {150 + 100 * $x}] [expr {150 + 100 * $y}] } .c create polygon $coords -fill green -width 2
Each creation command returns the ID of the graphical object. This allows you to manipulate the object:
set obj [.c create text 100 100 -text Hello] after 2000 [list .c itemconfig $obj -text Goodbye!]
Besides the object’s ID you can also use tags to manipulate the object, or better: all objects that have that tag:
# # This text is hidden by the rectangle # .c create text 100 100 -text Goodbye -tag later .c create text 100 120 -text "See you soon" -tag later .c create text 100 100 -text Hello -tag welcome .c create rectangle 50 50 250 250 -fill green # # Show the texts # after 500 {.c raise welcome} after 2500 { .c raise later .c lower welcome }
This example exhibits yet another property of the graphical objects: they are drawn in a particular order and you can influence that order via the raise and lower subcommands.
One thing to note about the canvas is that the y coordinate runs from top to bottom, as is common with computer graphics, but it may surprise you. So the origin (0,0) is at the top-left corner of the canvas.
If you just specify a number, that number is interpreted as the number of pixels. But you can use centimeters, millimeters, inches and points (1/72 of an inch) too:
.c create line 1c 2.5c 2c 3.5c
What is more: coordinates are considered to be floating-point numbers. This is very useful when scaling the object for instance:
set obj [.c create oval 100 100 110 110] for {set i 0} {$i < 10} {incr i} { after [expr {$i*100}] [list .c scale $obj 100 100 1.1 1.1] } for {set i 0} {$i < 10} {incr i} { after [expr {10+$i*100}] [list .c scale $obj 100 100 0.909 0.909] }
The scale subcommand scales the coordinates of the object by a certain factor (they may be different in both directions) with respect to a central point — (100,100) in this case.
You can change the coordinates of a given object directly too:
.c coords $obj 20 20 200 200
and the oval grows and moves but the other attributes (colour, filled or not, etc.) remain the same.
For interactive applications it is very useful to be able to handle mouse events or keystrokes. In Tcl/Tk this is done via bindings — you bind a particular command to a particular event. The canvas widgets supports two types of bindings:
Bindings to the widget as a whole
Bindings to individual graphical objects
Here we consider the second type, as the first type is not specific to the canvas widget.
Consider the following script:
.c create rectangle 20 20 60 60 -fill grey -tag enterleave .c create rectangle 60 20 100 60 -fill grey -tag enterleave .c create rectangle 20 60 60 100 -fill grey -tag enterleave .c create rectangle 60 60 100 100 -fill grey -tag enterleave .c bind enterleave <Enter> {.c itemconfig current -fill green} .c bind enterleave <Leave> {.c itemconfig current -fill grey}
If you move the mouse over these rectangles you will see that they turn green when it enters the rectangle and grey again when it leaves. The tag ‘current’ is a special tag that is managed by the canvas widget itself, and it indicates the topmost (last to be drawn) object that contains the mouse.
For the canvas, widgets and images that you want to put in it are just ‘ordinary’ graphical objects. Here is an example of how to create a widget inside a canvas:
button .c.button -text OK -command {.c create text 100 100 -text OK} .c create window 100 200 -window .c.button
The widget can be a child of the canvas, or a child of one of the ancestors of the canvas. Pressing the button will create a text ‘OK’ above the button, as you would expect.
Images can be handled in a similar way, there are in fact two types of images available: images and bitmaps, which are two-colour images. As there are a number of built-in bitmaps, we will take that as an example:
.c create bitmap 10 10 -bitmap question -anchor nw \ -background yellow -foreground red
Like all other graphical items, there are a number of options you can use to control the appearance of the image or the widget. In the above example we have used the -anchor option to control how the image is displayed with respect to the coordinates we give. The value ‘nw’ (north-west) means that the given coordinates refer to the upper-left corner of the image.
The canvas has a large number of subcommands and they are all documented in the manual page. Here are a few common ones, just to give an impression:
Tip:
If you issue the postscript subcommand while there is nothing visible on
the
screen, the PostScript file will be empty. You need to wait until the
canvas has been drawn. This is a side-effect that you need to be aware
of. Compare the results of the following script:
pack [canvas .c] .c create rectangle 100 100 200 200 -fill red .c postscript rectangle1.ps exit and pack [canvas .c] .c create rectangle 100 100 200 200 -fill red tkwait visibility . .c postscript rectangle2.ps exit
|
The program below will draw a fractal tree by first drawing a vertical line and then recursively drawing two lines at an angle to the first one. To create a bit more detail, it also calculates the colour for the new lines from the individual red, green and blue contributions. This way you can produce shaded graphics.
pack [canvas .c -width 400 -height 300 -bg white] proc drawBranch {xstart ystart length angle rgb} { # # Compose the colour # foreach {red green blue} $rgb {break} set colour [format "#%2.2x%2.2x%2.2x" $red $green $blue] # # Determine the end point # set phi [expr {$angle * 3.1415926 / 180.0}] set xend [expr {$xstart + $length * cos($phi)}] set yend [expr {$ystart + $length * sin($phi)}] # # The line thickness # set width 3 if { $length < 25 } { set width 2 } if { $length < 5 } { set width 1 } # # Draw the branch and the sub-branches # .c create line $xstart $ystart $xend $yend -fill $colour -width $width if { $length > 1.0 } { set rgbLeft [list [expr {int(0.8*$red)}] $green $blue] set rgbRight [list $red $green [expr {int(0.8*$blue)}]] set angleLeft [expr {$angle + 45.0}] set angleRight [expr {$angle - 45.0}] set length [expr {2.0*$length/3.0}] drawBranch $xend $yend $length $angleLeft $rgbLeft drawBranch $xend $yend $length $angleRight $rgbRight } } drawBranch 200 280 100 270 {255 0 255}
The first line starts with an angle of 270 to avoid the somewhat awkward direction of the vertical coordinate (later on it does not matter anymore because of the symmetry of the design).
Drawing stops automatically when the line length is shorter than a single pixel.
The colour contribution is reduced by 20% and the length by 33% per generation, figures that were found experimentally to give a nice result.
Note: This example also shows that the canvas widget is capable of displaying large amounts of graphical objects. There are more than 8000 individual lines in the tree.