codekingpro/portable-devtools
114k
1#2# An Introduction to Tkinter3#4# Copyright (c) 1997 by Fredrik Lundh5#6# This copyright applies to Dialog, askinteger, askfloat and asktring7#8# fredrik@pythonware.com9# http://www.pythonware.com10#11"""This modules handles dialog boxes.12 13It contains the following public symbols:14 15SimpleDialog -- A simple but flexible modal dialog box16 17Dialog -- a base class for dialogs18 19askinteger -- get an integer from the user20 21askfloat -- get a float from the user22 23askstring -- get a string from the user24"""25 26from tkinter import *27from tkinter import _get_temp_root, _destroy_temp_root28from tkinter import messagebox29 30 31class SimpleDialog:32 33 def __init__(self, master,34 text='', buttons=[], default=None, cancel=None,35 title=None, class_=None):36 if class_:37 self.root = Toplevel(master, class_=class_)38 else:39 self.root = Toplevel(master)40 if title:41 self.root.title(title)42 self.root.iconname(title)43 44 _setup_dialog(self.root)45 46 self.message = Message(self.root, text=text, aspect=400)47 self.message.pack(expand=1, fill=BOTH)48 self.frame = Frame(self.root)49 self.frame.pack()50 self.num = default51 self.cancel = cancel52 self.default = default53 self.root.bind('<Return>', self.return_event)54 for num in range(len(buttons)):55 s = buttons[num]56 b = Button(self.frame, text=s,57 command=(lambda self=self, num=num: self.done(num)))58 if num == default:59 b.config(relief=RIDGE, borderwidth=8)60 b.pack(side=LEFT, fill=BOTH, expand=1)61 self.root.protocol('WM_DELETE_WINDOW', self.wm_delete_window)62 self.root.transient(master)63 _place_window(self.root, master)64 65 def go(self):66 self.root.wait_visibility()67 self.root.grab_set()68 self.root.mainloop()69 self.root.destroy()70 return self.num71 72 def return_event(self, event):73 if self.default is None:74 self.root.bell()75 else:76 self.done(self.default)77 78 def wm_delete_window(self):79 if self.cancel is None:80 self.root.bell()81 else:82 self.done(self.cancel)83 84 def done(self, num):85 self.num = num86 self.root.quit()87 88 89class Dialog(Toplevel):90 91 '''Class to open dialogs.92 93 This class is intended as a base class for custom dialogs94 '''95 96 def __init__(self, parent, title = None):97 '''Initialize a dialog.98 99 Arguments:100 101 parent -- a parent window (the application window)102 103 title -- the dialog title104 '''105 master = parent106 if master is None:107 master = _get_temp_root()108 109 Toplevel.__init__(self, master)110 111 self.withdraw() # remain invisible for now112 # If the parent is not viewable, don't113 # make the child transient, or else it114 # would be opened withdrawn115 if parent is not None and parent.winfo_viewable():116 self.transient(parent)117 118 if title:119 self.title(title)120 121 _setup_dialog(self)122 123 self.parent = parent124 125 self.result = None126 127 body = Frame(self)128 self.initial_focus = self.body(body)129 body.pack(padx=5, pady=5)130 131 self.buttonbox()132 133 if self.initial_focus is None:134 self.initial_focus = self135 136 self.protocol("WM_DELETE_WINDOW", self.cancel)137 138 _place_window(self, parent)139 140 self.initial_focus.focus_set()141 142 # wait for window to appear on screen before calling grab_set143 self.wait_visibility()144 self.grab_set()145 self.wait_window(self)146 147 def destroy(self):148 '''Destroy the window'''149 self.initial_focus = None150 Toplevel.destroy(self)151 _destroy_temp_root(self.master)152 153 #154 # construction hooks155 156 def body(self, master):157 '''create dialog body.158 159 return widget that should have initial focus.160 This method should be overridden, and is called161 by the __init__ method.162 '''163 pass164 165 def buttonbox(self):166 '''add standard button box.167 168 override if you do not want the standard buttons169 '''170 171 box = Frame(self)172 173 w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)174 w.pack(side=LEFT, padx=5, pady=5)175 w = Button(box, text="Cancel", width=10, command=self.cancel)176 w.pack(side=LEFT, padx=5, pady=5)177 178 self.bind("<Return>", self.ok)179 self.bind("<Escape>", self.cancel)180 181 box.pack()182 183 #184 # standard button semantics185 186 def ok(self, event=None):187 188 if not self.validate():189 self.initial_focus.focus_set() # put focus back190 return191 192 self.withdraw()193 self.update_idletasks()194 195 try:196 self.apply()197 finally:198 self.cancel()199 200 def cancel(self, event=None):201 202 # put focus back to the parent window203 if self.parent is not None:204 self.parent.focus_set()205 self.destroy()206 207 #208 # command hooks209 210 def validate(self):211 '''validate the data212 213 This method is called automatically to validate the data before the214 dialog is destroyed. By default, it always validates OK.215 '''216 217 return 1 # override218 219 def apply(self):220 '''process the data221 222 This method is called automatically to process the data, *after*223 the dialog is destroyed. By default, it does nothing.224 '''225 226 pass # override227 228 229# Place a toplevel window at the center of parent or screen230# It is a Python implementation of ::tk::PlaceWindow.231def _place_window(w, parent=None):232 w.wm_withdraw() # Remain invisible while we figure out the geometry233 w.update_idletasks() # Actualize geometry information234 235 minwidth = w.winfo_reqwidth()236 minheight = w.winfo_reqheight()237 maxwidth = w.winfo_vrootwidth()238 maxheight = w.winfo_vrootheight()239 if parent is not None and parent.winfo_ismapped():240 x = parent.winfo_rootx() + (parent.winfo_width() - minwidth) // 2241 y = parent.winfo_rooty() + (parent.winfo_height() - minheight) // 2242 vrootx = w.winfo_vrootx()243 vrooty = w.winfo_vrooty()244 x = min(x, vrootx + maxwidth - minwidth)245 x = max(x, vrootx)246 y = min(y, vrooty + maxheight - minheight)247 y = max(y, vrooty)248 if w._windowingsystem == 'aqua':249 # Avoid the native menu bar which sits on top of everything.250 y = max(y, 22)251 else:252 x = (w.winfo_screenwidth() - minwidth) // 2253 y = (w.winfo_screenheight() - minheight) // 2254 255 w.wm_maxsize(maxwidth, maxheight)256 w.wm_geometry('+%d+%d' % (x, y))257 w.wm_deiconify() # Become visible at the desired location258 259 260def _setup_dialog(w):261 if w._windowingsystem == "aqua":262 w.tk.call("::tk::unsupported::MacWindowStyle", "style",263 w, "moveableModal", "")264 elif w._windowingsystem == "x11":265 w.wm_attributes(type="dialog")266 267# --------------------------------------------------------------------268# convenience dialogues269 270class _QueryDialog(Dialog):271 272 def __init__(self, title, prompt,273 initialvalue=None,274 minvalue = None, maxvalue = None,275 parent = None):276 277 self.prompt = prompt278 self.minvalue = minvalue279 self.maxvalue = maxvalue280 281 self.initialvalue = initialvalue282 283 Dialog.__init__(self, parent, title)284 285 def destroy(self):286 self.entry = None287 Dialog.destroy(self)288 289 def body(self, master):290 291 w = Label(master, text=self.prompt, justify=LEFT)292 w.grid(row=0, padx=5, sticky=W)293 294 self.entry = Entry(master, name="entry")295 self.entry.grid(row=1, padx=5, sticky=W+E)296 297 if self.initialvalue is not None:298 self.entry.insert(0, self.initialvalue)299 self.entry.select_range(0, END)300 301 return self.entry302 303 def validate(self):304 try:305 result = self.getresult()306 except ValueError:307 messagebox.showwarning(308 "Illegal value",309 self.errormessage + "\nPlease try again",310 parent = self311 )312 return 0313 314 if self.minvalue is not None and result < self.minvalue:315 messagebox.showwarning(316 "Too small",317 "The allowed minimum value is %s. "318 "Please try again." % self.minvalue,319 parent = self320 )321 return 0322 323 if self.maxvalue is not None and result > self.maxvalue:324 messagebox.showwarning(325 "Too large",326 "The allowed maximum value is %s. "327 "Please try again." % self.maxvalue,328 parent = self329 )330 return 0331 332 self.result = result333 334 return 1335 336 337class _QueryInteger(_QueryDialog):338 errormessage = "Not an integer."339 340 def getresult(self):341 return self.getint(self.entry.get())342 343 344def askinteger(title, prompt, **kw):345 '''get an integer from the user346 347 Arguments:348 349 title -- the dialog title350 prompt -- the label text351 **kw -- see SimpleDialog class352 353 Return value is an integer354 '''355 d = _QueryInteger(title, prompt, **kw)356 return d.result357 358 359class _QueryFloat(_QueryDialog):360 errormessage = "Not a floating-point value."361 362 def getresult(self):363 return self.getdouble(self.entry.get())364 365 366def askfloat(title, prompt, **kw):367 '''get a float from the user368 369 Arguments:370 371 title -- the dialog title372 prompt -- the label text373 **kw -- see SimpleDialog class374 375 Return value is a float376 '''377 d = _QueryFloat(title, prompt, **kw)378 return d.result379 380 381class _QueryString(_QueryDialog):382 def __init__(self, *args, **kw):383 if "show" in kw:384 self.__show = kw["show"]385 del kw["show"]386 else:387 self.__show = None388 _QueryDialog.__init__(self, *args, **kw)389 390 def body(self, master):391 entry = _QueryDialog.body(self, master)392 if self.__show is not None:393 entry.configure(show=self.__show)394 return entry395 396 def getresult(self):397 return self.entry.get()398 399 400def askstring(title, prompt, **kw):401 '''get a string from the user402 403 Arguments:404 405 title -- the dialog title406 prompt -- the label text407 **kw -- see SimpleDialog class408 409 Return value is a string410 '''411 d = _QueryString(title, prompt, **kw)412 return d.result413 414 415if __name__ == '__main__':416 417 def test():418 root = Tk()419 def doit(root=root):420 d = SimpleDialog(root,421 text="This is a test dialog. "422 "Would this have been an actual dialog, "423 "the buttons below would have been glowing "424 "in soft pink light.\n"425 "Do you believe this?",426 buttons=["Yes", "No", "Cancel"],427 default=0,428 cancel=2,429 title="Test Dialog")430 print(d.go())431 print(askinteger("Spam", "Egg count", initialvalue=12*12))432 print(askfloat("Spam", "Egg weight\n(in tons)", minvalue=1,433 maxvalue=100))434 print(askstring("Spam", "Egg label"))435 t = Button(root, text='Test', command=doit)436 t.pack()437 q = Button(root, text='Quit', command=t.quit)438 q.pack()439 t.mainloop()440 441 test()442 