Page contents
canvas, a Tk command, creates and manipulates canvas widgets.
See Also edit
- Alternative Canvases
- lists widgets and toolkits with similar scope to the canvas widget
- canvas woes
- Tk examples
- Category Animation
- Category Games
- Category Toys
- tkpath
- SVG-like rendering for the canvas
Documentation edit
- official reference
- The Tk Canvas Widget
- by [Derek Fountain] , Linux Journal ,2004-02
Description edit
The canvas widget is Tk's workhorse for 2D graphical display, and can handle both bitmap and vector graphics. It was inspired by Joel Bartlett's ezd program, which provides structured graphics in a Scheme environment.A canvas is one of the most powerful concepts in Tk. It acts as a drawing plane for lines, rectangles, ovals, polygons, text, arcs (e.g. pieslices) as well as container widget to group other widgets, and it provides the ability to group elements together for creation, deletion, moving, etc.History edit
- Don't Fidget with Widgets, Draw!
- by Joel Bartlett ,1991
Item Types edit
Examples edit
Look at the pages for each item type for more demos.Arranged roughly in order from simple to complex:- A minimal starfield
- canvashelp
- expanding/collapsing text item
- Alternative buttons on canvas
- Canvas presentation graphics
- Example Canvas Widget
- Harmonic Color Wheel
- Canvas pixel painting
- Canvas rectangle marking
- Sun, moon, and stars
- Simple Canvas Demo
- Canvas item selections
- Canvas item selection by mouse click
- A symmetric doodler
- Tkeyes
- an xeyes clone
- Canvas buttons
- button-like behaviour on canvas items, by Kevin Kenny
- Canvas buttons in 3-D
- 3d buttons, by Kevin Kenny
- Drawing and editing polygons
- Bounding boxes of characters in canvases
- Notes on a canvas
- Crosshairs on a canvas
- Canvas object dropper
- XML Graph to canvas
- Simple XML report writer
Grouping Items edit
Atomic canvas tag item groups with tagsTk's canvas has a lot of support for manipulating multiple items at once by giving a few items the same tag, but I find sometimes I would like to group a bunch of items together, and treat them as if they were only one item.For example the current tag only applies to the item first under the mouse, so if you have a graphic composite of say, a text and a rectangle with the same tag, you can not use the most generic code:bind .c <1> {set oldX %x ; set oldY %y} bind .c <B1-Motion> {%W move current [expr %x-$oldX] [expr %y-$oldY]}because only one of the items, the rectangle or the text, will move. To enable both of the items to be moved at once, I implemented an idea I saw in Zinc, atomic groups.To do this I added two methods to the canvas, tagconf and tagcget, which allow you to modify the behaviour of the tag as opposed to the items under the tag.I propose an option -atomic which defaults to off. When it's turned on, if an individual item with a tag's whose l-atomic is true is passed to any method that operates on multiple items (basically move and scale), all of the items possesing the atomic tag have the passed tag added to their list of tags.
Tips edit
Unlike frames, canvases come with a 2-pixel -highlightthickness ... set them to zero if you want the canvas to pack/grid/etc. like a frame does.Anti-Aliasing edit
Anti-aliasing for line, polygons, oval items (X only, requires the Cairo lib): http://phpsource.net/?page=tkMoving Things Around edit
- A tiny drawing program: TclBrix
- Drawing and editing polygons:
- Flipping a canvas:
- Move an item on a Canvas Widget in a Straight Line (animated):
3-D edit
Layout edit
- Getting the Canvas View Area in Pixels
- Canvas microjustification
- Attracting objects on the canvas
- An experiment based on the idea of objects on the canvas attracting each other, and repositioning themselves accordingly.
Geometrical Calculations edit
Polygons edit
Transparency edit
Jeffrey Hobbs wrote in c.l.t on 2001-12-21:I think the point is that a completely transparent rectangle is essentially a fully filled for bindings, which allows you to create "hot spots" of bindings that don't require visual extras (like for imagemaps). If an outline of fill is specified, then you have a semantically different box, and triggers only occur for the visible portion.[jal_frezie] Would it be possible to allow a canvas to have a transparent background? This would be an easy way to implement hierarchies of graphical items, as you could have a child canvas in a window item on its parent canvas with no graphical indication of the distinction between the items on the two canvases. Thus you could replicate the most useful bit of Zinc, with the only change to the language being that the cammand ".canvas configure -bg {}" would set the background transparent rather than raising an exception. I have looked at the code implementing canvases, but not very much, and I imagine this change would be quite simple to implement.DKF: The current rendering engine (except possibly on OSX) isn't up to handling alpha-blended windows, and you can only efficiently do non-rectangular windows with an extension.
Polygons from lines edit
Polygon items, even if displayed transparent with -fill {}, cover the underlying items, so tag bindings to them don't fire. Get truly transparent polygons by constructing them explicitly from lines:proc myPolygon {c coords {color black}} { foreach {x0 y0} $coords break foreach {x1 y1} [concat [lrange $coords 2 end] $x0 $y0] { $c create line $x0 $y0 $x1 $y1 -fill $color -tag markup set x0 $x1; set y0 $y1 } }DKF: You can do this more easily by using the fact that lines need not just be point-to-point entities.
proc myPolygon2 {c coords {color black}} { # Close the path first set p0 [lrange $coords 0 1] set pEnd [lrange $coords end-1 end] if {$p0 ne $pEnd} { eval lappend coords $p0 } # Now create the item # note that the result of this command (the item id) is the proc result too $c create line $coords -fill $color -tag markup }
Scrolling edit
Panning
Adding panning to a canvas is simple. Suppose you have a canvas widget (with or without scrollbars, that doesn't matter). The only thing to add are two small bindings:canvas .c pack .c bind .c <ButtonPress-1> {%W scan mark %x %y} bind .c <B1-Motion> {%W scan dragto %x %y 1}That's all. Pressing the mouse button 1 will initiate panning and subsequent moving pans the widget. You do not need to take care of scaling or other factors since all is done in window coordinates. Note the number '1' as the last argument to 'scan dragto'. This is the gain factor which defaults to 10. Setting it to '1' make the command behave like the cursor sticks to the point where you pressed the mouse button.If you want to have fun, try to remove the first of the two bindings or set the gain to other values ... :-)
Finding Visible or Partly Visible Items edit
MAK: This function will return all of the tags for items that are currently visible (either entirely visible if partial is 0 or partly off-screen if partial is 1) within the canvas, provided you've got your scroll region set correctly.proc canvasVisibleTags { hWnd {partial 1} } { foreach { xmin ymin xmax ymax } [$hWnd cget -scrollregion] break foreach { y1 y2 } [$hWnd yview] break foreach { x1 x2 } [$hWnd xview] break set top [expr {($ymax - $ymin) * $y1 + $ymin}] set bot [expr {($ymax - $ymin) * $y2 + $ymin}] set left [expr {($xmax - $xmin) * $x1 + $xmin}] set right [expr {($xmax - $xmin) * $x2 + $xmin}] if {$partial} { return [$hWnd find overlapping $left $top $right $bot] } else { return [$hWnd find enclosed $left $top $right $bot] } }
Canvas see #1 - move view to make items visible edit
MAK: I didn't see any "see" functions for the canvas on the Wiki, so here is a somewhat reformatted (for my own aesthetics) version of a similar function found in an old usenet post [1] with the added capability that you can specify more than one item as what you want to scroll to.Useful if you have multiple items that together comprise one logical item (in my case, in my own tree widget where I want to "see" the expand/collapse button, the icon, and the label rather than just the label). This algorithm will scroll only as far as necessary to make the specified items visible, rather than centering on the specified items.proc canvasSee { hWnd items } { set bbox [eval $hWnd bbox $items] if {$bbox == ""} { return } foreach { x1 y1 x2 y2 } $bbox break foreach { top bottom } [$hWnd yview] break foreach { left right } [$hWnd xview] break foreach { x_min y_min x_max y_max } [$hWnd cget -scrollregion] break set width [expr {$x_max - $x_min}] set right [expr {$right * $width}] set left [expr {$left * $width}] set height [expr {$y_max - $y_min}] set top [expr {$top * $height}] set bottom [expr {$bottom * $height}] if { $x1 < $left } { $hWnd xview moveto [expr {double($x1-$x_min)/$width}] } elseif {$x2 > $right} { $hWnd xview moveto [expr {double($x2-$x_min-($right-$left))/$width}] } if { $y1 < $top } { $hWnd yview moveto [expr {double($y1-$y_min)/$height}] } elseif {$y2 > $bottom} { $hWnd yview moveto [expr {double($y2-$y_min-($bottom-$top))/$height}] } }
Canvas see #2 - move view to make items visible edit
MAKThis is another similarly reformatted and extended algorithm from another usenet post [2] -- unlike the above, this one tends to center the specified items within the visible area of the canvas.proc canvasSee { hWnd items } { set box [eval $hWnd bbox $items] if {$box == ""} { return } if {[string match {} [$hWnd cget -scrollregion]] } { # People really should set -scrollregion you know... foreach {x y x1 y1} $box break set x [expr round(2.5 * ($x1+$x) / [winfo width $hWnd])] set y [expr round(2.5 * ($y1+$y) / [winfo height $hWnd])] $hWnd xview moveto 0 $hWnd yview moveto 0 $hWnd xview scroll $x units $hWnd yview scroll $y units } else { # If -scrollregion is set properly, use this foreach { x y x1 y1 } $box break foreach { top btm } [$hWnd yview] break foreach { left right } [$hWnd xview] break foreach { p q xmax ymax } [$hWnd cget -scrollregion] break set xpos [expr (($x1+$x) / 2.0) / $xmax - ($right-$left) / 2.0] set ypos [expr (($y1+$y) / 2.0) / $ymax - ($btm-$top) / 2.0] $hWnd xview moveto $xpos $hWnd yview moveto $ypos } }
Tiled Backgrounds edit
DKF: Canvases don't directly support tiled backgrounds, but you can easily fix this by using a tiled image underneath everything else.-highlightcolor edit
Robert Heller provided the following sample of how to use the canvas's -highlightcolor option (slightly corrected by Jeff Hobbs):canvas .thecanvas pack .thecanvas .thecanvas create oval 0 0 50 50 -fill red -outline blue -tag theOval .thecanvas itemconfigure theOval -fill \ [tk_chooseColor -initialcolor [.thecanvas itemcget theOval -fill]] .thecanvas itemconfigure theOval -outline \ [tk_chooseColor -initialcolor [.thecanvas itemcget theOval -outline]] proc Rand255 {} { return [expr int(rand() * 256)] } .thecanvas itemconfigure theOval \ -fill [format {#%02x%02x%02x} [Rand255] [Rand255] [Rand255]] .thecanvas itemconfigure theOval \ -outline [format {#%02x%02x%02x} [Rand255] [Rand255] [Rand255]] .thecanvas configure -highlightcolor [format {#%02x%02x%02x} [Rand255] [Rand255] [Rand255]] focus .thecanvas
Geting the Canvas ID edit
MG 2005-02-20: Would it be useful for the [canvas bind ...] command to accept an extra substitution to the regular ones, for the canvas id of the item being clicked, and the name of the tag that invoked the binding? I know it's (always?) possible to get the id with [$canvas find overlapping %x,%y], but I'd guess it's something that's needed in pretty much every instance where canvas tags have bindings, so it would probably be much quicker and easier (in terms of learning how to do it and actual execution time) if it were done with a substitution?Peter Newman 2005-02-20:set myCanvasItemsCanvasID [.myCanvas create whatever xxx] ; ... .myCanvas bind $myCanvasItemsCanvasID <Event> "MyEventHandler ${myCanvasItemsCanvasID} %x %y" ; ... proc MyEventHandler { myCanvasItemsCanvasID mouseX_inWindow mouseY_inWindow } { xxx ... xxx } ;That's even faster that an extra substitution to the regular ones, since the substitution is made only once, when the binding script is compiled, rather than every time the event occurs.MG: That only works if you're binding to a single canvas item. If you're binding to a tag (which is what I meant, though didn't say too clearly). Then you can do something like
pack [canvas .c] for {set i 1} {$i < 10} {incr i} { .c create image [expr {$i*5}] 20 -image test -tags [list clickable] } .c bind clickable <Button-1> {puts "You pressed canvas item %I on canvas %W"}RS: The canvas item being clicked has the "current" tag...MG Thanks, RS. Don't recall ever seeing that before :)Scott Hill 2005-0-01: The tag current is managed automatically by Tk; it applies to the current item, which is the topmost item whose drawn area covers the position of the mouse cursor. If the mouse is not in the canvas widget or is not over an item, then no item has the current tag.
Exporting a Canvas edit
- Exporting a canvas to other formats
- can2svg
- Pdf canvas
- 2007-08-03
- pdf4tcl
- Printing a canvas under Windows
- Serializing a canvas widget
package require Img pack [canvas .c -height 50 -width 50] .c create rectangle 0 0 25 25 -fill blue .c create rectangle 25 25 50 50 -fill green raise . ;# if there's anything over the window on-screen, it'll be obscured in the image image create photo theCanvas -format window -data .c theCanvas write /your/path/to/image.gif -format gif
Clipping edit
MAK 2005-01-26: I just noticed something about the way window canvas items are clipped that's quite interesting. I'm not sure if it's a bug or not, but at least it's the same on different platforms: window items are not clipped to the canvas, but rather to their parent window. A demonstration:frame .f -bg blue canvas .c2 -bg red -width 100 grid rowconfigure .f 0 -weight 1 -minsize 0 grid rowconfigure .f 1 -weight 0 -minsize 0 grid columnconfig .f 0 -weight 1 -minsize 0 grid columnconfig .f 1 -weight 0 -minsize 0 grid [canvas .f.c -bg yellow -width 100 \ -xscrollcommand ".f.x set" -yscrollcommand ".f.y set"] \ -row 0 -column 0 -sticky news grid [scrollbar .f.y -orient vertical -command ".f.c yview"] \ -row 0 -column 1 -sticky news grid [scrollbar .f.x -orient horizontal -command ".f.c xview"] \ -row 1 -column 0 -sticky news grid columnconfig . 0 -weight 1 -minsize 0 grid columnconfig . 1 -weight 1 -minsize 0 grid rowconfigure . 0 -weight 1 -minsize 0 grid .f -row 0 -column 0 -sticky news grid .c2 -row 0 -column 1 -sticky news checkbutton .cb -text "012345678901234567890123456789" -bg green checkbutton .f.cb -text "012345678901234567890123456789" -bg green checkbutton .f.c.cb -text "012345678901234567890123456789" -bg green .f.c create window 0 0 -anchor nw -window .cb .f.c create window 0 30 -anchor nw -window .f.cb .f.c create window 0 60 -anchor nw -window .f.c.cbThis script creates two canvases side by side - one red and yellow with scrollbars. It then creates three identical checkbuttons with green backgrounds - one that's a child of the toplevel, one that's a child of the frame with the canvas and scrollbars, and one that's a child of the canvas itself.Notice that the topmost checkbutton is not clipped at all - it overlaps both canvases, while the middle checkbutton is clipped to the frame and overlaps the scrollbars, and the bottom checkbutton is clipped to the canvas.However, that bottom checkbutton still overlaps the margin of the canvas.I'm tempted to think it's a bug or an oversight, but on the other hand I think I might find it very useful in working around Aqua's overrideredirect problems in one case. Anyway, I found it interesting and didn't see mention of this behavior in the manpage.DKF: This is general window-clipping behaviour, and is (and should be) that way on all platforms (and is useful/relevant with the text widget too). It is also why widgets embedded in a canvas should usually be children of the canvas itself.If you look carefully, you'll notice that the third widget overlaps the border of the canvas (it becomes more visible if you use a more visible - thicker and solid - border and highlight ring). A wrapper frame is sometimes necessary to prevent that sort of nonsense...
Feature Requests edit
-elide canvas
AMG: I'd like an -elide option for canvas items, similar to the text widget. This option, when set to true, would hide the item and inhibit user interaction. Currently, hiding an option can be done in one of four ways:- Move it out of the visible region.
- Delete it.
- Reset its colors to empty string.
- Lower it behind an opaque background item.
Discussion edit
Zinc looks interesting. On the zinc site there are source & unix binaries. Has anyone successfully compiled it for windows? - Ian GayChristophe Mertz: As mentionned in zinc, since 3.2.9x version, TkZinc is now compiled for windows. We are also looking for port on OSX... Any volunteer?Bugs edit
LV: What can people tell me about the canvas -state attribute? For instance, on the perl/tk mailing list recently, the following Tcl code was used to demonstrate what appears to be a Tk canvas bug:
package require Tk pack [canvas .c] set w [.c create window 50 50 -window [ label .c.label -text Hello ] -state hidden] set l [.c create line 30 60 70 60 -state hidden] set i 0 proc repeatproc {} { global i w l incr i if { $i%2 == 0 } { .c itemconfigure $w -state hidden .c itemconfigure $l -state hidden } else { .c itemconfigure $w -state normal .c itemconfigure $l -state normal } after 1000 repeatproc } after 1000 repeatproc