¿Cómo centrar una ventana en la pantalla en Tkinter?
Estoy intentando centrar una ventana de tkinter. Sé que puedo obtener mediante programación el tamaño de la ventana y el tamaño de la pantalla y usarlo para configurar la geometría, pero me pregunto si hay una forma más sencilla de centrar la ventana en la pantalla.
El método más simple (pero posiblemente inexacto) es usar tk::PlaceWindow , que toma la ruta de acceso de una ventana de nivel superior como argumento. La ruta de acceso de la ventana principal es.
import tkinter
root = tkinter.Tk()
root.eval('tk::PlaceWindow . center')
second_win = tkinter.Toplevel(root)
root.eval(f'tk::PlaceWindow {str(second_win)} center')
root.mainloop()
El problema
Las soluciones simples ignoran el marco más externo con la barra de título y la barra de menú , lo que lleva a un ligero desplazamiento respecto de estar realmente centrado.
La solución
import tkinter # Python 3
def center(win):
"""
centers a tkinter window
:param win: the main window 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()
Con tkinter siempre querrás llamar al update_idletasks()
método
directamente antes de recuperar cualquier geometría, para asegurarte de que los valores devueltos sean precisos.
Existen cuatro métodos que nos permiten 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/barra de menú.
titlebar_height = win.winfo_rooty() - win.winfo_y()
win_height = win.winfo_height() + (titlebar_height + frm_width)
Usted establece las dimensiones de la ventana y la ubicación con el método de geometría . La primera mitad de la cadena de geometría es el ancho y el alto de la ventana excluyendo el marco exterior,
y la segunda mitad son las coordenadas x e y superiores izquierdas del marco exterior.
win.geometry(f'{width}x{height}+{x}+{y}')
Ves la ventana moverse
Una forma de evitar que la ventana se mueva por la pantalla es hacer
.attributes('-alpha', 0.0)
que la ventana sea completamente transparente y luego configurarla 1.0
después de que la ventana se haya centrado. Usar withdraw()
o iconify()
después seguido de deiconify()
no parece funcionar bien, para este propósito, en Windows 7. Lo uso deiconify()
como truco para activar la ventana.
Haciéndolo opcional
Es posible que desee considerar brindarle al usuario la opción de centrar la ventana y no centrarla de forma predeterminada; de lo contrario, su código puede interferir con las funciones del administrador de ventanas. Por ejemplo, xfwm4 tiene una ubicación inteligente, que coloca las ventanas una al lado de la otra hasta que la pantalla está llena. También se puede configurar para centrar todas las ventanas, en cuyo caso no tendrá el problema de ver la ventana moverse (como se mencionó anteriormente).
Varios monitores
Si le preocupa el escenario de varios monitores, puede consultar el proyecto screeninfo o ver qué puede lograr con Qt (PySide6) o GTK (PyGObject) y luego usar uno de esos kits de herramientas en lugar de tkinter. La combinación de kits de herramientas GUI da como resultado una dependencia excesivamente grande.
Puedes intentar usar los métodos winfo_screenwidth
y winfo_screenheight
, que devuelven respectivamente el ancho y el alto (en píxeles) de tu Tk
instancia (ventana), y con algunas matemáticas básicas puedes centrar tu ventana:
import tkinter as tk
from PyQt4 import QtGui # or PySide
def center(toplevel):
toplevel.update_idletasks()
# Tkinter way to find the screen resolution
# screen_width = toplevel.winfo_screenwidth()
# screen_height = toplevel.winfo_screenheight()
# PyQt way to find the screen resolution
app = QtGui.QApplication([])
screen_width = app.desktop().screenGeometry().width()
screen_height = app.desktop().screenGeometry().height()
size = tuple(int(_) for _ in toplevel.geometry().split('+')[0].split('x'))
x = screen_width/2 - size[0]/2
y = screen_height/2 - size[1]/2
toplevel.geometry("+%d+%d" % (x, y))
toplevel.title("Centered!")
if __name__ == '__main__':
root = tk.Tk()
root.title("Not centered")
win = tk.Toplevel(root)
center(win)
root.mainloop()
Estoy llamando update_idletasks
al método antes de recuperar el ancho y el alto de la ventana para asegurarme de que los valores devueltos sean precisos.
Tkinter no ve si hay 2 o más monitores extendidos horizontal o verticalmente. Entonces, obtendrás la resolución total de todas las pantallas juntas y tu ventana terminará en algún lugar en el medio de las pantallas.
PyQt, por otro lado, tampoco ve un entorno de monitores múltiples, pero solo obtendrá la resolución del monitor superior izquierdo (imagine 4 monitores, 2 arriba y 2 abajo formando un cuadrado). Entonces, hace el trabajo colocando la ventana en el centro de esa pantalla. Si no desea utilizar PyQt y Tkinter , tal vez sería mejor utilizar PyQt desde el principio.
Esta respuesta es mejor para entender al principiante.
#import tkinter as tk
win = tk.Tk() # Creating instance of Tk class
win.title("Centering windows")
win.resizable(False, False) # This code helps to disable windows from resizing
window_height = 500
window_width = 900
screen_width = win.winfo_screenwidth()
screen_height = win.winfo_screenheight()
x_cordinate = int((screen_width/2) - (window_width/2))
y_cordinate = int((screen_height/2) - (window_height/2))
win.geometry("{}x{}+{}+{}".format(window_width, window_height, x_cordinate, y_cordinate))
win.mainloop()