Version 20 of binding to a toplevel window

Updated 2007-05-21 11:22:34 by ruj

MGS [2003/09/20] - Sometimes it is necessary/desirable to bind to a toplevel window. For example, I might want to withdraw a toplevel window, instead of having it iconify, when clicking the minimize button. There are several ways to do this:

1. Bind directly to the window name. e.g.

   bind . <Unmap> [list wm withdraw .]

However, the binding will trigger if you decide to unmanage any of the window's child widgets, due to their default binding tags.

2. Bind to the toplevel window's class. e.g.

   bind [winfo class .] <Unmap> [list wm withdraw %W]

However, this binding will fire for all windows with the same class as . .

3. Create a new binding tag for the toplevel window and then create the binding to that tag. e.g.

  bindtags . [list . bind. [winfo class .] all]
  bind bind. <Unmap> [list wm withdraw %W]

or more generally:

  set W [toplevel .mywin]
  bindtags $W [linsert [bindtags $W] 1 bind$W]
  bind bind$W <Unmap> [list wm withdraw %W]

RUJ: Hi, I would like to know ... How to bind a "Leave" command to main toplevel but it should be work on its child widgets also. I want to make popupwindow which will have some entries and labels & this window should destroyed after cursor moved away from window.

MJ - Just destroy the toplevel and all its child widgets are destroyed as well. In the <Leave> event binding make sure you only call the destroy when the toplevel is closed. Leaving a child widget will also call the binding, because the toplevel is in every child widget's bindtags list.

 toplevel .l
 bind .l <Leave> {tlclose %W}
 proc tlclose {w} {if {$w == [winfo toplevel $w]} {destroy $w}}

RUJ: Thanking you it is working very well. Before that i was trying by,

 set destroyScript [list destroy $wa]
 bind $wa <Enter> [list after cancel $destroyScript]
 bind $wa <Leave> $destroyScript

RUJ: hello MJ, I am trying to use above code for dragging window but it not working. this is the code giving error of expression syntax.

 set ::moveable 0
 wm overrideredirect $wa 1
 bind $wa <ButtonPress> {set ::moveable 1}
 bind $wa <ButtonRelease> {set ::moveable 0}
 bind $wa <Motion> {if {!$::moveable} {break}; if {%W == [winfo toplevel %W]} {wm geometry [winfo toplevel %W] +[expr %X-5]+[expr %Y-5]} }

Bryan Oakley says: The first part of the solution is to move all that code to a proc. Unless you have a specific reason to do otherwise you should stick to the rule of thumb "never have more than one command in a binding". It makes writing and maintaining code like this soooo much easier.

Try this:

 bind $wa <Motion> [list handleMotionEvent %W %X %Y]
 proc handleMotionEvent {w x y} {
     if {! $::moveable} return
     set top [winfo toplevel $w]
     if {$w ne $top} return

     wm geometry $top +[expr {$x-5}]+[expr {$y-5}]
 }

RUJ: Thanking you. it is working great... I have another problem that how i can drag elements within listbox to change its order or by up-down key. I am using two buttons right now. here is code for moving selected element down.

    listbox $top.lis50 \
        -background gray -listvariable "" ;

button $top.but01 \

        -pady 0 -text D1 -bd 1 -command movedown lis51}

 proc movedown { listp } {
 variable top ;
 variable uLimit ; 

 global exist_compNames
 set seleIndex ""
 set movecompindex ""
 set seleIndex [$top.$listp curselection]
 set seleIndexlen [llength $seleIndex]
 if {$seleIndex < 0} {
 mvarproc "You have selected nothing"

 } elseif {$seleIndexlen == 1} {
 set movecompindex [lindex $seleIndex end]
 set movecompname [$top.$listp get $seleIndex ]
 #tk_messageBox -message "movecompindex=$movecompindex movecompname=$movecompname"
 set removeindex [expr $movecompindex +1]
 set removedataname [$top.$listp get $removeindex ]
 set lenall [$top.$listp size]
 #set lenlist [expr $lenall -1]
 #tk_messageBox -message "movecompindex=$removeindex movecompname=$lenlist"
 if {$removeindex == $lenall} {
 errorproc "upper limit" 
 } else {
 $top.$listp delete $removeindex
 $top.$listp insert $movecompindex $removedataname
 }
 } else {
 set seleIndexEnd [lindex $seleIndex end]
 set seleIndexStart [lindex $seleIndex 0]
 set removeIndexL [expr $seleIndexEnd +1]
 set removeIndexLdata [$top.$listp get $removeIndexL]
 set lenall [$top.$listp size]
 if {$removeIndexL == $lenall} {

 errorproc "lower limit"

 } else {
 $top.$listp delete $removeIndexL
 $top.$listp insert $seleIndexStart $removeIndexLdata
 #tk_messageBox -message "$seleIndexEnd"
 }
 }
 }

See also: