I'm trying to center a tkinter window. I know I can programatically get the size of the window and the size of the screen and use that to set the geometry, but I'm wondering if there's a simpler way to center the window on the screen.
To position the Tkinters widgets, we can use place() geometry manager, where we will specify the anchor property. It can take (NW, N, NE, W, CENTER, E, SW, S, SE) as the position of the widget.
To do it right you need to get the font used by the window manager, compute the actual width of the title string based on that font, compute the width of a space, and then adjust accordingly.
To configure and align the text at the CENTER of a Tkinter Text widget, we can use justify=CENTER property.
mainloop() tells Python to run the Tkinter event loop. This method listens for events, such as button clicks or keypresses, and blocks any code that comes after it from running until you close the window where you called the method.
The simplest (but possibly inaccurate) method is to use tk::PlaceWindow, which takes the pathname of a toplevel window as an argument. The main window's pathname is .
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()
The problem
Simple solutions ignore the outermost frame with the title bar and the menu bar, which leads to a slight offset from being truly centered.
The solution
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()
With tkinter you always want to call the update_idletasks()
method
directly before retrieving any geometry, to ensure that the values returned are accurate.
There are four methods that allow us to determine the outer-frame's dimensions.winfo_rootx()
will give us the window's top left x coordinate, excluding the outer-frame.winfo_x()
will give us the outer-frame's top left x coordinate.
Their difference is the outer-frame's width.
frm_width = win.winfo_rootx() - win.winfo_x() win_width = win.winfo_width() + (2*frm_width)
The difference between winfo_rooty()
and winfo_y()
will be our title-bar / menu-bar's height.
titlebar_height = win.winfo_rooty() - win.winfo_y() win_height = win.winfo_height() + (titlebar_height + frm_width)
You set the window's dimensions and the location with the geometry method. The first half of the geometry string is the window's width and height excluding the outer-frame,
and the second half is the outer-frame's top left x and y coordinates.
win.geometry(f'{width}x{height}+{x}+{y}')
You see the window move
One way to prevent seeing the window move across the screen is to use .attributes('-alpha', 0.0)
to make the window fully transparent and then set it to 1.0
after the window has been centered. Using withdraw()
or iconify()
later followed by deiconify()
doesn't seem to work well, for this purpose, on Windows 7. I use deiconify()
as a trick to activate the window.
Making it optional
You might want to consider providing the user with an option to center the window, and not center by default; otherwise, your code can interfere with the window manager's functions. For example, xfwm4 has smart placement, which places windows side by side until the screen is full. It can also be set to center all windows, in which case you won't have the problem of seeing the window move (as addressed above).
Multiple monitors
If the multi-monitor scenario concerns you, then you can either look into the screeninfo project, or look into what you can accomplish with Qt (PySide2) or GTK (PyGObject), and then use one of those toolkits instead of tkinter. Combining GUI toolkits results in an unreasonably large dependency.
You can try to use the methods winfo_screenwidth
and winfo_screenheight
, which return respectively the width and height (in pixels) of your Tk
instance (window), and with some basic math you can center your window:
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()
I am calling update_idletasks
method before retrieving the width and the height of the window in order to ensure that the values returned are accurate.
Tkinter doesn't see if there are 2 or more monitors extended horizontal or vertical. So, you 'll get the total resolution of all screens together and your window will end-up somewhere in the middle of the screens.
PyQt from the other hand, doesn't see multi-monitors environment either, but it will get only the resolution of the Top-Left monitor (Imagine 4 monitors, 2 up and 2 down making a square). So, it does the work by putting the window on center of that screen. If you don't want to use both, PyQt and Tkinter, maybe it would be better to go with PyQt from start.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With