如何在Tkinter中使窗口居中?

93 投票
15 回答
213875 浏览
提问于 2025-04-16 01:58

我想把一个tkinter窗口居中显示。我知道可以通过编程获取窗口的大小和屏幕的大小,然后用这些信息来设置窗口的位置,但我在想有没有更简单的方法可以让窗口在屏幕上居中。

15 个回答

45

这个回答更适合初学者理解

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

最简单(但可能不准确)的方法是使用 tk::PlaceWindow,这个方法需要一个顶层窗口的 路径名 作为参数。主窗口的路径名是 .

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

问题

简单的解决方案忽略了带标题栏和 菜单栏 的最外层框架,这样就会导致窗口看起来没有完全居中。

解决方案

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

在使用 tkinter 时,你总是想在获取任何窗口尺寸之前,先调用 update_idletasks() 方法,
这样可以确保返回的值是准确的。

我们有四个方法可以用来确定外框的尺寸。
winfo_rootx() 会给我们窗口左上角的 x 坐标,不包括 外框。
winfo_x() 会给我们外框左上角的 x 坐标。
这两者的差值就是外框的宽度。

frm_width = win.winfo_rootx() - win.winfo_x()
win_width = win.winfo_width() + (2*frm_width)

winfo_rooty()winfo_y() 的差值就是我们的标题栏和菜单栏的高度。

titlebar_height = win.winfo_rooty() - win.winfo_y()
win_height = win.winfo_height() + (titlebar_height + frm_width)

你可以用 geometry 方法 来设置窗口的尺寸和位置。
几何字符串的前半部分是窗口的宽度和高度,不包括 外框,
后半部分是外框左上角的 x 和 y 坐标。

win.geometry(f'{width}x{height}+{x}+{y}')

你会看到窗口移动

为了避免看到窗口在屏幕上移动,可以使用 .attributes('-alpha', 0.0) 让窗口完全透明,然后在窗口居中后再设置为 1.0。在 Windows 7 上,使用 withdraw()iconify() 后再用 deiconify() 似乎效果不好。我用 deiconify() 作为一个小技巧来激活窗口。


让它变得可选

你可能想考虑给用户提供一个选项,让他们选择是否居中窗口,而不是默认居中;否则,你的代码可能会干扰窗口管理器的功能。例如,xfwm4 有智能放置功能,可以将窗口并排放置,直到屏幕满了。它也可以设置为居中所有窗口,这样就不会出现看到窗口移动的问题(如上所述)。


多显示器

如果你担心多显示器的情况,可以查看 screeninfo 项目,或者看看你能用 Qt (PySide6)GTK (PyGObject) 实现什么,然后选择其中一个工具包,而不是使用 tkinter。将多个 GUI 工具包结合使用会导致不合理的依赖关系。

72

你可以试着使用 winfo_screenwidthwinfo_screenheight 这两个方法,它们分别返回你 Tk 窗口的宽度和高度(单位是像素)。然后通过一些简单的数学计算,你就可以把窗口放在屏幕中央:

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

我在获取窗口的宽度和高度之前调用了 update_idletasks 方法,这样可以确保得到的值是准确的。

Tkinter 不会识别是否有两个或更多的显示器横向或纵向排列。所以,你会得到所有屏幕的总分辨率,然后你的窗口就会出现在这些屏幕的中间位置。

PyQt 也看不到多显示器的环境,但它只会获取左上角显示器的分辨率(想象一下有四个显示器,上面两个,下面两个,形成一个正方形)。所以,它会把窗口放在那个屏幕的中央。如果你不想使用 PyQtTkinter,那么从一开始就选择 PyQt 可能会更好。

撰写回答