2010-07-28 366 views
36

Estoy intentando centrar una ventana de tkinter. Sé que puedo obtener programáticamente el tamaño de la ventana y el tamaño de la pantalla y usar eso para establecer la geometría, pero me pregunto si hay una forma más simple de centrar la ventana en la pantalla.¿Cómo centrar una ventana en la pantalla en Tkinter?

Respuesta

54

Usted puede tratar de utilizar los métodos winfo_screenwidth y winfo_screenheight, que devuelven respectivamente el ancho y la altura (en píxeles) de su Tk ejemplo (ventana), y con un poco de matemática básica que puede centrar su ventana:

import tkinter as tk 


def center(toplevel): 
    toplevel.update_idletasks() 
    w = toplevel.winfo_screenwidth() 
    h = toplevel.winfo_screenheight() 
    size = tuple(int(_) for _ in toplevel.geometry().split('+')[0].split('x')) 
    x = w/2 - size[0]/2 
    y = h/2 - size[1]/2 
    toplevel.geometry("%dx%d+%d+%d" % (size + (x, y))) 


if __name__ == '__main__': 
    root = tk.Tk() 
    root.title("Not centered") 

    win = tk.Toplevel(root) 
    win.title("Centered!") 
    center(win) 

    root.mainloop() 

Llamo al método update_idletasks antes de recuperar el ancho y el alto de la ventana para garantizar que los valores devueltos sean precisos.

+13

"no más poderoso" es un poco subjetivo. Tiene tanto "poder" como otros juegos de herramientas (dependiendo de tu definición de "poder"), simplemente no hace tanto de la mano. Es la diferencia entre construir una casa con paredes prefabricadas (algunos otros juegos de herramientas) o construirla con una pila de madera (Tk). Puedes hacer prácticamente cualquier cosa en Tkinter que puedas hacer en otros kits de herramientas, solo tienes que trabajar un poco para ello. –

+0

Parece confirmar mis sospechas, ¡gracias! – psicopoo

+4

@Bryan - eso es precisamente lo que quiero decir con poder. Claro que puedes cruzar los Estados Unidos en un Yugo (tal vez) y un Ferrari, pero uno de ellos puede llevarte mucho más rápido. Por supuesto, ambos tienen el poder de interactuar con el usuario de ciertas maneras, pero Tkinter no tiene el poder de permitirle escribir programas más grandes en menos líneas (y con menos esfuerzo). Por supuesto, los programas más pequeños son realmente más fáciles en Tkinter porque no tiene que preocuparse por la misma sobrecarga para widgets que otros kits de herramientas, y eso es lo que me encanta de Tkinter: escribir programas más pequeños. @psicopoo, de nada! –

28

El enfoque general para centrar una ventana es calcular las coordenadas de pantalla adecuada para la tapa de píxel izquierdo de la ventana:

x = (screen_width/2) - (window_width/2) 
y = (screen_height/2) - (window_height/2) 

Sin embargo, esto es no suficiente para precisión centrar una ventana tkinter (en Windows 7 al menos);
porque el ancho y la altura de la ventana devueltos por cualquier método no incluirá el marco más externo, con el título y los botones min/max/close. Tampoco incluirá un menu bar (con Archivo, Edición, etc.). Afortunadamente hay un camino para encontrar las dimensiones de estos.

Aquí es la función más básica, que no tiene en cuenta el problema antes mencionado:

def center(win): 
    win.update_idletasks() 
    width = win.winfo_width() 
    height = win.winfo_height() 
    x = (win.winfo_screenwidth() // 2) - (width // 2) 
    y = (win.winfo_screenheight() // 2) - (height // 2) 
    win.geometry('{}x{}+{}+{}'.format(width, height, x, y)) 

Alternativas: winfo_reqwidth(), winfo_reqheight()

En primer lugar, y ante todo, queremos llamar al método de la ventana update_idletasks()
directamente antes de recuperar cualquier geometría, para garantizar que los valores devueltos sean precisos.

Es importante comprender el geometry strings utilizado con el método geometry().
La primera mitad es el ancho y la altura de la ventana excluyendo la estructura exterior,
y la segunda mitad corresponde a las coordenadas xey superior izquierda del marco exterior.

Existen cuatro métodos que nos permitirán determinar las dimensiones del marco exterior.
winfo_rootx() nos dará la coordenada x superior izquierda de la ventana, excluyendo el marco exterior.
winfo_x() nos dará la coordenada x superior izquierda del marco exterior.
Su diferencia es el ancho del marco exterior.

frm_width = win.winfo_rootx() - win.winfo_x() 
win_width = win.winfo_width() + (2*frm_width) 

La diferencia entre winfo_rooty() y winfo_y() será la altura de nuestra barra de título/de la barra de menús.

titlebar_height = win.winfo_rooty() - win.winfo_y() 
win_height = win.winfo_height() + (titlebar_height + frm_width) 

Aquí es la función completa, en un ejemplo de trabajo:

import tkinter # Python 3 

def center(win): 
    """ 
    centers a tkinter window 
    :param win: the root or Toplevel window to center 
    """ 
    win.update_idletasks() 
    width = win.winfo_width() 
    frm_width = win.winfo_rootx() - win.winfo_x() 
    win_width = width + 2 * frm_width 
    height = win.winfo_height() 
    titlebar_height = win.winfo_rooty() - win.winfo_y() 
    win_height = height + titlebar_height + frm_width 
    x = win.winfo_screenwidth() // 2 - win_width // 2 
    y = win.winfo_screenheight() // 2 - win_height // 2 
    win.geometry('{}x{}+{}+{}'.format(width, height, x, y)) 
    win.deiconify() 

if __name__ == '__main__': 
    root = tkinter.Tk() 
    root.attributes('-alpha', 0.0) 
    menubar = tkinter.Menu(root) 
    filemenu = tkinter.Menu(menubar, tearoff=0) 
    filemenu.add_command(label="Exit", command=root.destroy) 
    menubar.add_cascade(label="File", menu=filemenu) 
    root.config(menu=menubar) 
    frm = tkinter.Frame(root, bd=4, relief='raised') 
    frm.pack(fill='x') 
    lab = tkinter.Label(frm, text='Hello World!', bd=4, relief='sunken') 
    lab.pack(ipadx=4, padx=4, ipady=4, pady=4, fill='both') 
    center(root) 
    root.attributes('-alpha', 1.0) 
    root.mainloop() 

Una forma de prevenir viendo el movimiento ventana a través de la pantalla es utilizar .attributes('-alpha', 0.0) para hacer la ventana totalmente transparente y luego configurar a 1.0 después de que la ventana se haya centrado. El uso de withdraw() o iconify() más tarde seguido por deiconify() no parece funcionar bien, para este propósito, en Windows 7. Tenga en cuenta que utilizo deiconify() como un truco para activar la ventana.

+0

Parece que la función central no funciona en X11 sin procesar en lugar de SSH, en Windows a través de Xming. Parece adherirse a la colocación de formulario estándar de Window, empezando por la esquina superior izquierda. La salida de geometría parece correcta, pero es posible que algún tipo de interacción con Xming no funcione. – Kumba

+0

el truco -alpha no parece funcionar en Linux. No estoy seguro de qué más puedo hacer. –

15

Tk proporciona una función de ayuda que puede hacer esto como tk::PlaceWindow, pero no creo que haya sido expuesto como un método envuelto en Tkinter. Debería centrar un widget usando lo siguiente:

from tkinter import * 

app = Tk() 
app.eval('tk::PlaceWindow %s center' % app.winfo_pathname(app.winfo_id())) 
app.mainloop() 

Esta función también debe ocuparse de varias pantallas correctamente. También tiene opciones para centrar sobre otro widget o relativo al puntero (utilizado para colocar menús emergentes), para que no se salgan de la pantalla.

+1

Desafortunadamente no considera el marco exterior con el título y el botón de cerrar; por lo que todavía no está completamente centrado (en Windows 7 al menos). Esta es la breve alternativa a la respuesta de Wayne. +1 por mencionar esto. Tal vez podamos actualizarlo por los desarrolladores de Tk. –

+0

winfo_pathname falla en Python 3.5.1 (en Win10) con '_tkinter.TclError: id de ventana" 2885868 "no existe en esta aplicación'. No creo que haya tenido esto funcionando en Python 3, pero a partir de su importación en minúsculas, ¿supongo que funcionó para usted? Pude actualizar el código usando winfo_toplevel en su lugar: 'app.eval ('tk :: PlaceWindow% s center'% app.winfo_toplevel())' – idbrii

0

Utilizo la opción de marco y expandir. Muy simple. Quiero algunos botones en el medio de la pantalla. Cambiar el tamaño de la ventana y el botón permanecer en el medio. Esta es mi solución.

frame = Frame(parent_window) 
Button(frame, text='button1', command=command_1).pack(fill=X) 
Button(frame, text='button2', command=command_2).pack(fill=X) 
Button(frame, text='button3', command=command_3).pack(fill=X) 
frame.pack(anchor=CENTER, expand=1) 
1

he encontrado una solución para la misma pregunta on this site

from tkinter import Tk 
from tkinter.ttk import Label 
root = Tk() 
Label(root, text="Hello world").pack() 

# Apparently a common hack to get the window size. Temporarily hide the 
# window to avoid update_idletasks() drawing the window in the wrong 
# position. 
root.withdraw() 
root.update_idletasks() # Update "requested size" from geometry manager 

x = (root.winfo_screenwidth() - root.winfo_reqwidth())/2 
y = (root.winfo_screenheight() - root.winfo_reqheight())/2 
root.geometry("+%d+%d" % (x, y)) 

# This seems to draw the window frame immediately, so only call deiconify() 
# after setting correct window position 
root.deiconify() 
root.mainloop() 

seguro, lo cambié correspondientemente a mis propósitos, funciona.

Cuestiones relacionadas