Version 15 of Paning widgets

Updated 2006-04-01 14:32:01

<u style="display: none;">... no changes ... no changes ... no changes ... no changes ... no changes ... no changes ... no changes ... no changes ... no changes ... no changes ... no changes ... Thanks!!! Links: <a href='pagerank'>http://www.areaseo.com'>pagerank main</a> : [L1 ] - HTTP://www.areaseo.com improve pagerank default : [PageRank 11|http://www.areaseo.com ] - PageRank 11|HTTP://www.areaseo.com : http://www.areaseo.com/improvepr/ : [http://www.areaseo.com google rank 20] : [http://www.areaseo.com | pagerank 5] : "online pr16" http://www.areaseo.com : [L2 ] </u>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 loosing 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.


Category Example | Category GUI

Category Widget