How to center a window on the screen in Tkinter?

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.

174925 次浏览

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.

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 (PySide6) or GTK (PyGObject), and then use one of those toolkits instead of tkinter. Combining GUI toolkits results in an unreasonably large dependency.

Tk provides a helper function that can do this as tk::PlaceWindow, but I don't believe it has been exposed as a wrapped method in Tkinter. You would center a widget using the following:

from tkinter import *


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

This function should deal with multiple displays correctly as well. It also has options to center over another widget or relative to the pointer (used for placing popup menus), so that they don't fall off the screen.

I use frame and expand option. Very simple. I want some buttons in the middle of screen. Resize window and button stay in the middle. This is my solution.

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)

I have found a solution for the same question 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()

sure, I changed it correspondingly to my purposes, it works.

Use:

import tkinter as tk


if __name__ == '__main__':
root = tk.Tk()
root.title('Centered!')


w = 800
h = 650


ws = root.winfo_screenwidth()
hs = root.winfo_screenheight()
x = (ws/2) - (w/2)
y = (hs/2) - (h/2)


root.geometry('%dx%d+%d+%d' % (w, h, x, y))


root.mainloop()

This answer is better for understanding beginner

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

This works also in Python 3.x and centers the window on screen:

from tkinter import *


app = Tk()
app.eval('tk::PlaceWindow . center')
app.mainloop()

CENTERING THE WINDOW IN PYTHON Tkinter This is the most easiest thing in tkinter because all we must know is the dimension of the window as well as the dimensions of the computer screen. I come up with the following code which can help someone somehow and i did add some comments so that they can follow up.

code

    #  create a window first
root = Tk()
# define window dimensions width and height
window_width = 800
window_height = 500
# get the screen size of your computer [width and height using the root object as foolows]
screen_width = root.winfo_screenwidth()
screen_height = root.winfo_screenheight()
# Get the window position from the top dynamically as well as position from left or right as follows
position_top = int(screen_height/2 -window_height/2)
position_right = int(screen_width / 2 - window_width/2)
# this is the line that will center your window
root.geometry(f'{window_width}x{window_height}+{position_right}+{position_top}')
# initialise the window
root.mainloop(0)

This method is cross-platform, works for multiple monitors/screens (targets the active screen), and requires no other libraries than Tk. The root window will appear centered without any unwanted "flashing" or animations:

import tkinter as tk


def get_geometry(frame):
geometry = frame.winfo_geometry()
match = re.match(r'^(\d+)x(\d+)\+(\d+)\+(\d+)$', geometry)
return [int(val) for val in match.group(*range(1, 5))]


def center_window(root):
"""Must be called after application is fully initialized
so that the root window is the true final size."""
# Avoid unwanted "flashing" by making window transparent until fully ready
root.attributes('-alpha', 0)


# Get dimensions of active screen/monitor using fullscreen trick; withdraw
# window before making it fullscreen to preserve previous dimensions
root.withdraw()
root.attributes('-fullscreen', True)
root.update_idletasks()
(screen_width, screen_height, *_) = get_geometry(root)
root.attributes('-fullscreen', False)


# Restore and get "natural" window dimensions
root.deiconify()
root.update_idletasks()
(window_width, window_height, *_) = get_geometry(root)


# Compute and set proper window center
pos_x = round(screen_width / 2 - window_width / 2)
pos_y = round(screen_height / 2 - window_height / 2)
root.geometry(f'+{pos_x}+{pos_y}')
root.update_idletasks()
    

root.attributes('-alpha', 1)


# Usage:
root = tk.Tk()
center_window(root)

Note that at every point where window geometry is modified, update_idletasks() must be called to force the operation to occur synchronously/immediately. It uses Python 3 functionality but can easily be adapted to Python 2.x if necessary.

from tkinter import *


root = Tk()


# Gets the requested values of the height and widht.
windowWidth = root.winfo_reqwidth()
windowHeight = root.winfo_reqheight()
print("Width",windowWidth,"Height",windowHeight)


# Gets both half the screen width/height and window width/height
positionRight = int(root.winfo_screenwidth()/2 - windowWidth/2)
positionDown = int(root.winfo_screenheight()/2 - windowHeight/2)


# Positions the window in the center of the page.
root.geometry("+{}+{}".format(positionRight, positionDown))




root.mainloop()

I think this is a possible solution:

import pyautogui
from tkinter import *


x=pyautogui.size()[0]
y=pyautogui.size()[1]


root=Tk()
root.geometry('300x200+'+str(int(x/2-150))+'+'+str(int(y/2-100)))
# x/2 and y/2 fin the display center and -<a half of x root> and -<a half of #y root> #serve to center root


root.mainloop()


The way I do it is import the get_monitors function from screeninfo. Then set my preferred width and height of my window. Finally, I calculate the position and interpolate everything as a string for the .geometry() method's input.

from screeninfo import get_monitors
import tkinter as tk


# Set the size of my window to whatever I like
WIN_WIDTH = 350
WIN_HEIGHT = 250


root = tk.Tk()
root.geometry.(f"{WIN_WIDTH}x{WIN_HEIGHT}+{(get_monitors()[0].width - WIN_WIDTH)//2}+{(get_monitors()[0].height - WIN_HEIGHT)//2}")

Here is a simple one without the need of external modules:

import tkinter as tk
root = tk.Tk()


WIDTH = 300
HEIGHT = 250


x = int((root.winfo_screenwidth() / 2) - (WIDTH / 2))
y = int((root.winfo_screenheight() / 2) - (HEIGHT / 2))


root.geometry(f'{WIDTH}x{HEIGHT}+{x}+{y}')


root.mainloop()

Multi monitor solution (even complex geometries)

I used a combination of tkinter and screeninfo to be able to center windows on the currently active monitor in the case you have a root window and want to place a popup on the center of the screen the root window currently resides in.

In this example, root is the root window and popup is the popup to be placed in the center of the screen where the root is located.

This solutions also works if your screens are setup in a more complex way than next to each other, for example on top of each other.

import tkinter as tk
from screeninfo import get_monitors


def place_popup(popup: tk.Toplevel, root: tk.Tk, width: int, height: int) -> None:
"""Places a new window in the middle of the selected screen"""
monitor = get_monitor_from_coord(root.winfo_x(), root.winfo_y())
popup.geometry(
f"{width}x{height}+{(monitor.width - width) // 2 + monitor.x}+{(monitor.height - height) // 2+ monitor.y}")


def get_monitor_from_coord(x, y):
"""Find the active monitor from tkinter geometry coords"""
monitors = get_monitors()


for m in reversed(monitors):
if m.x <= x <= m.width + m.x and m.y <= y <= m.height + m.y:
return m
return monitors[0]


...
place_popup(popup, root, width, height)