Keith Vetter : 2008-04-09 : Every have a problem of trying to figure out why grid layout is not looking right? This little package is a tool to help you debug grid layout problems.
You invoke it with ::GridDebugger::Dialog ?master?.
It puts up a dialog and lets you choose a master widget which has gridded slaves. It then displays a mockup of cells corresponding to the gridded layout of the master (not to scale). On this mockup you can:
How it does the highlighting is kind of interesting. The command grid bbox ... yields size and position info. I then create a label of that size which I place at the correct location.
##+########################################################################## # # GridDebugger.tcl -- Dialog to investigate grid layout # by Keith Vetter, April 2008 # package require Tcl 8.5 package require Tk namespace eval ::GridDebugger { variable S ;# Our state variable D ;# Display copy array unset S array unset D set S(W) .__gaidDialog set S(last) "" set S(colors) [list red orange yellow green blue cyan purple violet] set D(master) "<no master>" set D(size) "? x ?" image create photo ::img::griddebugger -width 0 -height 0 } ##+########################################################################## # # ::GridDebugger::Dialog -- our debugging dialog # proc ::GridDebugger::Dialog {{master ""}} { variable S set W $S(W) set S(C) $W.c destroy $W toplevel $W wm title $W "Grid Debugger" wm protocol $W WM_DELETE_WINDOW ::GridDebugger::Exit label $W.lm -text "Master:" -anchor e $W.lm configure -font "[font actual [$W.lm cget -font]] -weight bold" set font [$W.lm cget -font] ::ttk::combobox $W.master \ -state readonly -textvariable ::GridDebugger::D(master) \ -postcommand ::GridDebugger::PostCommand \ -width 20 bind $W.master <<ComboboxSelected>> ::GridDebugger::NewMaster label $W.lsize -text "Size:" -anchor e -font $font label $W.size -textvariable ::GridDebugger::D(size) -anchor c -font $font grid $W.lm $W.master grid $W.lsize $W.size -sticky ew frame $S(W).f -bd 2 -relief ridge canvas $S(C) -width 500 -height 300 -highlightthickness 0 label $S(W).who -textvariable ::GridDebugger::D(who) -bd 2 -relief ridge \ -font $font pack $S(W).who -in $S(W).f -side bottom -fill x pack $S(C) -in $S(W).f -side left -fill both -expand 1 bind $S(C) <Configure> [list ::GridDebugger::ReCenter %W %h %w] ::ttk::button $S(W).exit -text Exit -command ::GridDebugger::Exit grid $S(W).f - - -sticky news -padx 5 grid $S(W).exit - - -pady 10 grid columnconfigure $W 2 -weight 1 grid rowconfigure $W 2 -weight 1 ::GridDebugger::NewMaster $master } ##+########################################################################## # # Recenter -- keeps 0,0 at the center of the canvas during resizing # proc ::GridDebugger::ReCenter {W h w} { ;# Called by configure event set h2 [expr {$h / 2}] ; set w2 [expr {$w / 2}] $W config -scrollregion [list -$w2 -$h2 $w2 $h2] ::GridDebugger::Resize } ##+########################################################################## # # ::GridDebugger::PostCommand -- fills in our combobox with all # widgets that have gridded slaves # proc ::GridDebugger::PostCommand {args} { variable S $S(W).master config -values [::GridDebugger::AllGridMasters $S(W)] } ##+########################################################################## # # ::GridDebugger::GridBbox -- highlights a grid box on master by 'place'ing # a label on top of it. # proc ::GridDebugger::GridBbox {master row col {clr yellow}} { foreach {x y w h} [grid bbox $master $col $row] break set top [winfo toplevel $master] set tag "$row,$col" set W [expr {$top eq "." ? ".__gaid$tag" : "$top.__gaid$tag"}] destroy $W label $W -image ::img::griddebugger -width $w -height $h -bg $clr $W config -padx 0 -pady 0 -bd 0 place $W -in $master -x $x -y $y -anchor nw bind $W <1> [list ::GridDebugger::UnClick $W $row $col] return $W } ##+########################################################################## # # ::GridDebugger::NewMaster -- called when a new master widget is selected # proc ::GridDebugger::NewMaster {{new ""}} { variable S variable D if {$new eq ""} { set new $D(master) } if {! [winfo exists $new]} { ::GridDebugger::Cleanup; return } foreach {cols rows} [grid size $new] break set this "$new/$rows/$cols" if {$this eq $S(last)} return ::GridDebugger::Cleanup ;# Remove old stuff if {$rows == 0 || $cols == 0} {return} set D(master) $new set S(master) $new set D(size) "$rows x $cols" ::GridDebugger::Checkerboard $S(C) $rows $cols set S(last) $this } ##+########################################################################## # # ::GridDebugger::Checkerboard -- draws our mockup of the gridded master # proc ::GridDebugger::Checkerboard {c rows cols} { $c delete all if {[winfo ismapped $c]} { set maxX [winfo width $c] ; set maxY [winfo height $c] } else { set maxX [winfo reqwidth $c] ; set maxY [winfo reqheight $c] } set dx [expr {($maxX-10) / $cols}] set dy [expr {($maxY-10) / $rows}] set dx [set dy [expr {min($dx,$dy)}]] set row2 [expr {$rows/2.0}] set col2 [expr {$cols/2.0}] for {set row 0} {$row < $rows} {incr row} { for {set col 0} {$col < $cols} {incr col} { set x0 [expr {($col - $col2) * $dx}] set y0 [expr {($row - $row2) * $dy}] set x1 [expr {$x0 + $dx}] set y1 [expr {$y0 + $dy}] set tag cell,$row,$col $c create rect $x0 $y0 $x1 $y1 -tag $tag -fill white -outline black $c bind $tag <1> [list ::GridDebugger::Click $c $tag] $c bind $tag <Enter> [list ::GridDebugger::GetSlaveInfo $row $col] } } } ##+########################################################################## # # ::GridDebugger::Resize -- handles resizing the window # proc ::GridDebugger::Resize {} { variable S set old $S(master) ::GridDebugger::Cleanup ;# Blow everything away ::GridDebugger::NewMaster $old } ##+########################################################################## # # ::GridDebugger::Click -- called when clicking on a cell in our # checkerboard. Toggles the cell and toggles highlighting the grid # cell on the master. # proc ::GridDebugger::Click {C tag} { variable S if {! [winfo exists $S(master)] || ! [winfo ismapped $S(master)]} { ::GridDebugger::Cleanup return } foreach {. row col} [split $tag ","] break set old [$C itemcget $tag -fill] if {$old eq "white"} { ;# Turn on set S(colors) [lassign $S(colors) clr] lappend S(colors) $clr $C itemconfigure $tag -fill $clr set w [::GridDebugger::GridBbox $S(master) $row $col $clr] set S(grid,$row,$col) $w } else { $C itemconfigure $tag -fill white destroy $S(grid,$row,$col) unset S(grid,$row,$col) } } ##+########################################################################## # # ::GridDebugger::UnClick -- forces a checkerboard cell to be off # proc ::GridDebugger::UnClick {W row col} { variable S destroy $W set tag cell,$row,$col $S(C) itemconfigure $tag -fill white unset S(grid,$row,$col) } ##+########################################################################## # # ::GridDebugger::AllGridMasters -- returns all widgets w/ gridded slaves # proc ::GridDebugger::AllGridMasters {{exclude ""}} { variable S set q . set all {} while {$q ne {}} { set q [lassign $q who] if {! [winfo ismapped $who]} continue ;# Not visible if {$who in $exclude} continue ;# Probably our debugger dialog lappend q {*}[winfo child $who] if {[grid slaves $who] ne {}} { lappend all $who } } return $all } ##+########################################################################## # # ::GridDebugger::Cleanup -- returns everything back to normal # proc ::GridDebugger::Cleanup {} { variable S variable D foreach arr [array names S grid,*] { foreach {. row col} [split $arr ","] break ::GridDebugger::UnClick $S($arr) $row $col } $S(C) delete all set D(size) "? x ?" set D(master) "<no master>" set S(master) "" set S(last) "" } ##+########################################################################## # # ::GridDebugger::Exit -- cleans up and exits our dialog # proc ::GridDebugger::Exit {} { variable S ::GridDebugger::Cleanup destroy $S(W) } ##+########################################################################## # # ::GridDebugger::GetSlaveInfo -- displays info about # the slave at row/col. # proc ::GridDebugger::GetSlaveInfo {row col} { variable S variable D set D(who) [grid slaves $S(master) -row $row -column $col] if {$D(who) ne ""} { array set info [grid info $D(who)] append D(who) " : $info(-rowspan)/$info(-columnspan) $info(-sticky)" } else { set D(who) "<empty>" } } ################################################################ # # Demo code # toplevel .top wm geom . +10+10 wm geom .top +300+10 for {set row 0} {$row < 3} {incr row} { for {set col 0} {$col < 6} {incr col} { set w .l$row$col label $w -text "$row,$col" -bd 1 -relief solid -width 5 grid $w -row $row -column $col } } label .top.a1 -text "Three columns" -bd 2 -relief ridge label .top.b1 -text "Three\nrows" -bd 2 -relief ridge label .top.b2 -text "ABC" -bd 2 -relief ridge label .top.b3 -text "ABC" -bd 2 -relief ridge label .top.c3 -text "XYZ" -bd 2 -relief ridge label .top.d2 -text "MNO" -bd 2 -relief ridge label .top.d3 -text "MNO" -bd 2 -relief ridge label .top.e1 -text "Three columns" -bd 2 -relief ridge grid .top.a1 - - -sticky ew grid .top.b1 .top.b2 .top.b3 -sticky ns grid ^ x .top.c3 grid ^ .top.d2 .top.d3 grid .top.e1 - - ::GridDebugger::Dialog .top return