# # An Introduction to Tkinter # # Copyright (c) 1997 by Fredrik Lundh # # This copyright applies to Dialog, askinteger, askfloat and asktring # # fredrik@pythonware.com # http://www.pythonware.com # """This modules handles dialog boxes. It contains the following public symbols: SimpleDialog -- A simple but flexible modal dialog box Dialog -- a base class for dialogs askinteger -- get an integer from the user askfloat -- get a float from the user askstring -- get a string from the user """ from tkinter import * from tkinter import messagebox import tkinter # used at _QueryDialog for tkinter._default_root class SimpleDialog: def __init__(self, master, text='', buttons=[], default=None, cancel=None, title=None, class_=None): if class_: self.root = Toplevel(master, class_=class_) else: self.root = Toplevel(master) if title: self.root.title(title) self.root.iconname(title) self.message = Message(self.root, text=text, aspect=400) self.message.pack(expand=1, fill=BOTH) self.frame = Frame(self.root) self.frame.pack() self.num = default self.cancel = cancel self.default = default self.root.bind('<Return>', self.return_event) for num in range(len(buttons)): s = buttons[num] b = Button(self.frame, text=s, command=(lambda self=self, num=num: self.done(num))) if num == default: b.config(relief=RIDGE, borderwidth=8) b.pack(side=LEFT, fill=BOTH, expand=1) self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window) self._set_transient(master) def _set_transient(self, master, relx=0.5, rely=0.3): widget = self.root widget.withdraw() # Remain invisible while we figure out the geometry widget.transient(master) widget.update_idletasks() # Actualize geometry information if master.winfo_ismapped(): m_width = master.winfo_width() m_height = master.winfo_height() m_x = master.winfo_rootx() m_y = master.winfo_rooty() else: m_width = master.winfo_screenwidth() m_height = master.winfo_screenheight() m_x = m_y = 0 w_width = widget.winfo_reqwidth() w_height = widget.winfo_reqheight() x = m_x + (m_width - w_width) * relx y = m_y + (m_height - w_height) * rely if x+w_width > master.winfo_screenwidth(): x = master.winfo_screenwidth() - w_width elif x < 0: x = 0 if y+w_height > master.winfo_screenheight(): y = master.winfo_screenheight() - w_height elif y < 0: y = 0 widget.geometry("+%d+%d" % (x, y)) widget.deiconify() # Become visible at the desired location def go(self): self.root.wait_visibility() self.root.grab_set() self.root.mainloop() self.root.destroy() return self.num def return_event(self, event): if self.default is None: self.root.bell() else: self.done(self.default) def wm_delete_window(self): if self.cancel is None: self.root.bell() else: self.done(self.cancel) def done(self, num): self.num = num self.root.quit() class Dialog(Toplevel): '''Class to open dialogs. This class is intended as a base class for custom dialogs ''' def __init__(self, parent, title = None): '''Initialize a dialog. Arguments: parent -- a parent window (the application window) title -- the dialog title ''' Toplevel.__init__(self, parent) self.withdraw() # remain invisible for now # If the master is not viewable, don't # make the child transient, or else it # would be opened withdrawn if parent.winfo_viewable(): self.transient(parent) if title: self.title(title) self.parent = parent self.result = None body = Frame(self) self.initial_focus = self.body(body) body.pack(padx=5, pady=5) self.buttonbox() if not self.initial_focus: self.initial_focus = self self.protocol("WM_DELETE_WINDOW", self.cancel) if self.parent is not None: self.geometry("+%d+%d" % (parent.winfo_rootx()+50, parent.winfo_rooty()+50)) self.deiconify() # become visible now self.initial_focus.focus_set() # wait for window to appear on screen before calling grab_set self.wait_visibility() self.grab_set() self.wait_window(self) def destroy(self): '''Destroy the window''' self.initial_focus = None Toplevel.destroy(self) # # construction hooks def body(self, master): '''create dialog body. return widget that should have initial focus. This method should be overridden, and is called by the __init__ method. ''' pass def buttonbox(self): '''add standard button box. override if you do not want the standard buttons ''' box = Frame(self) w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE) w.pack(side=LEFT, padx=5, pady=5) w = Button(box, text="Cancel", width=10, command=self.cancel) w.pack(side=LEFT, padx=5, pady=5) self.bind("<Return>", self.ok) self.bind("<Escape>", self.cancel) box.pack() # # standard button semantics def ok(self, event=None): if not self.validate(): self.initial_focus.focus_set() # put focus back return self.withdraw() self.update_idletasks() try: self.apply() finally: self.cancel() def cancel(self, event=None): # put focus back to the parent window if self.parent is not None: self.parent.focus_set() self.destroy() # # command hooks def validate(self): '''validate the data This method is called automatically to validate the data before the dialog is destroyed. By default, it always validates OK. ''' return 1 # override def apply(self): '''process the data This method is called automatically to process the data, *after* the dialog is destroyed. By default, it does nothing. ''' pass # override # -------------------------------------------------------------------- # convenience dialogues class _QueryDialog(Dialog): def __init__(self, title, prompt, initialvalue=None, minvalue = None, maxvalue = None, parent = None): if not parent: parent = tkinter._default_root self.prompt = prompt self.minvalue = minvalue self.maxvalue = maxvalue self.initialvalue = initialvalue Dialog.__init__(self, parent, title) def destroy(self): self.entry = None Dialog.destroy(self) def body(self, master): w = Label(master, text=self.prompt, justify=LEFT) w.grid(row=0, padx=5, sticky=W) self.entry = Entry(master, name="entry") self.entry.grid(row=1, padx=5, sticky=W+E) if self.initialvalue: self.entry.insert(0, self.initialvalue) self.entry.select_range(0, END) return self.entry def validate(self): try: result = self.getresult() except ValueError: messagebox.showwarning( "Illegal value", self.errormessage + "\nPlease try again", parent = self ) return 0 if self.minvalue is not None and result < self.minvalue: messagebox.showwarning( "Too small", "The allowed minimum value is %s. " "Please try again." % self.minvalue, parent = self ) return 0 if self.maxvalue is not None and result > self.maxvalue: messagebox.showwarning( "Too large", "The allowed maximum value is %s. " "Please try again." % self.maxvalue, parent = self ) return 0 self.result = result return 1 class _QueryInteger(_QueryDialog): errormessage = "Not an integer." def getresult(self): return int(self.entry.get()) def askinteger(title, prompt, **kw): '''get an integer from the user Arguments: title -- the dialog title prompt -- the label text **kw -- see SimpleDialog class Return value is an integer ''' d = _QueryInteger(title, prompt, **kw) return d.result class _QueryFloat(_QueryDialog): errormessage = "Not a floating point value." def getresult(self): return float(self.entry.get()) def askfloat(title, prompt, **kw): '''get a float from the user Arguments: title -- the dialog title prompt -- the label text **kw -- see SimpleDialog class Return value is a float ''' d = _QueryFloat(title, prompt, **kw) return d.result class _QueryString(_QueryDialog): def __init__(self, *args, **kw): if "show" in kw: self.__show = kw["show"] del kw["show"] else: self.__show = None _QueryDialog.__init__(self, *args, **kw) def body(self, master): entry = _QueryDialog.body(self, master) if self.__show is not None: entry.configure(show=self.__show) return entry def getresult(self): return self.entry.get() def askstring(title, prompt, **kw): '''get a string from the user Arguments: title -- the dialog title prompt -- the label text **kw -- see SimpleDialog class Return value is a string ''' d = _QueryString(title, prompt, **kw) return d.result if __name__ == '__main__': def test(): root = Tk() def doit(root=root): d = SimpleDialog(root, text="This is a test dialog. " "Would this have been an actual dialog, " "the buttons below would have been glowing " "in soft pink light.\n" "Do you believe this?", buttons=["Yes", "No", "Cancel"], default=0, cancel=2, title="Test Dialog") print(d.go()) print(askinteger("Spam", "Egg count", initialvalue=12*12)) print(askfloat("Spam", "Egg weight\n(in tons)", minvalue=1, maxvalue=100)) print(askstring("Spam", "Egg label")) t = Button(root, text='Test', command=doit) t.pack() q = Button(root, text='Quit', command=t.quit) q.pack() t.mainloop() test()