A pane is understood here as a frame holding two widgets and a thin borderline with a square button. By grabbing the button, the borderline can be moved in two directions, leading to a relative resize of the two embedded widgets. This was slightly modified after the example code in the Welch book 8.0, Example 22-5..7: vertical or horizontal panes. Suggested exercise: rewrite the code so the whole divider line can be grabbed ;-) See below for a possible solution... (EKB)
See also Bag of algorithms and Bag of Tk algorithms . RS
Tk 8.4 has a panedwindow included.
Namespace-d by DKF
DANGER WILL ROBINSON. Something's not right with the following code. It defines a proc named set (ring any bells, anyone?), and exports it. When I cut and paste the following code it fails with too many nested calls to "set" (gee, that's a surprise, since the proc set uses the set command).
namespace eval pane { namespace export create set proc create {f1 f2 args} { ### Map optional arguments into array values ### set t(-orient) horizontal rray values ### set t(-percent) 0.5 set t(-in) [winfo parent $f1] array set t $args ### Keep state in an array associated with the master frame ### set master $t(-in) pack $master -expand true -fill both pack propagate $master off upvar #0 [namespace current]::Pane$master pane array set pane [array get t] set pane(1) $f1 set pane(2) $f2 set pane(grip) $master.grip frame $pane(grip) -background gray75 -width 10 -height 10 -bd 1 -relief raised if {[string match vert* $pane(-orient)]} { ### Adjust boundary in Y direction ### set pane(D) Y place $pane(1) -in $master -x 0 -rely 0.0 -anchor nw -relwidth 1.0 -height -1 place $pane(2) -in $master -x 0 -rely 1.0 -anchor sw -relwidth 1.0 -height -1 place $pane(grip) -in $master -anchor c -relx 0.8 $pane(grip) configure -cursor sb_v_double_arrow } else { ### Adjust boundary in X direction ### set pane(D) X place $pane(1) -in $master -relx 0.0 -y 0 -anchor nw -relheight 1.0 -width -1 place $pane(2) -in $master -relx 1.0 -y 0 -anchor ne -relheight 1.0 -width -1 place $pane(grip) -in $master -anchor c -rely 0.2 $pane(grip) configure -cursor sb_h_double_arrow } $master configure -background grey75 ### bindings for resize AKA <Configure>, and dragging the grip. ### bind $master <Configure> [namespace code [list Geometry $master]] bind $pane(grip) <B1-Motion> [namespace code [list Drag $master %$pane(D)]] bind $pane(grip) <ButtonRelease-1> [namespace code [list Stop $master]] ### Do the initial layout ### Geometry $master } proc set {master value} { set [namespace current]::Pane${master}(-percent) $value Geometry $master } proc Drag {master D} { upvar #0 [namespace current]::Pane$master pane if [info exists pane(lastD)] { set delta [expr {double($pane(lastD)-$D)/$pane(size)}] set pane(-percent) [expr {$pane(-percent) - $delta}] if {$pane(-percent) < 0.0} { set pane(-percent) 0.0 } elseif {$pane(-percent) > 1.0} { set pane(-percent) 1.0 } Geometry $master } set pane(lastD) $D } proc Stop {master} { upvar #0 [namespace current]::Pane$master pane catch {unset pane(lastD)} } proc Geometry {master} { upvar #0 [namespace current]::Pane$master pane if {$pane(D) == "X"} { place $pane(1) -relwidth $pane(-percent) place $pane(2) -relwidth [expr {1.0 - $pane(-percent)}] place $pane(grip) -relx $pane(-percent) set pane(size) [winfo width $master] } else { # $pane(D) == "Y" place $pane(1) -relheight $pane(-percent) place $pane(2) -relheight [expr {1.0 - $pane(-percent)}] place $pane(grip) -rely $pane(-percent) set pane(size) [winfo height $master] } } }
Here's a usage example with horiz and vertical panes:
proc pane::test {{p .p} {orient hori}} { catch {destroy $p} frame $p -width 200 -height 200 ;# needed: no propagation message $p.1 -bg bisque -text [info procs] -relief ridge frame $p.2 label $p.2.foo -bg pink -text foo -relief ridge label $p.2.bar -bg grey75 -text bar -relief ridge pack $p -expand true -fill both pack propagate $p off pane::create $p.2.foo $p.2.bar -orient vert pane::create $p.1 $p.2 -orient $orient -percent 0.7 raise . }
Wow - that's a lot of code up there. Here's a simpler example written by Stephen Uhler. - DL
# 2 panes, one on top of the other (>= Tk4.0) frame .top frame .bottom frame .handle -borderwidth 2 -relief raised -bg orange -cursor sb_v_double_arrow . configure -bg black # fixed placement parameters place .top -relwidth 1 -height -1 place .bottom -relwidth 1 -rely 1 -anchor sw -height -1 place .handle -relx 0.9 -anchor e -width 10 -height 10 bind . <Configure> {set H [winfo height .]; set Y0 [winfo rooty .]} bind .handle <B1-Motion> {Place [expr {(%Y-$Y0)/double($H)}]} proc Place {fract} { place .top -relheight $fract place .handle -rely $fract place .bottom -relheight [expr {1.0 - $fract}] } Place .5 ;# initialization # now "pack" whatever you like in ".top" and ".bottom"
I like the simpler one but I kept losing the pane when I dragged it off of the window. Change the "Place" proc to be
proc Place {fract} { if { ($fract>=0.01) && ($fract<=0.99) } { place .top -relheight $fract place .handle -rely $fract place .bottom -relheight [expr {1.0 - $fract}] } }
Notice the if statement. Once this was added I could not drag the panes off of the window.
EKB April 9, 2005
A simple change to the simpler version lets you grab the divider line at any point. Replace:
place .handle -relx 0.9 -anchor e -width 10 -height 10
with
place .handle -relwidth 1 -x 0 -anchor w -height 5
Then if you turn off "-bg orange" when creating .handle, it looks a lot like pane dividers in most programs.