是否可以将 tkinter.simpledialog window 居中?

Is it possible to center a tkinter.simpledialog window?

我在一个更大的脚本中包含了条件密码捕获。代码如下所示:

if thing:  # found token, don't need password
    do stuff
else:  # no token, get password in popup
    try:
        import tkinter as tk
        import tkinter.simpledialog
        tk.Tk().withdraw()
        passwd = tkinter.simpledialog.askstring("Password", "Enter password:", show="*")
        return passwd

功能上没问题,但我的大多数用户都在 3840x1600 分辨率的显示器上,所以当对话框在左上角弹出时,很容易错过。

是否有一种简单的方法来覆盖简单对话框 class 以告诉它出现在监视器上的某个 X/Y,或者我唯一的选择是构建一个完整的 mainloop()

我找不到覆盖任何 classes 以直接修改基础 _QueryString class 的方法(而且它是私有的,所以我不想使用它,无论如何我可能都找不到简单的方法)。所以我只是写了一个自定义 askstring 函数。该功能是这样的,您需要提供父级和几何图形,它将首先安排一个内部函数(可以是一个适当的外部函数,但它应该没问题)以获取该父级的小部件中的最后一个小部件,并在预定之后时间应该是新对话框,所以它应该得到那个。 (检查以检查该小部件是否派生自 dialog.Dialog,而 dialog.Dialog_QueryString 继承的)。然后只需更改几何(因为在继承链上有 Toplevel 显然有一个 geometry 方法):

import tkinter as tk
import tkinter.simpledialog as dialog


def askstring(title, prompt, geometry='', **kwargs):
    def change_geometry():
        if not geometry:
            return
        widget = kwargs['parent'].winfo_children()[-1]
        if isinstance(widget, dialog.Dialog):
            widget.geometry(geometry)

    if 'parent' in kwargs:
        kwargs['parent'].after(10, change_geometry)
    return dialog.askstring(title, prompt, **kwargs)


root = tk.Tk()
root.withdraw()

askstring('Title', 'Prompt', geometry='300x200+200+200', parent=root)

有用:

so when the dialog pops up at the top left it's easy to miss.

如果指定可选参数 parent,对话框将出现在 window 的中间并在内部获得焦点。确保你的 window 是通过 .update_idletasks() 映射的,以获得相应的坐标。您也可以考虑让您的 window 透明而不是撤回。

import tkinter as tk
import tkinter.simpledialog

def ask_pw():
    pw = tkinter.simpledialog.askstring("Password",
                                        "Enter password:",
                                        show="*",
                                        parent=root)
root = tk.Tk()
root.geometry('250x250+500+500')
root.update_idletasks()
ask_pw()
#root.withdraw()
root.mainloop()

由于对话框是相对于其父级的位置放置的,因此您可以将其父级居中,即根 window 在您的例子中:

import tkinter as tk
from tkinter import simpledialog

root = tk.Tk()
# center root window
root.tk.eval(f'tk::PlaceWindow {root._w} center')
root.withdraw()

# set parent=root
passwd = simpledialog.askstring('Password', 'Enter password:', show='*', parent=root)