Version 5 of Centering a window

Updated 2002-06-08 13:30:28

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"
    }
 }

EMJ

    #    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).