A recurring question on comp.lang.tcl is, ``How can I center a toplevel window on the screen?''
The usual answer is to use the update command to wait for the window to be configured, and then use the [wm] command to adjust its geometry.
There is an alternative way to do it that doesn't involve the many pitfalls of update: bind to the <Configure> event and use that to notify the process that the window has been configured and it's time to compute the geometry. The code below gives the idea:
# KBK - 12 February 2001
proc make_the_window {} { # Create the toplevel window toplevel .t grid [label .t.l -text "This window\nshould be centered."] grid [button .t.d -text Dismiss -command {destroy .t}] # Set things up to position the window when it is # configured. bind .t <Configure> {center_the_toplevel %W} return } proc center_the_toplevel { w } { # Callback on the <Configure> event for a toplevel # that should be centered on the screen # Make sure that we aren't configuring a child window if { [string equal $w [winfo toplevel $w]] } { # Calculate the desired geometry set width [winfo reqwidth $w] set height [winfo reqheight $w] set x [expr { ( [winfo vrootwidth $w] - $width ) / 2 }] set y [expr { ( [winfo vrootheight $w] - $height ) / 2 }] # Hand the geometry off to the window manager wm geometry $w ${width}x${height}+${x}+${y} # Unbind <Configure> so that this procedure is # not called again when the window manager finishes # centering the window bind $w <Configure> {} } return } grid [button .b -text "Test" -command make_the_window] \ [button .q -text "Quit" -command exit]
tclguy writes, "If you have 8.3, the ideal proc is defined in $tk_library/tk.tcl as ::tk::PlaceWindow. This is a private function, but you can copy it for your needs (or expect that it exists at least for 8.3)."
W. Rösler: I used the following, taken from the dialog.tcl file from the Tk distribution, which seems to work fine:
proc center_window {w} { wm withdraw $w update idletasks set x [expr [winfo screenwidth $w]/2 - [winfo reqwidth $w]/2 \ - [winfo vrootx [winfo parent $w]]] set y [expr [winfo screenheight $w]/2 - [winfo reqheight $w]/2 \ - [winfo vrooty [winfo parent $w]]] wm geom $w +$x+$y wm deiconify $w }
Arjen Markus The following code is based partly on the above. It will bring up a "transient" window for displaying a logo, name whatever that eventually disappears. While the transient is visible, the main toplevel widget (.) is not.
# Show a transient window, withdraw the usual window while that is visible # proc center_transient_window { w } { set width [winfo reqwidth $w] set height [winfo reqheight $w] set x [expr { ( [winfo vrootwidth $w] - $width ) / 2 }] set y [expr { ( [winfo vrootheight $w] - $height ) / 2 }] # Hand the geometry off to the window manager wm geometry $w ${width}x${height}+${x}+${y} } wm withdraw . toplevel .transient wm overrideredirect .transient 1 wm transient .transient center_transient_window .transient canvas .transient.c pack .transient.c -fill both .transient.c create rectangle 10 10 40 40 -fill green after 3000 {destroy .transient ; wm deiconify .}
Note: because the window is transient, it does not receive Configure events.
Arjen Markus Below is a full-featured proc for displaying a picture in the transient window (it does not delete the image though):
# tkmisc.tcl -- # Package that implements various small Tk utilities # # tkmisc -- # Namespace for the commands # namespace eval ::tkmisc { namespace export showTransientWindow } # showTransientWindow # Show a transient window, possibly with a bitmap (at start-up for # instance) # # Arguments: # time Time it remains visible in seconds # pictfile Name of a picture file (may be empty) # script Script to be executed after the window has been created # (optional) # # Return value: # Widget name of the canvas created inside # # Note: # If the name of the picture file is empty, the window is drawn at # default size # If a script is given, it should take "w" to mean the canvas in the # transient window, for instance: # showTransientWindow 3 {} { # $w create text 10 10 -text "Hello World" # } # proc ::tkmisc::showTransientWindow { time pictfile {script {}} } { # # Withdraw the default toplevel window, create a transient one # (centred) with a default size or determined from the picture # set t .transient set w ${t}.c wm withdraw . toplevel $t wm overrideredirect $t 1 wm transient $t if { $pictfile != "" } { set img [image create photo -file $pictfile] set height [image height $img] set width [image width $img] canvas $w -width $width -height $height $w create image 0 0 -anchor nw -image $img } else { canvas $w set width [winfo reqwidth $t] set height [winfo reqheight $t] } # # Centre the toplevel window # set x [expr { ( [winfo vrootwidth $t] - $width ) / 2 }] set y [expr { ( [winfo vrootheight $t] - $height ) / 2 }] # Hand the geometry off to the window manager wm geometry $t ${width}x${height}+${x}+${y} pack $w -fill both if { $script != {} } { eval $script } # # Now make it disappear in time # Note: # The [list] command does not work for some reason. #after [expr {$time*1000}] [list destroy $t ; wm deiconify .] after [expr {$time*1000}] "destroy $t ; wm deiconify ." } # # Test code # if { [file tail [info script]] == [file tail $::argv0] } { namespace import ::tkmisc::* showTransientWindow 3 {} {$w create rectangle 10 10 30 30 -fill green} after 4000 { showTransientWindow 3 "logoMed.gif" } }
# The [list] command does not work for some reason. #after [expr {$time*1000}] [list destroy $t ; wm deiconify .] after [expr {$time*1000}] "destroy $t ; wm deiconify ."
But
after [expr {$time*1000}] [list destroy $t \; wm deiconify .]
does work - "Think like the interpreter" (quote from somebody, can't find it right now).
Arjen Markus Yes, but the effect was rather surprising - there was no error message but the command (and seemingly) some earlier commands did not work.
DGP Uhhhh... if
after [expr {$time*1000}] [list destroy $t \; wm deiconify .]
works, then you've got a buggy [list] in your Tcl library. [list] should quote the semicolon so that it isn't special to [eval]. I can't find such a buggy Tcl. What version of Tcl are you using?
AM I am using for instance Tcl 8.3.1 on Solaris, but also Tcl 8.3.4 from ActiveTcl on Windows, they all give (when typed into the console):
% set a [list b ; c]
ambiguous command name "c": case catch cd clock close concat continue
Isn't it the interpreter that sees ; as a command separator, before [list] can deal with it?
% set a [list b ; c] ambiguous command name "c": case catch cd clock close concat continue
In that example, the [list] command is ended by the semicolon, and the next command [c] is evalauted, raising the error seen. The [set a] never gets a chance to run because of errors during command substitution of its arguments.
Now look at:
% set a [list b \; c] b {;} c
No error, but the semi-colon is quoted. If I pass that list to [eval] it will see the command [b] with arguments ; and c. It will not see the two commands [b] and [c] separated by a semi-colon.
Finally consider:
% set a "b ; c" b ; c
That's what you need to pass to [eval] to get two commands evaluated in sequence.
Clear?