blob: 2b501c70b603e7413075009540e137eb880d724f [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
Guido van Rossum3e395be1994-07-12 08:58:25 +000015from Tkinter import *
16
17class Option:
18
Guido van Rossumb78e03c1994-07-12 15:53:26 +000019 varclass = StringVar # May be overridden
20
21 def __init__(self, dialog, option):
22 self.dialog = dialog
Guido van Rossum3e395be1994-07-12 08:58:25 +000023 self.option = option
Guido van Rossumb78e03c1994-07-12 15:53:26 +000024 self.master = dialog.top
25 self.default, self.klass = dialog.options[option]
26 self.var = self.varclass(self.master)
Guido van Rossum3e395be1994-07-12 08:58:25 +000027 self.frame = Frame(self.master,
28 {Pack: {'expand': 0, 'fill': 'x'}})
29 self.label = Label(self.frame,
30 {'text': option + ':',
31 Pack: {'side': 'left'},
32 })
33 self.update()
Guido van Rossumb78e03c1994-07-12 15:53:26 +000034 self.addoption()
Guido van Rossum3e395be1994-07-12 08:58:25 +000035
36 def refresh(self):
Guido van Rossumb78e03c1994-07-12 15:53:26 +000037 self.dialog.refresh()
Guido van Rossum3e395be1994-07-12 08:58:25 +000038 self.update()
39
40 def update(self):
41 try:
Guido van Rossumb78e03c1994-07-12 15:53:26 +000042 self.current = self.dialog.current[self.option]
Guido van Rossum3e395be1994-07-12 08:58:25 +000043 except KeyError:
44 self.current = self.default
45 self.var.set(self.current)
46
Guido van Rossumb78e03c1994-07-12 15:53:26 +000047 def set(self, e=None): # Should be overridden
Guido van Rossum3e395be1994-07-12 08:58:25 +000048 pass
49
50class BooleanOption(Option):
51
Guido van Rossumb78e03c1994-07-12 15:53:26 +000052 varclass = BooleanVar
53
54 def addoption(self):
Guido van Rossum3e395be1994-07-12 08:58:25 +000055 self.button = Checkbutton(self.frame,
56 {'text': 'on/off',
57 'onvalue': '1',
58 'offvalue': '0',
59 'variable': self.var,
60 'relief': 'raised',
61 'borderwidth': 2,
62 'command': self.set,
63 Pack: {'side': 'right'},
64 })
65
66class EnumOption(Option):
67
Guido van Rossumb78e03c1994-07-12 15:53:26 +000068 def addoption(self):
Guido van Rossum3e395be1994-07-12 08:58:25 +000069 self.button = Menubutton(self.frame,
70 {'textvariable': self.var,
71 'relief': 'raised',
72 'borderwidth': 2,
73 Pack: {'side': 'right'},
74 })
75 self.menu = Menu(self.button)
76 self.button['menu'] = self.menu
Guido van Rossumb78e03c1994-07-12 15:53:26 +000077 for v in self.dialog.classes[self.klass]:
Guido van Rossum3e395be1994-07-12 08:58:25 +000078 self.menu.add_radiobutton(
Guido van Rossumb78e03c1994-07-12 15:53:26 +000079 {'label': v,
Guido van Rossum3e395be1994-07-12 08:58:25 +000080 'variable': self.var,
81 'value': v,
82 'command': self.set,
83 })
84
85class StringOption(Option):
86
Guido van Rossumb78e03c1994-07-12 15:53:26 +000087 def addoption(self):
Guido van Rossum3e395be1994-07-12 08:58:25 +000088 self.entry = Entry(self.frame,
89 {'textvariable': self.var,
90 'width': 10,
91 'relief': 'sunken',
92 'borderwidth': 2,
93 Pack: {'side': 'right',
94 'fill': 'x', 'expand': 1},
95 })
96 self.entry.bind('<Return>', self.set)
97
Guido van Rossumb78e03c1994-07-12 15:53:26 +000098class ReadonlyOption(Option):
Guido van Rossum3e395be1994-07-12 08:58:25 +000099
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000100 def addoption(self):
101 self.label = Label(self.frame,
102 {'textvariable': self.var,
Guido van Rossum3d0df461994-08-03 08:10:35 +0000103 'anchor': 'e',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000104 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
Guido van Rossum3d0df461994-08-03 08:10:35 +0000110 self.fixclasses()
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000111 self.refresh()
112 self.top = Toplevel(self.master)
113 self.top.title(self.__class__.__name__)
114 self.top.minsize(1, 1)
115 self.addchoices()
116
Guido van Rossum3d0df461994-08-03 08:10:35 +0000117 def refresh(self): pass # Must override
118
119 def fixclasses(self): pass # May override
120
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000121 def addchoices(self):
122 self.choices = {}
123 list = []
124 for k, dc in self.options.items():
125 list.append(k, dc)
126 list.sort()
127 for k, (d, c) in list:
128 try:
129 cl = self.classes[c]
130 except KeyError:
131 cl = 'unknown'
132 if type(cl) == TupleType:
133 cl = self.enumoption
134 elif cl == 'boolean':
135 cl = self.booleanoption
136 elif cl == 'readonly':
137 cl = self.readonlyoption
138 else:
139 cl = self.stringoption
140 self.choices[k] = cl(self, k)
141
Guido van Rossum3d0df461994-08-03 08:10:35 +0000142 # Must override:
143 options = {}
144 classes = {}
145
146 # May override:
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000147 booleanoption = BooleanOption
148 stringoption = StringOption
149 enumoption = EnumOption
150 readonlyoption = ReadonlyOption
151
152class PackDialog(Dialog):
153
154 def __init__(self, widget):
155 self.widget = widget
156 Dialog.__init__(self, widget)
157
158 def refresh(self):
159 self.current = self.widget.newinfo()
160 self.current['.class'] = self.widget.winfo_class()
Guido van Rossum50f437c1994-07-12 16:37:21 +0000161 self.current['.name'] = self.widget._w
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000162
163 class packoption: # Mix-in class
164 def set(self, e=None):
165 self.current = self.var.get()
166 try:
167 Pack.config(self.dialog.widget,
168 {self.option: self.current})
Guido van Rossum3d0df461994-08-03 08:10:35 +0000169 except TclError, msg:
170 print msg
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000171 self.refresh()
172
173 class booleanoption(packoption, BooleanOption): pass
174 class enumoption(packoption, EnumOption): pass
175 class stringoption(packoption, StringOption): pass
176 class readonlyoption(packoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000177
178 options = {
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000179 '.class': (None, 'Class'),
Guido van Rossum50f437c1994-07-12 16:37:21 +0000180 '.name': (None, 'Name'),
181 'after': (None, 'Widget'),
Guido van Rossum3e395be1994-07-12 08:58:25 +0000182 'anchor': ('center', 'Anchor'),
183 'before': (None, 'Widget'),
184 'expand': ('no', 'Boolean'),
185 'fill': ('none', 'Fill'),
186 'in': (None, 'Widget'),
187 'ipadx': (0, 'Pad'),
188 'ipady': (0, 'Pad'),
189 'padx': (0, 'Pad'),
190 'pady': (0, 'Pad'),
191 'side': ('top', 'Side'),
192 }
193
194 classes = {
195 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'),
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000196 'Boolean': 'boolean',
197 'Class': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000198 'Expand': 'boolean',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000199 'Fill': ('none', 'x', 'y', 'both'),
Guido van Rossum50f437c1994-07-12 16:37:21 +0000200 'Name': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000201 'Pad': 'pixel',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000202 'Side': ('top', 'right', 'bottom', 'left'),
203 'Widget': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000204 }
205
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000206class RemotePackDialog(PackDialog):
207
208 def __init__(self, master, app, widget):
209 self.master = master
210 self.app = app
211 self.widget = widget
212 self.refresh()
213 self.top = Toplevel(self.master)
Guido van Rossum50f437c1994-07-12 16:37:21 +0000214 self.top.title(self.app + ' PackDialog')
215 self.top.minsize(1, 1)
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000216 self.addchoices()
217
218 def refresh(self):
219 try:
220 words = self.master.tk.splitlist(
221 self.master.send(self.app,
222 'pack',
223 'newinfo',
224 self.widget))
225 except TclError, msg:
Guido van Rossum3d0df461994-08-03 08:10:35 +0000226 print msg
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000227 return
228 dict = {}
229 for i in range(0, len(words), 2):
230 key = words[i][1:]
231 value = words[i+1]
232 dict[key] = value
233 dict['.class'] = self.master.send(self.app,
234 'winfo',
235 'class',
236 self.widget)
Guido van Rossum50f437c1994-07-12 16:37:21 +0000237 dict['.name'] = self.widget
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000238 self.current = dict
239
240 class remotepackoption: # Mix-in class
241 def set(self, e=None):
242 self.current = self.var.get()
243 try:
244 self.dialog.master.send(
245 self.dialog.app,
Guido van Rossum3d0df461994-08-03 08:10:35 +0000246 'pack',
247 'config',
248 self.dialog.widget,
249 '-'+self.option,
250 self.dialog.master.tk.merge(
251 self.current))
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000252 except TclError, msg:
Guido van Rossum3d0df461994-08-03 08:10:35 +0000253 print msg
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000254 self.refresh()
255
256 class booleanoption(remotepackoption, BooleanOption): pass
257 class enumoption(remotepackoption, EnumOption): pass
258 class stringoption(remotepackoption, StringOption): pass
259 class readonlyoption(remotepackoption, ReadonlyOption): pass
260
261class WidgetDialog(Dialog):
262
Guido van Rossum3e395be1994-07-12 08:58:25 +0000263 def __init__(self, widget):
264 self.widget = widget
Guido van Rossum3d0df461994-08-03 08:10:35 +0000265 self.klass = widget.winfo_class()
266 Dialog.__init__(self, widget)
267
268 def fixclasses(self):
269 if self.addclasses.has_key(self.klass):
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000270 classes = {}
271 for c in (self.classes,
Guido van Rossum3d0df461994-08-03 08:10:35 +0000272 self.addclasses[self.klass]):
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000273 for k in c.keys():
274 classes[k] = c[k]
275 self.classes = classes
Guido van Rossum3e395be1994-07-12 08:58:25 +0000276
277 def refresh(self):
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000278 self.configuration = self.widget.config()
Guido van Rossum50f437c1994-07-12 16:37:21 +0000279 self.update()
280 self.current['.class'] = self.widget.winfo_class()
281 self.current['.name'] = self.widget._w
282
283 def update(self):
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000284 self.current = {}
285 self.options = {}
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000286 for k, v in self.configuration.items():
287 if len(v) > 4:
288 self.current[k] = v[4]
289 self.options[k] = v[3], v[2] # default, klass
Guido van Rossum50f437c1994-07-12 16:37:21 +0000290 self.options['.class'] = (None, 'Class')
291 self.options['.name'] = (None, 'Name')
Guido van Rossum3e395be1994-07-12 08:58:25 +0000292
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000293 class widgetoption: # Mix-in class
294 def set(self, e=None):
295 self.current = self.var.get()
296 try:
297 self.dialog.widget[self.option] = self.current
Guido van Rossum3d0df461994-08-03 08:10:35 +0000298 except TclError, msg:
299 print msg
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000300 self.refresh()
Guido van Rossum3e395be1994-07-12 08:58:25 +0000301
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000302 class booleanoption(widgetoption, BooleanOption): pass
303 class enumoption(widgetoption, EnumOption): pass
304 class stringoption(widgetoption, StringOption): pass
305 class readonlyoption(widgetoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000306
307 # Universal classes
308 classes = {
309 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'),
310 'Aspect': 'integer',
311 'Background': 'color',
312 'Bitmap': 'bitmap',
313 'BorderWidth': 'pixel',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000314 'Class': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000315 'CloseEnough': 'double',
316 'Command': 'command',
317 'Confine': 'boolean',
318 'Cursor': 'cursor',
319 'CursorWidth': 'pixel',
320 'DisabledForeground': 'color',
321 'ExportSelection': 'boolean',
322 'Font': 'font',
323 'Foreground': 'color',
324 'From': 'integer',
325 'Geometry': 'geometry',
326 'Height': 'pixel',
327 'InsertWidth': 'time',
328 'Justify': ('left', 'center', 'right'),
329 'Label': 'string',
330 'Length': 'pixel',
331 'MenuName': 'widget',
Guido van Rossum50f437c1994-07-12 16:37:21 +0000332 'Name': 'readonly',
Guido van Rossum3e395be1994-07-12 08:58:25 +0000333 'OffTime': 'time',
334 'OnTime': 'time',
335 'Orient': ('horizontal', 'vertical'),
336 'Pad': 'pixel',
337 'Relief': ('raised', 'sunken', 'flat', 'ridge', 'groove'),
338 'RepeatDelay': 'time',
339 'RepeatInterval': 'time',
340 'ScrollCommand': 'command',
341 'ScrollIncrement': 'pixel',
342 'ScrollRegion': 'rectangle',
343 'ShowValue': 'boolean',
344 'SetGrid': 'boolean',
345 'Sliderforeground': 'color',
346 'SliderLength': 'pixel',
347 'Text': 'string',
348 'TickInterval': 'integer',
349 'To': 'integer',
350 'Underline': 'index',
351 'Variable': 'variable',
352 'Value': 'string',
353 'Width': 'pixel',
354 'Wrap': ('none', 'char', 'word'),
355 }
356
357 # Classes that (may) differ per widget type
358 _tristate = {'State': ('normal', 'active', 'disabled')}
359 _bistate = {'State': ('normal', 'disabled')}
360 addclasses = {
Guido van Rossum3d0df461994-08-03 08:10:35 +0000361 'Button': _tristate,
362 'Radiobutton': _tristate,
363 'Checkbutton': _tristate,
364 'Entry': _bistate,
365 'Text': _bistate,
366 'Menubutton': _tristate,
367 'Slider': _bistate,
Guido van Rossum3e395be1994-07-12 08:58:25 +0000368 }
Guido van Rossum50f437c1994-07-12 16:37:21 +0000369
370class RemoteWidgetDialog(WidgetDialog):
371
372 def __init__(self, master, app, widget):
Guido van Rossum50f437c1994-07-12 16:37:21 +0000373 self.app = app
374 self.widget = widget
Guido van Rossum3d0df461994-08-03 08:10:35 +0000375 self.klass = master.send(self.app,
376 'winfo',
377 'class',
378 self.widget)
379 Dialog.__init__(self, master)
Guido van Rossum50f437c1994-07-12 16:37:21 +0000380
381 def refresh(self):
382 try:
383 items = self.master.tk.splitlist(
384 self.master.send(self.app,
385 self.widget,
386 'config'))
387 except TclError, msg:
Guido van Rossum3d0df461994-08-03 08:10:35 +0000388 print msg
Guido van Rossum50f437c1994-07-12 16:37:21 +0000389 return
390 dict = {}
391 for item in items:
392 words = self.master.tk.splitlist(item)
393 key = words[0][1:]
394 value = (key,) + words[1:]
395 dict[key] = value
396 self.configuration = dict
397 self.update()
Guido van Rossum3d0df461994-08-03 08:10:35 +0000398 self.current['.class'] = self.klass
Guido van Rossum50f437c1994-07-12 16:37:21 +0000399 self.current['.name'] = self.widget
400
401 class remotewidgetoption: # Mix-in class
402 def set(self, e=None):
403 self.current = self.var.get()
404 try:
405 self.dialog.master.send(
406 self.dialog.app,
407 self.dialog.widget,
408 'config',
409 '-'+self.option,
410 self.current)
411 except TclError, msg:
Guido van Rossum3d0df461994-08-03 08:10:35 +0000412 print msg
Guido van Rossum50f437c1994-07-12 16:37:21 +0000413 self.refresh()
414
415 class booleanoption(remotewidgetoption, BooleanOption): pass
416 class enumoption(remotewidgetoption, EnumOption): pass
417 class stringoption(remotewidgetoption, StringOption): pass
418 class readonlyoption(remotewidgetoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000419
Guido van Rossum3e395be1994-07-12 08:58:25 +0000420def test():
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000421 import sys
Guido van Rossum3e395be1994-07-12 08:58:25 +0000422 root = Tk()
423 root.minsize(1, 1)
Guido van Rossume7e8d1e1994-07-13 12:56:10 +0000424 if sys.argv[1:]:
425 remotetest(root, sys.argv[1])
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000426 else:
Guido van Rossum50f437c1994-07-12 16:37:21 +0000427 frame = Frame(root, {'name': 'frame',
428 Pack: {'expand': 1, 'fill': 'both'},
429 })
430 button = Button(frame, {'name': 'button',
431 'text': 'button',
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000432 Pack: {'expand': 1}})
Guido van Rossum50f437c1994-07-12 16:37:21 +0000433 canvas = Canvas(frame, {'name': 'canvas',
434 Pack: {}})
435 fpd = PackDialog(frame)
436 fwd = WidgetDialog(frame)
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000437 bpd = PackDialog(button)
438 bwd = WidgetDialog(button)
439 cpd = PackDialog(canvas)
440 cwd = WidgetDialog(canvas)
Guido van Rossum3e395be1994-07-12 08:58:25 +0000441 root.mainloop()
442
Guido van Rossume7e8d1e1994-07-13 12:56:10 +0000443def remotetest(root, app):
444 from listtree import listtree
445 list = listtree(root, app)
446 list.bind('<Any-Double-1>', opendialogs)
447 list.app = app # Pass it on to handler
448
449def opendialogs(e):
450 import string
451 list = e.widget
452 sel = list.curselection()
453 for i in sel:
454 item = list.get(i)
455 widget = string.split(item)[0]
456 RemoteWidgetDialog(list, list.app, widget)
457 if widget == '.': continue
458 try:
459 RemotePackDialog(list, list.app, widget)
460 except TclError, msg:
461 print msg
462
Guido van Rossum3e395be1994-07-12 08:58:25 +0000463test()