3

How can I get a blocking modal input dialog box in standard Python?

I need the user to input a value before the code can proceed.

Here is some not-working test code, but the idea is that I should be able to call MyDialog from anywhere in the script, so this is just a simplified example.

import tkinter

class MyDialog:
    def __init__(self, prompt):
        self.top = tkinter.Toplevel()
        tkinter.Label(self.top, text=prompt).pack()
        self.e = tkinter.Entry(self.top)
        self.e.pack(padx=5)
        tkinter.Button(self.top, text="OK", command=self.ok).pack(pady=5)

    def ok(self):
        self.top.destroy()
        return self.e.get()


root = tkinter.Tk()
userName = MyDialog('Enter your name')
tkinter.Label(root, text="Hello {}".format(userName)).pack()

root.mainloop()

The dialog should not only disable the master window, but block whatever code called it. And it should be able to pass the value back to the calling code.

mcu
  • 3,302
  • 8
  • 38
  • 64
  • Possible duplicate of [Disable the underlying window when a popup is created in Python TKinter](http://stackoverflow.com/questions/15363923/disable-the-underlying-window-when-a-popup-is-created-in-python-tkinter) – Steven Summers Nov 08 '15 at 16:05
  • @StevenSummers That solution does not provide code blocking. – mcu Nov 08 '15 at 16:30
  • Could you clarify on what code would be blocked and what kind of value would be passed back? Such as, if a button opened `MyDialog` then block the button from opening again? – Steven Summers Nov 08 '15 at 16:38
  • Line `tkinter.Label(root...` should not get executed until the user has closed the dialog, at which time you will have a user-supplied value in `userName`. – mcu Nov 08 '15 at 16:40
  • Can't you create that in the button click event? It seems strange to have it outside the event. – Steven Summers Nov 08 '15 at 17:02
  • Do you mean create `userName`? This still would not block the code. – mcu Nov 08 '15 at 17:07

1 Answers1

3

The solution requires two critical pieces. First, use grab_set to block all events in the other window (or, more correctly, send all events to the dialog window). Second, use wait_window to prevent the method from returning until the dialog has been destroyed.

That being said, you shouldn't be using it like in your example. You need to have the mainloop running before you create the window. It might work OK on some platforms, but in general you shouldn't expect your GUI to behave properly until mainloop is running.

Here's a simple example:

import Tkinter as tk

class MyDialog(object):
    def __init__(self, parent, prompt):
        self.toplevel = tk.Toplevel(parent)
        self.var = tk.StringVar()
        label = tk.Label(self.toplevel, text=prompt)
        entry = tk.Entry(self.toplevel, width=40, textvariable=self.var)
        button = tk.Button(self.toplevel, text="OK", command=self.toplevel.destroy)

        label.pack(side="top", fill="x")
        entry.pack(side="top", fill="x")
        button.pack(side="bottom", anchor="e", padx=4, pady=4)

    def show(self):
        self.toplevel.grab_set()
        self.toplevel.wait_window()
        value = self.var.get()
        return value

class Example(tk.Frame):
    def __init__(self, parent):
        tk.Frame.__init__(self, parent)

        self.button = tk.Button(self, text="Click me!", command=self.on_click)
        self.label = tk.Label(self, text="", width=40)
        self.label.pack(side="top", fill="x")
        self.button.pack(padx=20, pady=20)

    def on_click(self):
        result = MyDialog(self, "Enter your name:").show()
        self.label.configure(text="your result: '%s'" % result)

if __name__ == "__main__":
    root = tk.Tk()
    Example(root).pack(fill="both", expand=True)
    root.mainloop()
Bryan Oakley
  • 370,779
  • 53
  • 539
  • 685
  • Excellent. I only wish this was already part of `tkinter`, sort of like `tkinter.messagebox`. But this way, it is more customizable. – mcu Nov 08 '15 at 17:40
  • @coding4fun: this type of dialog is part of tkinter. It's called [askstring](http://effbot.org/tkinterbook/tkinter-entry-dialogs.htm). – Bryan Oakley Nov 08 '15 at 18:39