Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 991
psicopoo Avatar asked Jul 28 '10 12:07

psicopoo


People also ask

How do I center a Tkinter frame?

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.

How do I center a title in Tkinter window?

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.

How do I center an object in Tkinter?

To configure and align the text at the CENTER of a Tkinter Text widget, we can use justify=CENTER property.

What does Mainloop () do Tkinter?

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.


2 Answers

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.

like image 84
Honest Abe Avatar answered Sep 18 '22 16:09

Honest Abe


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.

like image 26
Wayne Werner Avatar answered Sep 19 '22 16:09

Wayne Werner