summaryrefslogtreecommitdiff
path: root/uitools/TkDlgWrapper.py
blob: 54b223686571d64c870759022e925c29b966d975 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
#!/usr/bin/env python
######################################################################
# Try to access some of the newer Tk dialogs.
# This module is inspired by the Python 1.[34] Dialog.py module.
#
# Mitch Chapman
#---------------------------------------------------------------------
# $Log: TkDlgWrapper.py,v $
# Revision 1.1  1996/12/01 22:58:54  mchapman
# Initial revision
#
######################################################################

__version__ = "$Revision: 1.1 $"

import Tkinter; Tk=Tkinter
import os, copy

VersionError = "VersionError"

if Tk.TkVersion < 4.2:
    raise VersionError, "This module requires Tk 4.2 or greater."

######################################################################
# This is an abstract wrapper for all classes of built-in Tk dialogs.
# It's subclassed from Tk.Widget to gain access to widget
# configuration methods.
######################################################################
class T(Tk.Widget):
    ##################################################################
    # Initialize a new instance.
    ##################################################################
    def __init__(self, widgetName, master=None, **kw):
	self.widgetName = "__%s__" % widgetName
	# The unadorned widgetName is the Tk command/proc which
	# displays the dialog, e.g. tk_chooseColor
	self.tkCommand = widgetName
	self.master = master
	# Defer the actual widget initialization.  I want instances
	# of T to persist even when they are not visible. For
	# example, this allows a file selection dialog to remember
	# what file extension it last selected.  But in order to allow
	# persistence, the creation of the underlying Tk dialog must
	# be delayed.
	
	# Make a copy of the configuration, so it can persist.  This
	# is where self remembers things such as the last file type
	# selected, last color used, etc.
	self.kw = copy.copy(kw)

    ##################################################################
    # Show the dialog.
    ##################################################################
    def show(self, **kw):
	# Use the configuration options to override the current config --
	# and remember them for later.
	for k, v in kw.items():
	    self.kw[k] = v
	Tk.Widget._setup(self, self.master, self.kw)
	resultStr = apply(self.tk.call, (self.tkCommand,) +
			  self._options(self.kw))
	try:
	    # Don't leak Python widget descriptors?
	    Tk.Widget.destroy(self)
	except Tk.TclError: pass
	
        return resultStr

    ##################################################################
    # Explicitly destroy a widget instance.  Nothing to do, here,
    # because the widget is gone as soon as show() returns.
    ##################################################################
    def destroy(self):
	pass


######################################################################
# Abstract wrapper for the Tk 4.2 tk_get\(Open\|Save\)File dialog.
# Tries to remember the last-used directory, so it can be used as
# the initial directory next time the dialog is displayed.
######################################################################
class OpenSaveFile(T):
    ##################################################################
    # Initialize a new instance.  whichDlg tells whether this is
    # a tk_getOpenFile or a tk_getSaveFile, etc.
    ##################################################################
    def __init__(self, whichDlg, master=None, **kw):
	apply(T.__init__, (self, whichDlg, master), kw)
	self.filename = None

    ##################################################################
    # Show the dialog and return either the selected file or "".
    ##################################################################
    def show(self, **kw):
	self.filename = apply(T.show, (self,), kw)
	# Try to remember a few configuration items for the next time
	# the dialog is displayed.
	if self.filename:
	    dirname, basename = os.path.split(self.filename)

	    # Next time, start in the directory we ended in this time.
	    self.kw['initialdir'] = dirname
	    
	    # Try to figure out what extension to use next time.
	    ext = os.path.splitext(basename)[1]
	    self.kw['defaultextension'] = "%s" % ext

	    # Try to specify the default name to use next time.
	    self.kw['initialfile'] = basename

	    # The Tk dialogs are a little stupid: Even if you specify
	    # an initial file with extension ".txt", the dialog will show
	    # only files matching the first set of extensions in the
	    # -filetypes option.
	    # My cheap workaround is to create a new first entry for
	    # -filetypes which matches the extension selected this
	    # time.
	    oldtypes = ()
	    if self.kw.has_key('filetypes'):
		oldtypes = self.kw['filetypes']
	    # The following works only for Unix (and maybe Windows).
	    # Somebody wanna fix this for handling Mac file types?
	    newtypes = ()
	    dfltExt = ext or "*"
	    dfltDescription = "(Last Selected)"
	    for typespec in oldtypes:
		description, types = typespec
		if description == dfltDescription:
		    newtypes = ((dfltDescription, dfltExt),) + oldtypes[1:]
		    break  # EXIT FOR LOOP
	    else:
		newtypes = ((dfltDescription, dfltExt),) + oldtypes

	    self.kw['filetypes'] = newtypes

	    # The Tk dialogs are a little smart: If you create both an
	    # open and a save dialog in the same application, they seem
	    # to both slave to the same working directory.  Change to dir
	    # "/blah" when opening?  Then when you try to save, the
	    # default displayed directory will be "/blah".
	    # This may be a bug, given what the man pages say, but
	    # I like it.
	return self.filename

######################################################################
# Wrapper for the tk_getOpenFile dialog.
######################################################################
class OpenFile(OpenSaveFile):
    def __init__(self, master=None, **kw):
	apply(OpenSaveFile.__init__, (self, "tk_getOpenFile", master), kw)


######################################################################
# Wrapper for the tk_getSaveFile dialog.
######################################################################
class SaveFile(OpenSaveFile):
    def __init__(self, master=None, **kw):
	apply(OpenSaveFile.__init__, (self, "tk_getSaveFile", master), kw)


######################################################################
# Wrapper for the tk_chooseColor dialog.
######################################################################
class ChooseColor(T):
    ##################################################################
    # Initialize a new instance.
    ##################################################################
    def __init__(self, master=None, **kw):
	apply(T.__init__, (self, "tk_chooseColor", master), kw)


######################################################################
# Wrapper for the tk_messageBox dialog.
######################################################################
class MessageBox(T):
    ##################################################################
    # Initialize a new instance.
    ##################################################################
    def __init__(self, master=None, defaultCfg=None, **kw):
	cfg = Tk._cnfmerge((defaultCfg or {}, kw))
	apply(T.__init__, (self, "tk_messageBox", master), cfg)


######################################################################
# These are the specific types of MessageBox dialogs provided by Tk.
# For each of these classes, the return value of show() is a string
# containing the label of the button which was pressed.
#
# Yeah, I know, these aren't particularly useful when you can roll
# your own much more beautiful dialogs in Python.  But hey!  They're
# here and they work, and they're supposed to have native look-n-feel
# on every supported platform.
######################################################################

class AbortRetryIgnore(MessageBox):
    def __init__(self, master=None, **kw):
	apply(MessageBox.__init__,
	      (self, master, {'icon':'error', 'type':'abortretryignore'}), kw)

class OK(MessageBox):
    def __init__(self, master=None, **kw):
	apply(MessageBox.__init__, (self, master, {'type':'ok'}), kw)

class OKCancel(MessageBox):
    def __init__(self, master=None, **kw):
	apply(MessageBox.__init__, (self, master, {'type':'okcancel'}), kw)

class RetryCancel(MessageBox):
    def __init__(self, master=None, **kw):
	apply(MessageBox.__init__,
	      (self, master, {'icon':'error', 'type':'retrycancel'}), kw)

class YesNo(MessageBox):
    def __init__(self, master=None, **kw):
	apply(MessageBox.__init__,
	      (self, master, {'icon':'question', 'type':'yesno'}), kw)

class YesNoCancel(MessageBox):
    def __init__(self, master=None, **kw):
	apply(MessageBox.__init__,
	      (self, master, {'icon':'question', 'type':'yesnocancel'}), kw)


######################################################################
# Don't wrap tk_dialog -- that's already done in Dialog.py.
######################################################################

######################################################################
# Main function for unit testing.
######################################################################
def main():
    # Demo all of the message box dialog classes:
    for c in [AbortRetryIgnore, OK, OKCancel,
	      RetryCancel, YesNo, YesNoCancel]:
	d = c(title=c.__name__)
	print d.show(message="This is an instance of %s." % c.__name__)

    # Demo the color chooser dialog:
    d = ChooseColor(title="PickaKuhla", initialcolor="SlateGray")
    color = d.show()
    print "Selected color is", `color`

    # Demo the open file dialog.
    d = OpenFile(defaultextension=".py",
		    filetypes=(("Python", ".py"),
			       ("Emacs Lisp", ".el"),
			       ("Text", ".txt"),
			       ("All Files", "*")))
    
    # Run multiple times to demonstrate how the dialog remembers the
    # last working directory, file extension, etc.
    for i in range(2):
	filename = d.show()
	print "Open file", `d.filename`

    print 72 * "_"
    print "Current working directory is", os.getcwd()
    d = SaveFile(defaultextension=".py",
		 filetypes=(("Pithon [er, Python]", ".py"),
			    ("Text", ".txt"),
			    ("All Files", "*")))
    for i in range(2):
	filename = d.show()
	print "Save File As", `d.filename`


if __name__ == "__main__":
    main()