Light weight widgets that are drawn on a canvas, rather than having their own window, might be useful in several situations.
Kevin Kenny made some nice canvas buttons.
I took Kevin's buttons and added a 3-D appearance. Kevin's file, slightly altered, appears here.
The 3-D effect is achieved using routines from the 3-D Boxes (Support for Canvas Buttons in 3-D) page.
Those routines allow you treat corners and sides separately, so if someone wanted to make a combobox, they could. It's not restricted to boxes, and it's not restricted to buttons.
Here's a link to the support routines, that these 3-D canvas buttons are built on.
3-D Boxes (Support for Canvas Buttons in 3-D)
Here's a link to the original Canvas Buttons.
Rick Hedin # ---------------------------------------------------------------------- # # cbutton3d.tcl -- # # Example of how to provide button-like behavior on canvas # items. # # This version has 3-D buttons. set ::RCSID([info script]) \ {$Id: 1379,v 1.3 2006-09-24 06:00:02 jcw Exp $} package provide canvasbutton 1.0 source box3d.tcl ;# Assumed to be in the working directory. namespace eval canvasbutton { # nexttag - Next unique tag number for a "button" being # created variable nexttag 0 # command - command(tag#) contains the command to execute when # a "button" is selected. variable command # cursor - cursor(pathName) contains the (saved) cursor # symbol of the widget when the pointer is in # a "button" variable cursor # enteredButton - contains the tag number of the button # containing the pointer. variable enteredButton {} # pressedButton - contains the tag number of the "button" # in which the mouse button was pressed variable pressedButton {} # buttoninfo - Info about the button, indexed by the button's id. # buttoninfo(<id>,id) - The first argument is the id of the button # according to the button logic. Returns the id of the button according # to the button display. # buttoninfo(<id>,textx) - Returns the original x coordinate of the text, # without shifting. # buttoninfo(<id>,texty) - The original y coordinate of the text. variable buttoninfo namespace export canvasbutton } # ---------------------------------------------------------------------- # # canvasbutton::canvasbutton -- # # Create a button-like object on a canvas. # # Parameters: # w Path name of the canvas # x0 Canvas X co-ordinate of left edge # y0 Canvas Y co-ordinate of top edge # x1 Canvas X co-ordinate of right edge # y1 Canvas Y co-ordinate of bottom edge # text Text to display in the button # cmd Command to execute when the button is selected. # # Results: # Unique canvas tag assigned to the items that make # up the button. # # Side effects: # A rectangle and a text item are created in the canvas, # and bindings are established to give them button-like # behavior. # #---------------------------------------------------------------------- proc canvasbutton::canvasbutton {w x0 y0 x1 y1 text cmd} { variable nexttag variable command variable buttoninfo set btag [list canvasb# [incr nexttag]] set command($btag) $cmd $w create rectangle [expr $x0 - 2] [expr $y0 - 2] [expr $x1 + 2] \ [expr $y1 + 2] -outline black -width 3 -state hidden \ -tags [list $btag [linsert $btag end frame] ] set id [Create3DBox $w $x0 $y0 $x1 $y1 5 -dx dx -dy dy \ -tags [list $btag [linsert $btag end button] ] ] set buttoninfo($nexttag,id) $id set x [expr { ($x0+$x1) / 2 }] set y [expr { ($y0+$y1) / 2 }] set buttoninfo($nexttag,textx) $x set buttoninfo($nexttag,texty) $y $w create text [expr $x + $dx] [expr $y + $dx] -anchor center \ -justify center -text $text \ -tags [list $btag [linsert $btag end text]] set extent [Get3DBoxPolygon $id] $w create polygon $extent -fill "" -outline "" \ -tags [list canvasb $btag [linsert $btag end region] ] # For an exciting error, reverse the order of creation of the text # and the polygon, and click the buttons until you enter a tight, # unescapeable loop. $w bind canvasb <Enter> [list [namespace current]::enter %W] $w bind canvasb <Leave> [list [namespace current]::leave %W] $w bind canvasb <ButtonPress-1> \ [list [namespace current]::press %W] $w bind canvasb <ButtonRelease-1> \ [list [namespace current]::release %W] return $btag } # ---------------------------------------------------------------------- # # canvasbutton::enter -- # # Process the <Enter> event on a canvas-button. # # Parameters: # w Path name of the canvas # # Results: # None. # # Side effects: # When the mouse pointer is in a button, the button is # highlighted with a broad outline and the cursor # symbol changes to an arrow. When the active button # is pressed, it is highlighted in green. # # ---------------------------------------------------------------------- proc canvasbutton::enter {w} { variable enteredButton variable pressedButton variable buttoninfo variable cursor set enteredButton [findBtag $w] set frame [linsert $enteredButton end frame] set button [linsert $enteredButton end button] set text [linsert $enteredButton end text] set cursor($w) [$w cget -cursor] $w configure -cursor arrow $w itemconfigure $frame -state normal $w lower $frame $button if {![string compare $enteredButton $pressedButton]} { set id [lindex $enteredButton 1] Set3DBoxRelief $buttoninfo($id,id) recessed -dx dx -dy dy $w coords $text [expr $buttoninfo($id,textx) + $dx] \ [expr $buttoninfo($id,texty) + $dy] } } # ---------------------------------------------------------------------- # # canvasbutton::leave -- # # Process the <Leave> event on a canvas-button. # # Parameters: # w Path name of the canvas # # Results: # None. # # Side effects: # Reverts the cursor symbol, the border width # if needed, the highlight color of the button. # # ---------------------------------------------------------------------- proc canvasbutton::leave {w} { variable enteredButton variable pressedButton variable buttoninfo variable cursor if {[string compare $enteredButton {}]} { set btag [findBtag $w] set frame [linsert $btag end frame] set text [linsert $btag end text] $w itemconfigure $frame -state hidden $w configure -cursor $cursor($w) unset cursor($w) if {![string compare $btag $pressedButton]} { set id [lindex $btag 1] Set3DBoxRelief $buttoninfo($id,id) raised -dx dx -dy dy $w coords $text [expr $buttoninfo($id,textx) + $dx] \ [expr $buttoninfo($id,texty) + $dy] } set enteredButton {} } return } # ---------------------------------------------------------------------- # # canvasbutton::press -- # # Process the <ButtonPress-1> event on a canvas-button. # # Parameters: # w Path name of the canvas # # Results: # None. # # Side effects: # Highlights the selected button in green. # # ---------------------------------------------------------------------- proc canvasbutton::press {w} { variable pressedButton variable buttoninfo set pressedButton [findBtag $w] set text [linsert $pressedButton end text] set id [lindex $pressedButton 1] Set3DBoxRelief $buttoninfo($id,id) recessed -dx dx -dy dy $w coords $text [expr $buttoninfo($id,textx) + $dx] \ [expr $buttoninfo($id,texty) + $dy] return } # ---------------------------------------------------------------------- # # canvasbutton::release -- # # Process the <ButtonRelease-1> event on a canvas-button. # # Parameters: # w Path name of the canvas # # Results: # None. # # Side effects: # Reverts the highlight color on the button. If the # mouse has not left the button, invokes the button's # command. # # ---------------------------------------------------------------------- proc canvasbutton::release {w} { variable enteredButton variable pressedButton variable buttoninfo variable command set pressedButtonWas $pressedButton set pressedButton {} set text [linsert $pressedButtonWas end text] set id [lindex $pressedButtonWas 1] Set3DBoxRelief $buttoninfo($id,id) raised -dx dx -dy dy $w coords $text [expr $buttoninfo($id,textx) + $dx] \ [expr $buttoninfo($id,texty) + $dy] if {![string compare $enteredButton $pressedButtonWas]} { uplevel #0 $command($pressedButtonWas) } return } # ---------------------------------------------------------------------- # # canvasbutton::findBtag -- # # Locate the unique tag of a canvas-button # # Parameters: # w Path name of the canvas # # Results: # Button tag, or the null string if the current # item is not a canvas-button # # Side effects: # Searches the tag list of the current canvas item # for a tag that begins with the string, `canvasb#', # and returns the first two elements of the tag # interpreted as a Tcl list. # # ---------------------------------------------------------------------- proc canvasbutton::findBtag {w} { foreach tag [$w itemcget current -tags] { if {[regexp {^canvasb#} [lindex $tag 0]]} { return [lrange $tag 0 1] } } return {} } if {![string compare $argv0 [info script]]} { grid [canvas .c -width 300 -height 200 -cursor crosshair] namespace import canvasbutton::* .c create text 150 150 -anchor n -tags label \ -font {Helvetica 10 bold} canvasbutton .c 30 70 80 120 "First\nButton" { .c itemconfigure label -text One } canvasbutton .c 125 70 175 120 "Second\nButton" { .c itemconfigure label -text Two } canvasbutton .c 220 70 270 120 "Third\nButton" { .c itemconfigure label -text Three } canvasbutton .c 240 160 280 180 "Quit" exit }