move sections
[python/dscho.git] / Demo / tkinter / guido / AttrDialog.py
blob86333adc7d1b5434de31e1fc60a9c200c49c5e3b
2 # The options of a widget are described by the following attributes
3 # of the Pack and Widget dialogs:
5 # Dialog.current: {name: value}
6 # -- changes during Widget's lifetime
8 # Dialog.options: {name: (default, klass)}
9 # -- depends on widget class only
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)
15 from Tkinter import *
17 class Option:
19 varclass = StringVar # May be overridden
21 def __init__(self, dialog, option):
22 self.dialog = dialog
23 self.option = option
24 self.master = dialog.top
25 self.default, self.klass = dialog.options[option]
26 self.var = self.varclass(self.master)
27 self.frame = Frame(self.master)
28 self.frame.pack(fill=X)
29 self.label = Label(self.frame, text=(option + ":"))
30 self.label.pack(side=LEFT)
31 self.update()
32 self.addoption()
34 def refresh(self):
35 self.dialog.refresh()
36 self.update()
38 def update(self):
39 try:
40 self.current = self.dialog.current[self.option]
41 except KeyError:
42 self.current = self.default
43 self.var.set(self.current)
45 def set(self, e=None): # Should be overridden
46 pass
48 class BooleanOption(Option):
50 varclass = BooleanVar
52 def addoption(self):
53 self.button = Checkbutton(self.frame,
54 text='on/off',
55 onvalue=1,
56 offvalue=0,
57 variable=self.var,
58 relief=RAISED,
59 borderwidth=2,
60 command=self.set)
61 self.button.pack(side=RIGHT)
63 class EnumOption(Option):
65 def addoption(self):
66 self.button = Menubutton(self.frame,
67 textvariable=self.var,
68 relief=RAISED, borderwidth=2)
69 self.button.pack(side=RIGHT)
70 self.menu = Menu(self.button)
71 self.button['menu'] = self.menu
72 for v in self.dialog.classes[self.klass]:
73 self.menu.add_radiobutton(
74 label=v,
75 variable=self.var,
76 value=v,
77 command=self.set)
79 class StringOption(Option):
81 def addoption(self):
82 self.entry = Entry(self.frame,
83 textvariable=self.var,
84 width=10,
85 relief=SUNKEN,
86 borderwidth=2)
87 self.entry.pack(side=RIGHT, fill=X, expand=1)
88 self.entry.bind('<Return>', self.set)
90 class ReadonlyOption(Option):
92 def addoption(self):
93 self.label = Label(self.frame, textvariable=self.var,
94 anchor=E)
95 self.label.pack(side=RIGHT)
97 class Dialog:
99 def __init__(self, master):
100 self.master = master
101 self.fixclasses()
102 self.refresh()
103 self.top = Toplevel(self.master)
104 self.top.title(self.__class__.__name__)
105 self.top.minsize(1, 1)
106 self.addchoices()
108 def refresh(self): pass # Must override
110 def fixclasses(self): pass # May override
112 def addchoices(self):
113 self.choices = {}
114 list = []
115 for k, dc in self.options.items():
116 list.append((k, dc))
117 list.sort()
118 for k, (d, c) in list:
119 try:
120 cl = self.classes[c]
121 except KeyError:
122 cl = 'unknown'
123 if type(cl) == TupleType:
124 cl = self.enumoption
125 elif cl == 'boolean':
126 cl = self.booleanoption
127 elif cl == 'readonly':
128 cl = self.readonlyoption
129 else:
130 cl = self.stringoption
131 self.choices[k] = cl(self, k)
133 # Must override:
134 options = {}
135 classes = {}
137 # May override:
138 booleanoption = BooleanOption
139 stringoption = StringOption
140 enumoption = EnumOption
141 readonlyoption = ReadonlyOption
143 class PackDialog(Dialog):
145 def __init__(self, widget):
146 self.widget = widget
147 Dialog.__init__(self, widget)
149 def refresh(self):
150 self.current = self.widget.info()
151 self.current['.class'] = self.widget.winfo_class()
152 self.current['.name'] = self.widget._w
154 class packoption: # Mix-in class
155 def set(self, e=None):
156 self.current = self.var.get()
157 try:
158 apply(self.dialog.widget.pack, (),
159 {self.option: self.current})
160 except TclError, msg:
161 print msg
162 self.refresh()
164 class booleanoption(packoption, BooleanOption): pass
165 class enumoption(packoption, EnumOption): pass
166 class stringoption(packoption, StringOption): pass
167 class readonlyoption(packoption, ReadonlyOption): pass
169 options = {
170 '.class': (None, 'Class'),
171 '.name': (None, 'Name'),
172 'after': (None, 'Widget'),
173 'anchor': ('center', 'Anchor'),
174 'before': (None, 'Widget'),
175 'expand': ('no', 'Boolean'),
176 'fill': ('none', 'Fill'),
177 'in': (None, 'Widget'),
178 'ipadx': (0, 'Pad'),
179 'ipady': (0, 'Pad'),
180 'padx': (0, 'Pad'),
181 'pady': (0, 'Pad'),
182 'side': ('top', 'Side'),
185 classes = {
186 'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER),
187 'Boolean': 'boolean',
188 'Class': 'readonly',
189 'Expand': 'boolean',
190 'Fill': (NONE, X, Y, BOTH),
191 'Name': 'readonly',
192 'Pad': 'pixel',
193 'Side': (TOP, RIGHT, BOTTOM, LEFT),
194 'Widget': 'readonly',
197 class RemotePackDialog(PackDialog):
199 def __init__(self, master, app, widget):
200 self.master = master
201 self.app = app
202 self.widget = widget
203 self.refresh()
204 self.top = Toplevel(self.master)
205 self.top.title(self.app + ' PackDialog')
206 self.top.minsize(1, 1)
207 self.addchoices()
209 def refresh(self):
210 try:
211 words = self.master.tk.splitlist(
212 self.master.send(self.app,
213 'pack',
214 'info',
215 self.widget))
216 except TclError, msg:
217 print msg
218 return
219 dict = {}
220 for i in range(0, len(words), 2):
221 key = words[i][1:]
222 value = words[i+1]
223 dict[key] = value
224 dict['.class'] = self.master.send(self.app,
225 'winfo',
226 'class',
227 self.widget)
228 dict['.name'] = self.widget
229 self.current = dict
231 class remotepackoption: # Mix-in class
232 def set(self, e=None):
233 self.current = self.var.get()
234 try:
235 self.dialog.master.send(
236 self.dialog.app,
237 'pack',
238 'config',
239 self.dialog.widget,
240 '-'+self.option,
241 self.dialog.master.tk.merge(
242 self.current))
243 except TclError, msg:
244 print msg
245 self.refresh()
247 class booleanoption(remotepackoption, BooleanOption): pass
248 class enumoption(remotepackoption, EnumOption): pass
249 class stringoption(remotepackoption, StringOption): pass
250 class readonlyoption(remotepackoption, ReadonlyOption): pass
252 class WidgetDialog(Dialog):
254 def __init__(self, widget):
255 self.widget = widget
256 self.klass = widget.winfo_class()
257 Dialog.__init__(self, widget)
259 def fixclasses(self):
260 if self.addclasses.has_key(self.klass):
261 classes = {}
262 for c in (self.classes,
263 self.addclasses[self.klass]):
264 for k in c.keys():
265 classes[k] = c[k]
266 self.classes = classes
268 def refresh(self):
269 self.configuration = self.widget.config()
270 self.update()
271 self.current['.class'] = self.widget.winfo_class()
272 self.current['.name'] = self.widget._w
274 def update(self):
275 self.current = {}
276 self.options = {}
277 for k, v in self.configuration.items():
278 if len(v) > 4:
279 self.current[k] = v[4]
280 self.options[k] = v[3], v[2] # default, klass
281 self.options['.class'] = (None, 'Class')
282 self.options['.name'] = (None, 'Name')
284 class widgetoption: # Mix-in class
285 def set(self, e=None):
286 self.current = self.var.get()
287 try:
288 self.dialog.widget[self.option] = self.current
289 except TclError, msg:
290 print msg
291 self.refresh()
293 class booleanoption(widgetoption, BooleanOption): pass
294 class enumoption(widgetoption, EnumOption): pass
295 class stringoption(widgetoption, StringOption): pass
296 class readonlyoption(widgetoption, ReadonlyOption): pass
298 # Universal classes
299 classes = {
300 'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER),
301 'Aspect': 'integer',
302 'Background': 'color',
303 'Bitmap': 'bitmap',
304 'BorderWidth': 'pixel',
305 'Class': 'readonly',
306 'CloseEnough': 'double',
307 'Command': 'command',
308 'Confine': 'boolean',
309 'Cursor': 'cursor',
310 'CursorWidth': 'pixel',
311 'DisabledForeground': 'color',
312 'ExportSelection': 'boolean',
313 'Font': 'font',
314 'Foreground': 'color',
315 'From': 'integer',
316 'Geometry': 'geometry',
317 'Height': 'pixel',
318 'InsertWidth': 'time',
319 'Justify': (LEFT, CENTER, RIGHT),
320 'Label': 'string',
321 'Length': 'pixel',
322 'MenuName': 'widget',
323 'Name': 'readonly',
324 'OffTime': 'time',
325 'OnTime': 'time',
326 'Orient': (HORIZONTAL, VERTICAL),
327 'Pad': 'pixel',
328 'Relief': (RAISED, SUNKEN, FLAT, RIDGE, GROOVE),
329 'RepeatDelay': 'time',
330 'RepeatInterval': 'time',
331 'ScrollCommand': 'command',
332 'ScrollIncrement': 'pixel',
333 'ScrollRegion': 'rectangle',
334 'ShowValue': 'boolean',
335 'SetGrid': 'boolean',
336 'Sliderforeground': 'color',
337 'SliderLength': 'pixel',
338 'Text': 'string',
339 'TickInterval': 'integer',
340 'To': 'integer',
341 'Underline': 'index',
342 'Variable': 'variable',
343 'Value': 'string',
344 'Width': 'pixel',
345 'Wrap': (NONE, CHAR, WORD),
348 # Classes that (may) differ per widget type
349 _tristate = {'State': (NORMAL, ACTIVE, DISABLED)}
350 _bistate = {'State': (NORMAL, DISABLED)}
351 addclasses = {
352 'Button': _tristate,
353 'Radiobutton': _tristate,
354 'Checkbutton': _tristate,
355 'Entry': _bistate,
356 'Text': _bistate,
357 'Menubutton': _tristate,
358 'Slider': _bistate,
361 class RemoteWidgetDialog(WidgetDialog):
363 def __init__(self, master, app, widget):
364 self.app = app
365 self.widget = widget
366 self.klass = master.send(self.app,
367 'winfo',
368 'class',
369 self.widget)
370 Dialog.__init__(self, master)
372 def refresh(self):
373 try:
374 items = self.master.tk.splitlist(
375 self.master.send(self.app,
376 self.widget,
377 'config'))
378 except TclError, msg:
379 print msg
380 return
381 dict = {}
382 for item in items:
383 words = self.master.tk.splitlist(item)
384 key = words[0][1:]
385 value = (key,) + words[1:]
386 dict[key] = value
387 self.configuration = dict
388 self.update()
389 self.current['.class'] = self.klass
390 self.current['.name'] = self.widget
392 class remotewidgetoption: # Mix-in class
393 def set(self, e=None):
394 self.current = self.var.get()
395 try:
396 self.dialog.master.send(
397 self.dialog.app,
398 self.dialog.widget,
399 'config',
400 '-'+self.option,
401 self.current)
402 except TclError, msg:
403 print msg
404 self.refresh()
406 class booleanoption(remotewidgetoption, BooleanOption): pass
407 class enumoption(remotewidgetoption, EnumOption): pass
408 class stringoption(remotewidgetoption, StringOption): pass
409 class readonlyoption(remotewidgetoption, ReadonlyOption): pass
411 def test():
412 import sys
413 root = Tk()
414 root.minsize(1, 1)
415 if sys.argv[1:]:
416 remotetest(root, sys.argv[1])
417 else:
418 frame = Frame(root, name='frame')
419 frame.pack(expand=1, fill=BOTH)
420 button = Button(frame, name='button', text='button')
421 button.pack(expand=1)
422 canvas = Canvas(frame, name='canvas')
423 canvas.pack()
424 fpd = PackDialog(frame)
425 fwd = WidgetDialog(frame)
426 bpd = PackDialog(button)
427 bwd = WidgetDialog(button)
428 cpd = PackDialog(canvas)
429 cwd = WidgetDialog(canvas)
430 root.mainloop()
432 def remotetest(root, app):
433 from listtree import listtree
434 list = listtree(root, app)
435 list.bind('<Any-Double-1>', opendialogs)
436 list.app = app # Pass it on to handler
438 def opendialogs(e):
439 import string
440 list = e.widget
441 sel = list.curselection()
442 for i in sel:
443 item = list.get(i)
444 widget = string.split(item)[0]
445 RemoteWidgetDialog(list, list.app, widget)
446 if widget == '.': continue
447 try:
448 RemotePackDialog(list, list.app, widget)
449 except TclError, msg:
450 print msg
452 test()