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:
- source code for
tkinter.simpledialog
which is what I used to solve this (also from previous experience)
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)