There is no "snap-to-grid" feature built into the Canvas widget, sorry to say. That would be a useful feature in many applications. However, it's not too difficult to implement.
In one of my classes, we already have a series of examples showing how to create a simple rectangles drawing program. I just took the last one in that series and added a snap-to-grid feature, both for drawing rectangles and for moving them around the canvas.
To use this application, Click-Drag-Release with the left mouse button to create a rectangle. Hold down the Control key and Click-Drag-Release to move an existing rectangle.
You might be particularly interested in the
rect_move_end procedure, which queries the current coordinates of a rectangle, and then adjusts them so that the upper-lefthand corner is on the nearest grid point.
My apologies for the formatting of this example code. The line lengths are longer than those supported by Tek-Tips, and I don't feel like going through the entire program trying to do good line wraps.
Code:
# ----------------------------------------------------------------------
# FILE: rect5.tcl
# DESCRIPTION: Implementing a "snap to grid" feature."
# ======================================================================
# Copyright (c) 2001-2 Kenneth Jones
# ======================================================================
canvas .pic
pack .pic
bind .pic <ButtonPress-1> {rect_create_start %W %x %y}
bind .pic <B1-Motion> {rect_create_drag %W %x %y}
bind .pic <ButtonRelease-1> {rect_create_end %W %x %y}
# The size of the grid divisions
set GRIDSIZE 20
# Note that the space is required in the following binding actions.
# If the action looks like a NULL string, the bind command removes
# any existing binding for the specified event.
bind .pic <Control-ButtonPress-1> { }
bind .pic <Control-B1-Motion> { }
bind .pic <Control-ButtonRelease-1> { }
proc rect_create_start {canv x y} {
global GRIDSIZE
global rect
# Start drawing rectangle on the nearest grid point
set rect(x0) [expr {round($x/$GRIDSIZE)*$GRIDSIZE}]
set rect(y0) [expr {round($y/$GRIDSIZE)*$GRIDSIZE}]
set rect(id) [$canv create rect $rect(x0) $rect(y0) $rect(x0) $rect(y0)]
$canv bind $rect(id) <Control-ButtonPress-1> [list rect_move_start $canv $rect(id) %x %y]
$canv bind $rect(id) <Control-B1-Motion> [list rect_move_drag $canv $rect(id) %x %y]
$canv bind $rect(id) <Control-ButtonRelease-1> [list rect_move_end $canv $rect(id) %x %y]
}
proc rect_create_drag {canv x y} {
global rect
$canv coords $rect(id) $rect(x0) $rect(y0) $x $y
}
proc rect_create_end {canv x y} {
global GRIDSIZE
global rect
# Make final vertex on the nearest grid point
set x [expr {round($x/$GRIDSIZE)*$GRIDSIZE}]
set y [expr {round($y/$GRIDSIZE)*$GRIDSIZE}]
rect_create_drag $canv $x $y
$canv itemconfigure $rect(id) -fill blue
}
proc rect_move_start {canv id x y} {
global rect
set rect(xs) $x
set rect(ys) $y
$canv raise $id
$canv itemconfigure $id -stipple gray50
}
proc rect_move_drag {canv id x y} {
global rect
$canv move $id [expr {$x-$rect(xs)}] [expr {$y-$rect(ys)}]
set rect(xs) $x
set rect(ys) $y
}
proc rect_move_end {canv id x y} {
global GRIDSIZE
rect_move_drag $canv $id $x $y
# Get the final coordinates of the rectangle
foreach {x0 y0 x1 y1} [$canv coords $id] {break}
# Put the upper-lefthand corner on the nearest grid point
$canv move $id [expr {(round($x0/$GRIDSIZE)*$GRIDSIZE)-$x0}] [expr {(round($y0/$GRIDSIZE)*$GRIDSIZE)-$y0}]
$canv itemconfigure $id -stipple ""
}
- Ken Jones, President
Avia Training and Consulting
866-TCL-HELP (866-825-4357) US Toll free
415-643-8692 Voice
415-643-8697 Fax