88

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.

nbro
  • 15,395
  • 32
  • 113
  • 196
psicopoo
  • 1,576
  • 1
  • 12
  • 20
  • app.eval('tk::PlaceWindow %s center' % app.winfo_pathname(app.winfo_id())) doesn't work on KDE Plasma, I saw this code many time but never worked fine, the longer version is much better and work everytime on windows or linux. – Requiem May 07 '19 at 20:36

15 Answers15

93

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.

Honest Abe
  • 8,430
  • 4
  • 49
  • 64
  • It looks like the center function doesn't work in raw X11 over SSH, on Windows via Xming. Seems to adhere to Window's standard form placement, starting from the top-left. Geometry output looks correct, but it's possible some kind of interaction with Xming isn't working? – Kumba Jun 24 '13 at 11:44
  • 1
    the -alpha trick doesn't seem to work in Linux. Not sure what else I can do. – Gabriel Staples Aug 21 '16 at 02:18
  • Your code sets the window to a fixed size. The window will not automatically resize itself when widgets are added/removed/resized. It would be better to set only the window's position and leave its size alone. – Aran-Fey Jul 26 '18 at 10:04
  • 1
    Aran-Fey, i have implemented this code into my Method, that creates a Toplevel window, which also includes a Label with an Image that gets resized. If you call this method AFTER your call the resizing methods, it will place the window with the resized images in the center, so it does work for me, the trick just was to call the method after the resizing. – James Feb 06 '19 at 13:42
71

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.

Aran-Fey
  • 39,665
  • 11
  • 104
  • 149
Wayne Werner
  • 49,299
  • 29
  • 200
  • 290
  • 20
    "not most powerful" is a bit subjective. It has as much "power" as other toolkits (depending on your definition of "power"), it just doesn't do as much hand-holding. It's the difference between building a house with pre-made walls (some other toolkits) or building it from a pile of lumber (Tk). You can pretty much do anything in Tkinter that you can do in other toolkits, you just have to sometimes work a little for it. – Bryan Oakley Jul 28 '10 at 15:48
  • 5
    @Bryan - that's precisely what I mean by power. Sure you can get across the United States in a Yugo (maybe) and a Ferrari, but one of them can get you there a lot faster. Of course they both have the power to interact with the user in certain ways, but Tkinter doesn't have the power to let you write larger programs in less lines (and with less effort). Of course, smaller programs are actually easier in Tkinter because you don't have to worry about the same overhead for widgets as other toolkits, and that's what I love Tkinter for - writing smaller programs. @psicopoo, you're welcome! – Wayne Werner Jul 28 '10 at 16:56
  • 1
    When I use this method, the window _jumps_ to center and is not appeared in the center at first. Why it is so? – Mohammad Aug 10 '16 at 04:12
  • @mohammad.k because tkinter gets to draw the window before the call happens. There are some techniques that you could probably use to get around that I think, such as withdrawing the window first. – Wayne Werner Aug 10 '16 at 06:26
  • @WayneWerner If I withdraw the window, after deiconify nothing is shown but a tiny rectangle. – Mohammad Aug 10 '16 at 06:32
  • Do you re-size the window when it's de-iconified? – Wayne Werner Aug 10 '16 at 13:58
  • Good solution, but there is an issue. Functions `.winfo_screenwidth()` and `.winfo_screenheigth()` doesn't see if there are 2 or more monitors connected on PC horizontal or vertical. So, let's say that you have 2 monitors on PC extended horizontal, this will launch the "half left" window on the right edge of the left screen and "half right" window on the left edge of the right screen. Is there any way to get the pixels of the active monitor, the monitors number or if they are extended vertical or horizontal ?? – ioaniatr May 24 '18 at 23:14
  • 2
    Heads up: I've edited your answer so it no longer sets the window to a fixed size. Your original code prevented the window from resizing itself when widgets were added/removed/resized. – Aran-Fey Jul 26 '18 at 10:02
  • By using a totally different GUI toolkit, this adds an unreasonably large dependency and complication for packaging. Can anyone with a few years of experience in software distribution actually recommend this approach? I can't. – Honest Abe Sep 16 '20 at 23:34
  • Sure, I wouldn't /recommend/ it - that's just silly. But if centering your window, cross platform, is a desire, you probably don't want tkinter – Wayne Werner Sep 17 '20 at 00:04
  • Glad to hear we are in agreement Wayne. Unfortunately, the example as provided by 3rd party editors uses this approach, which implies it's a reasonable solution. – Honest Abe Sep 17 '20 at 00:38
  • 2
    Thanks!! in my case I had to install `PyQt5` and `import QtWidgets` from it (instead of `from PyQt4 import QtGui`). I guess it's some kind of upgrade or update :) – Benny Jun 14 '21 at 08:05
44

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()
  • Have a problem with two screens, the with is primary screen + secondary screen. The window starts between two screens. – e-info128 Apr 02 '21 at 03:23
31

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.

Honest Abe
  • 8,430
  • 4
  • 49
  • 64
patthoyts
  • 32,320
  • 3
  • 62
  • 93
  • 2
    Unfortunately it does not consider the outer frame with the title and close button; so it's still not fully centered (on Windows 7 at least). This is the short alternative to Wayne's answer. +1 for bringing this up though. Maybe we can get it upgraded by the Tk developers. – Honest Abe Jan 30 '15 at 02:35
  • 2
    winfo_pathname fails on Python 3.5.1 (on Win10) with `_tkinter.TclError: window id "2885868" doesn't exist in this application`. I don't think I ever had this working on Python 3, but from your lower case import, I assume it worked for you? I was able to update the code by using winfo_toplevel instead: `app.eval('tk::PlaceWindow %s center' % app.winfo_toplevel())` – idbrii Jan 17 '17 at 19:11
22

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()
bitagoras
  • 483
  • 4
  • 8
13

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.

Ievgen S.
  • 191
  • 2
  • 6
9

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()
NightOwl888
  • 55,572
  • 24
  • 139
  • 212
Hoseong Jeon
  • 1,240
  • 2
  • 12
  • 20
2

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)
easyScript
  • 353
  • 3
  • 6
2

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)
crispengari
  • 7,901
  • 7
  • 45
  • 53
2
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()
User
  • 1,460
  • 14
  • 11
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()
Nick
  • 98
  • 7
2

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)
Jan Willem
  • 820
  • 1
  • 6
  • 24
1

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.

ashg
  • 11
  • 1
1

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

eatsfood
  • 950
  • 2
  • 21
  • 31
Gorio
  • 11
  • 1
1

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}")
Dan
  • 160
  • 1
  • 6
  • 19