blob: d9ad82a32cd51e47669dec154d7350f9fa063706 [file] [log] [blame]
Guido van Rossum3e395be1994-07-12 08:58:25 +00001
2# The options of a widget are described by the following attributes
3# of the Pack and Widget dialogs:
4#
5# Dialog.current: {name: value}
6# -- changes during Widget's lifetime
7#
8# Dialog.options: {name: (default, klass)}
9# -- depends on widget class only
10#
11# Dialog.classes: {klass: (v0, v1, v2, ...) | 'boolean' | 'other'}
12# -- totally static, though different between PackDialog and WidgetDialog
13# (but even that could be unified)
14
15
16from Tkinter import *
17
18class Option:
19
Guido van Rossumb78e03c1994-07-12 15:53:26 +000020 varclass = StringVar # May be overridden
21
22 def __init__(self, dialog, option):
23 self.dialog = dialog
Guido van Rossum3e395be1994-07-12 08:58:25 +000024 self.option = option
Guido van Rossumb78e03c1994-07-12 15:53:26 +000025 self.master = dialog.top
26 self.default, self.klass = dialog.options[option]
27 self.var = self.varclass(self.master)
Guido van Rossum3e395be1994-07-12 08:58:25 +000028 self.frame = Frame(self.master,
29 {Pack: {'expand': 0, 'fill': 'x'}})
30 self.label = Label(self.frame,
31 {'text': option + ':',
32 Pack: {'side': 'left'},
33 })
34 self.update()
Guido van Rossumb78e03c1994-07-12 15:53:26 +000035 self.addoption()
Guido van Rossum3e395be1994-07-12 08:58:25 +000036
37 def refresh(self):
Guido van Rossumb78e03c1994-07-12 15:53:26 +000038 self.dialog.refresh()
Guido van Rossum3e395be1994-07-12 08:58:25 +000039 self.update()
40
41 def update(self):
42 try:
Guido van Rossumb78e03c1994-07-12 15:53:26 +000043 self.current = self.dialog.current[self.option]
Guido van Rossum3e395be1994-07-12 08:58:25 +000044 except KeyError:
45 self.current = self.default
46 self.var.set(self.current)
47
Guido van Rossumb78e03c1994-07-12 15:53:26 +000048 def set(self, e=None): # Should be overridden
Guido van Rossum3e395be1994-07-12 08:58:25 +000049 pass
50
51class BooleanOption(Option):
52
Guido van Rossumb78e03c1994-07-12 15:53:26 +000053 varclass = BooleanVar
54
55 def addoption(self):
Guido van Rossum3e395be1994-07-12 08:58:25 +000056 self.button = Checkbutton(self.frame,
57 {'text': 'on/off',
58 'onvalue': '1',
59 'offvalue': '0',
60 'variable': self.var,
61 'relief': 'raised',
62 'borderwidth': 2,
63 'command': self.set,
64 Pack: {'side': 'right'},
65 })
66
67class EnumOption(Option):
68
Guido van Rossumb78e03c1994-07-12 15:53:26 +000069 def addoption(self):
Guido van Rossum3e395be1994-07-12 08:58:25 +000070 self.button = Menubutton(self.frame,
71 {'textvariable': self.var,
72 'relief': 'raised',
73 'borderwidth': 2,
74 Pack: {'side': 'right'},
75 })
76 self.menu = Menu(self.button)
77 self.button['menu'] = self.menu
Guido van Rossumb78e03c1994-07-12 15:53:26 +000078 for v in self.dialog.classes[self.klass]:
Guido van Rossum3e395be1994-07-12 08:58:25 +000079 self.menu.add_radiobutton(
Guido van Rossumb78e03c1994-07-12 15:53:26 +000080 {'label': v,
Guido van Rossum3e395be1994-07-12 08:58:25 +000081 'variable': self.var,
82 'value': v,
83 'command': self.set,
84 })
85
86class StringOption(Option):
87
Guido van Rossumb78e03c1994-07-12 15:53:26 +000088 def addoption(self):
Guido van Rossum3e395be1994-07-12 08:58:25 +000089 self.entry = Entry(self.frame,
90 {'textvariable': self.var,
91 'width': 10,
92 'relief': 'sunken',
93 'borderwidth': 2,
94 Pack: {'side': 'right',
95 'fill': 'x', 'expand': 1},
96 })
97 self.entry.bind('<Return>', self.set)
98
Guido van Rossumb78e03c1994-07-12 15:53:26 +000099class ReadonlyOption(Option):
Guido van Rossum3e395be1994-07-12 08:58:25 +0000100
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000101 def addoption(self):
102 self.label = Label(self.frame,
103 {'textvariable': self.var,
104 Pack: {'side': 'right'}})
Guido van Rossum3e395be1994-07-12 08:58:25 +0000105
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000106class Dialog:
Guido van Rossum3e395be1994-07-12 08:58:25 +0000107
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000108 def __init__(self, master):
109 self.master = master
110 self.refresh()
111 self.top = Toplevel(self.master)
112 self.top.title(self.__class__.__name__)
113 self.top.minsize(1, 1)
114 self.addchoices()
115
116 def addchoices(self):
117 self.choices = {}
118 list = []
119 for k, dc in self.options.items():
120 list.append(k, dc)
121 list.sort()
122 for k, (d, c) in list:
123 try:
124 cl = self.classes[c]
125 except KeyError:
126 cl = 'unknown'
127 if type(cl) == TupleType:
128 cl = self.enumoption
129 elif cl == 'boolean':
130 cl = self.booleanoption
131 elif cl == 'readonly':
132 cl = self.readonlyoption
133 else:
134 cl = self.stringoption
135 self.choices[k] = cl(self, k)
136
137 booleanoption = BooleanOption
138 stringoption = StringOption
139 enumoption = EnumOption
140 readonlyoption = ReadonlyOption
141
142class PackDialog(Dialog):
143
144 def __init__(self, widget):
145 self.widget = widget
146 Dialog.__init__(self, widget)
147
148 def refresh(self):
149 self.current = self.widget.newinfo()
150 self.current['.class'] = self.widget.winfo_class()
151
152 class packoption: # Mix-in class
153 def set(self, e=None):
154 self.current = self.var.get()
155 try:
156 Pack.config(self.dialog.widget,
157 {self.option: self.current})
158 except TclError:
159 self.refresh()
160
161 class booleanoption(packoption, BooleanOption): pass
162 class enumoption(packoption, EnumOption): pass
163 class stringoption(packoption, StringOption): pass
164 class readonlyoption(packoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000165
166 options = {
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000167 '.class': (None, 'Class'),
Guido van Rossum3e395be1994-07-12 08:58:25 +0000168 'after': (None, 'Widet'),
169 'anchor': ('center', 'Anchor'),
170 'before': (None, 'Widget'),
171 'expand': ('no', 'Boolean'),
172 'fill': ('none', 'Fill'),
173 'in': (None, 'Widget'),
174 'ipadx': (0, 'Pad'),
175 'ipady': (0, 'Pad'),
176 'padx': (0, 'Pad'),
177 'pady': (0, 'Pad'),
178 'side': ('top', 'Side'),
179 }
180
181 classes = {
182 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'),
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000183 'Boolean': 'boolean',
184 'Class': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000185 'Expand': 'boolean',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000186 'Fill': ('none', 'x', 'y', 'both'),
Guido van Rossum3e395be1994-07-12 08:58:25 +0000187 'Pad': 'pixel',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000188 'Side': ('top', 'right', 'bottom', 'left'),
189 'Widget': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000190 }
191
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000192class RemotePackDialog(PackDialog):
193
194 def __init__(self, master, app, widget):
195 self.master = master
196 self.app = app
197 self.widget = widget
198 self.refresh()
199 self.top = Toplevel(self.master)
200 self.top.title('Remote %s Pack: %s' % (self.app, self.widget))
201 self.top.minsize(1, 1) # XXX
202 self.addchoices()
203
204 def refresh(self):
205 try:
206 words = self.master.tk.splitlist(
207 self.master.send(self.app,
208 'pack',
209 'newinfo',
210 self.widget))
211 except TclError, msg:
212 print 'send pack newinfo', self.widget, ':', msg
213 return
214 dict = {}
215 for i in range(0, len(words), 2):
216 key = words[i][1:]
217 value = words[i+1]
218 dict[key] = value
219 dict['.class'] = self.master.send(self.app,
220 'winfo',
221 'class',
222 self.widget)
223 self.current = dict
224
225 class remotepackoption: # Mix-in class
226 def set(self, e=None):
227 self.current = self.var.get()
228 try:
229 self.dialog.master.send(
230 self.dialog.app,
231 'pack', 'config', self.dialog.widget,
232 '-'+self.option, self.current)
233 except TclError, msg:
234 print 'send pack config ... :', msg
235 self.refresh()
236
237 class booleanoption(remotepackoption, BooleanOption): pass
238 class enumoption(remotepackoption, EnumOption): pass
239 class stringoption(remotepackoption, StringOption): pass
240 class readonlyoption(remotepackoption, ReadonlyOption): pass
241
242class WidgetDialog(Dialog):
243
Guido van Rossum3e395be1994-07-12 08:58:25 +0000244 def __init__(self, widget):
245 self.widget = widget
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000246 if self.addclasses.has_key(self.widget.widgetName):
247 classes = {}
248 for c in (self.classes,
249 self.addclasses[self.widget.widgetName]):
250 for k in c.keys():
251 classes[k] = c[k]
252 self.classes = classes
253 Dialog.__init__(self, widget)
Guido van Rossum3e395be1994-07-12 08:58:25 +0000254
255 def refresh(self):
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000256 self.configuration = self.widget.config()
257 self.current = {}
258 self.options = {}
Guido van Rossum28bb6521994-07-12 15:55:43 +0000259 self.options['.class'] = (None, 'Class')
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000260 self.current['.class'] = self.widget.winfo_class()
261 for k, v in self.configuration.items():
262 if len(v) > 4:
263 self.current[k] = v[4]
264 self.options[k] = v[3], v[2] # default, klass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000265
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000266 class widgetoption: # Mix-in class
267 def set(self, e=None):
268 self.current = self.var.get()
269 try:
270 self.dialog.widget[self.option] = self.current
271 except TclError:
272 self.refresh()
Guido van Rossum3e395be1994-07-12 08:58:25 +0000273
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000274 class booleanoption(widgetoption, BooleanOption): pass
275 class enumoption(widgetoption, EnumOption): pass
276 class stringoption(widgetoption, StringOption): pass
277 class readonlyoption(widgetoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000278
279 # Universal classes
280 classes = {
281 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'),
282 'Aspect': 'integer',
283 'Background': 'color',
284 'Bitmap': 'bitmap',
285 'BorderWidth': 'pixel',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000286 'Class': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000287 'CloseEnough': 'double',
288 'Command': 'command',
289 'Confine': 'boolean',
290 'Cursor': 'cursor',
291 'CursorWidth': 'pixel',
292 'DisabledForeground': 'color',
293 'ExportSelection': 'boolean',
294 'Font': 'font',
295 'Foreground': 'color',
296 'From': 'integer',
297 'Geometry': 'geometry',
298 'Height': 'pixel',
299 'InsertWidth': 'time',
300 'Justify': ('left', 'center', 'right'),
301 'Label': 'string',
302 'Length': 'pixel',
303 'MenuName': 'widget',
304 'OffTime': 'time',
305 'OnTime': 'time',
306 'Orient': ('horizontal', 'vertical'),
307 'Pad': 'pixel',
308 'Relief': ('raised', 'sunken', 'flat', 'ridge', 'groove'),
309 'RepeatDelay': 'time',
310 'RepeatInterval': 'time',
311 'ScrollCommand': 'command',
312 'ScrollIncrement': 'pixel',
313 'ScrollRegion': 'rectangle',
314 'ShowValue': 'boolean',
315 'SetGrid': 'boolean',
316 'Sliderforeground': 'color',
317 'SliderLength': 'pixel',
318 'Text': 'string',
319 'TickInterval': 'integer',
320 'To': 'integer',
321 'Underline': 'index',
322 'Variable': 'variable',
323 'Value': 'string',
324 'Width': 'pixel',
325 'Wrap': ('none', 'char', 'word'),
326 }
327
328 # Classes that (may) differ per widget type
329 _tristate = {'State': ('normal', 'active', 'disabled')}
330 _bistate = {'State': ('normal', 'disabled')}
331 addclasses = {
332 'button': _tristate,
333 'radiobutton': _tristate,
334 'checkbutton': _tristate,
335 'entry': _bistate,
336 'text': _bistate,
337 'menubutton': _tristate,
338 'slider': _bistate,
339 }
340
341
Guido van Rossum3e395be1994-07-12 08:58:25 +0000342def test():
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000343 import sys
Guido van Rossum3e395be1994-07-12 08:58:25 +0000344 root = Tk()
345 root.minsize(1, 1)
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000346 if sys.argv[2:]:
347 pd = RemotePackDialog(root, sys.argv[1], sys.argv[2])
348 else:
349 frame = Frame(root, {Pack: {'expand': 1, 'fill': 'both'}})
350 button = Button(frame, {'text': 'button',
351 Pack: {'expand': 1}})
352 canvas = Canvas(frame, {Pack: {}})
353 bpd = PackDialog(button)
354 bwd = WidgetDialog(button)
355 cpd = PackDialog(canvas)
356 cwd = WidgetDialog(canvas)
Guido van Rossum3e395be1994-07-12 08:58:25 +0000357 root.mainloop()
358
359test()