1#
2# An Introduction to Tkinter
3# tkSimpleDialog.py
4#
5# Copyright (c) 1997 by Fredrik Lundh
6#
7# fredrik@pythonware.com
8# http://www.pythonware.com
9#
10
11# --------------------------------------------------------------------
12# dialog base class
13
14'''Dialog boxes
15
16This module handles dialog boxes. It contains the following
17public symbols:
18
19Dialog -- a base class for dialogs
20
21askinteger -- get an integer from the user
22
23askfloat -- get a float from the user
24
25askstring -- get a string from the user
26'''
27
28from Tkinter import *
29
30class Dialog(Toplevel):
31
32    '''Class to open dialogs.
33
34    This class is intended as a base class for custom dialogs
35    '''
36
37    def __init__(self, parent, title = None):
38
39        '''Initialize a dialog.
40
41        Arguments:
42
43            parent -- a parent window (the application window)
44
45            title -- the dialog title
46        '''
47        Toplevel.__init__(self, parent)
48
49        self.withdraw() # remain invisible for now
50        # If the master is not viewable, don't
51        # make the child transient, or else it
52        # would be opened withdrawn
53        if parent.winfo_viewable():
54            self.transient(parent)
55
56        if title:
57            self.title(title)
58
59        self.parent = parent
60
61        self.result = None
62
63        body = Frame(self)
64        self.initial_focus = self.body(body)
65        body.pack(padx=5, pady=5)
66
67        self.buttonbox()
68
69
70        if not self.initial_focus:
71            self.initial_focus = self
72
73        self.protocol("WM_DELETE_WINDOW", self.cancel)
74
75        if self.parent is not None:
76            self.geometry("+%d+%d" % (parent.winfo_rootx()+50,
77                                      parent.winfo_rooty()+50))
78
79        self.deiconify() # become visibile now
80
81        self.initial_focus.focus_set()
82
83        # wait for window to appear on screen before calling grab_set
84        self.wait_visibility()
85        self.grab_set()
86        self.wait_window(self)
87
88    def destroy(self):
89        '''Destroy the window'''
90        self.initial_focus = None
91        Toplevel.destroy(self)
92
93    #
94    # construction hooks
95
96    def body(self, master):
97        '''create dialog body.
98
99        return widget that should have initial focus.
100        This method should be overridden, and is called
101        by the __init__ method.
102        '''
103        pass
104
105    def buttonbox(self):
106        '''add standard button box.
107
108        override if you do not want the standard buttons
109        '''
110
111        box = Frame(self)
112
113        w = Button(box, text="OK", width=10, command=self.ok, default=ACTIVE)
114        w.pack(side=LEFT, padx=5, pady=5)
115        w = Button(box, text="Cancel", width=10, command=self.cancel)
116        w.pack(side=LEFT, padx=5, pady=5)
117
118        self.bind("<Return>", self.ok)
119        self.bind("<Escape>", self.cancel)
120
121        box.pack()
122
123    #
124    # standard button semantics
125
126    def ok(self, event=None):
127
128        if not self.validate():
129            self.initial_focus.focus_set() # put focus back
130            return
131
132        self.withdraw()
133        self.update_idletasks()
134
135        try:
136            self.apply()
137        finally:
138            self.cancel()
139
140    def cancel(self, event=None):
141
142        # put focus back to the parent window
143        if self.parent is not None:
144            self.parent.focus_set()
145        self.destroy()
146
147    #
148    # command hooks
149
150    def validate(self):
151        '''validate the data
152
153        This method is called automatically to validate the data before the
154        dialog is destroyed. By default, it always validates OK.
155        '''
156
157        return 1 # override
158
159    def apply(self):
160        '''process the data
161
162        This method is called automatically to process the data, *after*
163        the dialog is destroyed. By default, it does nothing.
164        '''
165
166        pass # override
167
168
169# --------------------------------------------------------------------
170# convenience dialogues
171
172class _QueryDialog(Dialog):
173
174    def __init__(self, title, prompt,
175                 initialvalue=None,
176                 minvalue = None, maxvalue = None,
177                 parent = None):
178
179        if not parent:
180            import Tkinter
181            parent = Tkinter._default_root
182
183        self.prompt   = prompt
184        self.minvalue = minvalue
185        self.maxvalue = maxvalue
186
187        self.initialvalue = initialvalue
188
189        Dialog.__init__(self, parent, title)
190
191    def destroy(self):
192        self.entry = None
193        Dialog.destroy(self)
194
195    def body(self, master):
196
197        w = Label(master, text=self.prompt, justify=LEFT)
198        w.grid(row=0, padx=5, sticky=W)
199
200        self.entry = Entry(master, name="entry")
201        self.entry.grid(row=1, padx=5, sticky=W+E)
202
203        if self.initialvalue is not None:
204            self.entry.insert(0, self.initialvalue)
205            self.entry.select_range(0, END)
206
207        return self.entry
208
209    def validate(self):
210
211        import tkMessageBox
212
213        try:
214            result = self.getresult()
215        except ValueError:
216            tkMessageBox.showwarning(
217                "Illegal value",
218                self.errormessage + "\nPlease try again",
219                parent = self
220            )
221            return 0
222
223        if self.minvalue is not None and result < self.minvalue:
224            tkMessageBox.showwarning(
225                "Too small",
226                "The allowed minimum value is %s. "
227                "Please try again." % self.minvalue,
228                parent = self
229            )
230            return 0
231
232        if self.maxvalue is not None and result > self.maxvalue:
233            tkMessageBox.showwarning(
234                "Too large",
235                "The allowed maximum value is %s. "
236                "Please try again." % self.maxvalue,
237                parent = self
238            )
239            return 0
240
241        self.result = result
242
243        return 1
244
245
246class _QueryInteger(_QueryDialog):
247    errormessage = "Not an integer."
248    def getresult(self):
249        return int(self.entry.get())
250
251def askinteger(title, prompt, **kw):
252    '''get an integer from the user
253
254    Arguments:
255
256        title -- the dialog title
257        prompt -- the label text
258        **kw -- see SimpleDialog class
259
260    Return value is an integer
261    '''
262    d = _QueryInteger(title, prompt, **kw)
263    return d.result
264
265class _QueryFloat(_QueryDialog):
266    errormessage = "Not a floating point value."
267    def getresult(self):
268        return float(self.entry.get())
269
270def askfloat(title, prompt, **kw):
271    '''get a float from the user
272
273    Arguments:
274
275        title -- the dialog title
276        prompt -- the label text
277        **kw -- see SimpleDialog class
278
279    Return value is a float
280    '''
281    d = _QueryFloat(title, prompt, **kw)
282    return d.result
283
284class _QueryString(_QueryDialog):
285    def __init__(self, *args, **kw):
286        if "show" in kw:
287            self.__show = kw["show"]
288            del kw["show"]
289        else:
290            self.__show = None
291        _QueryDialog.__init__(self, *args, **kw)
292
293    def body(self, master):
294        entry = _QueryDialog.body(self, master)
295        if self.__show is not None:
296            entry.configure(show=self.__show)
297        return entry
298
299    def getresult(self):
300        return self.entry.get()
301
302def askstring(title, prompt, **kw):
303    '''get a string from the user
304
305    Arguments:
306
307        title -- the dialog title
308        prompt -- the label text
309        **kw -- see SimpleDialog class
310
311    Return value is a string
312    '''
313    d = _QueryString(title, prompt, **kw)
314    return d.result
315
316if __name__ == "__main__":
317
318    root = Tk()
319    root.update()
320
321    print askinteger("Spam", "Egg count", initialvalue=12*12)
322    print askfloat("Spam", "Egg weight\n(in tons)", minvalue=1, maxvalue=100)
323    print askstring("Spam", "Egg label")
324