Home > Mobile >  Is it possible to center a tkinter.simpledialog window?
Is it possible to center a tkinter.simpledialog window?

Time:11-10

I'm including a conditional password capture in a larger script. The code looks like this:

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

functionally it's fine, but most of my users are on monitors at 3840x1600 resolution, so when the dialog pops up at the top left it's easy to miss.

Is there a brief way to override the simpledialog class to tell it to appear at a certain X/Y on the monitor, or is my only option to build a full mainloop()?

CodePudding user response:

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

If you specify the optional argument parent, the dialogbox will appear in the middle of your window and gets the focus internally. Make sure your window is mapped via .update_idletasks() to get corresponding coordinates. You may consider also to make your window transparent instead of withdraw.

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

CodePudding user response:

I couldn't find a way to override any of the classes to directly modify the underlying _QueryString class (also it is private so I didn't want to use that and I probably wouldn't have found an easy way with that anyways). So I just wrote a custom askstring function. The functionality is such that you need to provide the parent and the geometry and it will first schedule an inner function (could be a proper outer one but it should be fine) to get the last widget in the widgets of that parent and after the scheduled time it should be the new dialog so it should get that. (Check in place to check if that widget is derived from dialog.Dialog which is from what the _QueryString inherits). Then just change the geometry (because up the inheritance chain there is Toplevel which obviously has a geometry method):

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)

Useful:

CodePudding user response:

As the dialog is placed relative to the position of its parent, so you can center its parent, i.e. root window in your case:

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)
  • Related