blob: 98bdd145cbc26fa9d2d2f28a88b9d8964ac310dd [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
Tim Peters182b5ac2004-07-18 06:16:08 +000019 varclass = StringVar # May be overridden
Guido van Rossumb78e03c1994-07-12 15:53:26 +000020
Tim Peters182b5ac2004-07-18 06:16:08 +000021 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()
Guido van Rossum3e395be1994-07-12 08:58:25 +000033
Tim Peters182b5ac2004-07-18 06:16:08 +000034 def refresh(self):
35 self.dialog.refresh()
36 self.update()
Guido van Rossum3e395be1994-07-12 08:58:25 +000037
Tim Peters182b5ac2004-07-18 06:16:08 +000038 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)
Guido van Rossum3e395be1994-07-12 08:58:25 +000044
Tim Peters182b5ac2004-07-18 06:16:08 +000045 def set(self, e=None): # Should be overridden
46 pass
Guido van Rossum3e395be1994-07-12 08:58:25 +000047
48class BooleanOption(Option):
49
Tim Peters182b5ac2004-07-18 06:16:08 +000050 varclass = BooleanVar
Guido van Rossumb78e03c1994-07-12 15:53:26 +000051
Tim Peters182b5ac2004-07-18 06:16:08 +000052 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)
Guido van Rossum3e395be1994-07-12 08:58:25 +000062
63class EnumOption(Option):
64
Tim Peters182b5ac2004-07-18 06:16:08 +000065 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)
Guido van Rossum3e395be1994-07-12 08:58:25 +000078
79class StringOption(Option):
80
Tim Peters182b5ac2004-07-18 06:16:08 +000081 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)
Guido van Rossum3e395be1994-07-12 08:58:25 +000089
Guido van Rossumb78e03c1994-07-12 15:53:26 +000090class ReadonlyOption(Option):
Guido van Rossum3e395be1994-07-12 08:58:25 +000091
Tim Peters182b5ac2004-07-18 06:16:08 +000092 def addoption(self):
93 self.label = Label(self.frame, textvariable=self.var,
94 anchor=E)
95 self.label.pack(side=RIGHT)
Guido van Rossum3e395be1994-07-12 08:58:25 +000096
Guido van Rossumb78e03c1994-07-12 15:53:26 +000097class Dialog:
Guido van Rossum3e395be1994-07-12 08:58:25 +000098
Tim Peters182b5ac2004-07-18 06:16:08 +000099 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()
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000107
Tim Peters182b5ac2004-07-18 06:16:08 +0000108 def refresh(self): pass # Must override
Guido van Rossum3d0df461994-08-03 08:10:35 +0000109
Tim Peters182b5ac2004-07-18 06:16:08 +0000110 def fixclasses(self): pass # May override
Guido van Rossum3d0df461994-08-03 08:10:35 +0000111
Tim Peters182b5ac2004-07-18 06:16:08 +0000112 def addchoices(self):
113 self.choices = {}
114 list = []
Collin Winter6f2df4d2007-07-17 20:59:35 +0000115 for k, dc in list(self.options.items()):
Tim Peters182b5ac2004-07-18 06:16:08 +0000116 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)
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000132
Tim Peters182b5ac2004-07-18 06:16:08 +0000133 # Must override:
134 options = {}
135 classes = {}
Guido van Rossum3d0df461994-08-03 08:10:35 +0000136
Tim Peters182b5ac2004-07-18 06:16:08 +0000137 # May override:
138 booleanoption = BooleanOption
139 stringoption = StringOption
140 enumoption = EnumOption
141 readonlyoption = ReadonlyOption
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000142
143class PackDialog(Dialog):
144
Tim Peters182b5ac2004-07-18 06:16:08 +0000145 def __init__(self, widget):
146 self.widget = widget
147 Dialog.__init__(self, widget)
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000148
Tim Peters182b5ac2004-07-18 06:16:08 +0000149 def refresh(self):
150 self.current = self.widget.info()
151 self.current['.class'] = self.widget.winfo_class()
152 self.current['.name'] = self.widget._w
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000153
Tim Peters182b5ac2004-07-18 06:16:08 +0000154 class packoption: # Mix-in class
155 def set(self, e=None):
156 self.current = self.var.get()
157 try:
Neal Norwitzd9108552006-03-17 08:00:19 +0000158 self.dialog.widget.pack(**{self.option: self.current})
Guido van Rossumb940e112007-01-10 16:19:56 +0000159 except TclError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000160 print(msg)
Tim Peters182b5ac2004-07-18 06:16:08 +0000161 self.refresh()
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000162
Tim Peters182b5ac2004-07-18 06:16:08 +0000163 class booleanoption(packoption, BooleanOption): pass
164 class enumoption(packoption, EnumOption): pass
165 class stringoption(packoption, StringOption): pass
166 class readonlyoption(packoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000167
Tim Peters182b5ac2004-07-18 06:16:08 +0000168 options = {
169 '.class': (None, 'Class'),
170 '.name': (None, 'Name'),
171 'after': (None, 'Widget'),
172 'anchor': ('center', 'Anchor'),
173 'before': (None, 'Widget'),
174 'expand': ('no', 'Boolean'),
175 'fill': ('none', 'Fill'),
176 'in': (None, 'Widget'),
177 'ipadx': (0, 'Pad'),
178 'ipady': (0, 'Pad'),
179 'padx': (0, 'Pad'),
180 'pady': (0, 'Pad'),
181 'side': ('top', 'Side'),
182 }
Guido van Rossum3e395be1994-07-12 08:58:25 +0000183
Tim Peters182b5ac2004-07-18 06:16:08 +0000184 classes = {
185 'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER),
186 'Boolean': 'boolean',
187 'Class': 'readonly',
188 'Expand': 'boolean',
189 'Fill': (NONE, X, Y, BOTH),
190 'Name': 'readonly',
191 'Pad': 'pixel',
192 'Side': (TOP, RIGHT, BOTTOM, LEFT),
193 'Widget': 'readonly',
194 }
Guido van Rossum3e395be1994-07-12 08:58:25 +0000195
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000196class RemotePackDialog(PackDialog):
197
Tim Peters182b5ac2004-07-18 06:16:08 +0000198 def __init__(self, master, app, widget):
199 self.master = master
200 self.app = app
201 self.widget = widget
202 self.refresh()
203 self.top = Toplevel(self.master)
204 self.top.title(self.app + ' PackDialog')
205 self.top.minsize(1, 1)
206 self.addchoices()
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000207
Tim Peters182b5ac2004-07-18 06:16:08 +0000208 def refresh(self):
209 try:
210 words = self.master.tk.splitlist(
211 self.master.send(self.app,
212 'pack',
213 'info',
214 self.widget))
Guido van Rossumb940e112007-01-10 16:19:56 +0000215 except TclError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000216 print(msg)
Tim Peters182b5ac2004-07-18 06:16:08 +0000217 return
218 dict = {}
219 for i in range(0, len(words), 2):
220 key = words[i][1:]
221 value = words[i+1]
222 dict[key] = value
223 dict['.class'] = self.master.send(self.app,
224 'winfo',
225 'class',
226 self.widget)
227 dict['.name'] = self.widget
228 self.current = dict
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000229
Tim Peters182b5ac2004-07-18 06:16:08 +0000230 class remotepackoption: # Mix-in class
231 def set(self, e=None):
232 self.current = self.var.get()
233 try:
234 self.dialog.master.send(
235 self.dialog.app,
236 'pack',
237 'config',
238 self.dialog.widget,
239 '-'+self.option,
240 self.dialog.master.tk.merge(
241 self.current))
Guido van Rossumb940e112007-01-10 16:19:56 +0000242 except TclError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000243 print(msg)
Tim Peters182b5ac2004-07-18 06:16:08 +0000244 self.refresh()
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000245
Tim Peters182b5ac2004-07-18 06:16:08 +0000246 class booleanoption(remotepackoption, BooleanOption): pass
247 class enumoption(remotepackoption, EnumOption): pass
248 class stringoption(remotepackoption, StringOption): pass
249 class readonlyoption(remotepackoption, ReadonlyOption): pass
Guido van Rossumb78e03c1994-07-12 15:53:26 +0000250
251class WidgetDialog(Dialog):
252
Tim Peters182b5ac2004-07-18 06:16:08 +0000253 def __init__(self, widget):
254 self.widget = widget
255 self.klass = widget.winfo_class()
256 Dialog.__init__(self, widget)
Guido van Rossum3d0df461994-08-03 08:10:35 +0000257
Tim Peters182b5ac2004-07-18 06:16:08 +0000258 def fixclasses(self):
Collin Winter6f2df4d2007-07-17 20:59:35 +0000259 if self.klass in self.addclasses:
Tim Peters182b5ac2004-07-18 06:16:08 +0000260 classes = {}
261 for c in (self.classes,
262 self.addclasses[self.klass]):
Collin Winter6f2df4d2007-07-17 20:59:35 +0000263 for k in list(c.keys()):
Tim Peters182b5ac2004-07-18 06:16:08 +0000264 classes[k] = c[k]
265 self.classes = classes
Guido van Rossum3e395be1994-07-12 08:58:25 +0000266
Tim Peters182b5ac2004-07-18 06:16:08 +0000267 def refresh(self):
268 self.configuration = self.widget.config()
269 self.update()
270 self.current['.class'] = self.widget.winfo_class()
271 self.current['.name'] = self.widget._w
Guido van Rossum50f437c1994-07-12 16:37:21 +0000272
Tim Peters182b5ac2004-07-18 06:16:08 +0000273 def update(self):
274 self.current = {}
275 self.options = {}
Collin Winter6f2df4d2007-07-17 20:59:35 +0000276 for k, v in list(self.configuration.items()):
Tim Peters182b5ac2004-07-18 06:16:08 +0000277 if len(v) > 4:
278 self.current[k] = v[4]
279 self.options[k] = v[3], v[2] # default, klass
280 self.options['.class'] = (None, 'Class')
281 self.options['.name'] = (None, 'Name')
Guido van Rossum3e395be1994-07-12 08:58:25 +0000282
Tim Peters182b5ac2004-07-18 06:16:08 +0000283 class widgetoption: # Mix-in class
284 def set(self, e=None):
285 self.current = self.var.get()
286 try:
287 self.dialog.widget[self.option] = self.current
Guido van Rossumb940e112007-01-10 16:19:56 +0000288 except TclError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000289 print(msg)
Tim Peters182b5ac2004-07-18 06:16:08 +0000290 self.refresh()
Guido van Rossum3e395be1994-07-12 08:58:25 +0000291
Tim Peters182b5ac2004-07-18 06:16:08 +0000292 class booleanoption(widgetoption, BooleanOption): pass
293 class enumoption(widgetoption, EnumOption): pass
294 class stringoption(widgetoption, StringOption): pass
295 class readonlyoption(widgetoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000296
Tim Peters182b5ac2004-07-18 06:16:08 +0000297 # Universal classes
298 classes = {
299 'Anchor': (N, NE, E, SE, S, SW, W, NW, CENTER),
300 'Aspect': 'integer',
301 'Background': 'color',
302 'Bitmap': 'bitmap',
303 'BorderWidth': 'pixel',
304 'Class': 'readonly',
305 'CloseEnough': 'double',
306 'Command': 'command',
307 'Confine': 'boolean',
308 'Cursor': 'cursor',
309 'CursorWidth': 'pixel',
310 'DisabledForeground': 'color',
311 'ExportSelection': 'boolean',
312 'Font': 'font',
313 'Foreground': 'color',
314 'From': 'integer',
315 'Geometry': 'geometry',
316 'Height': 'pixel',
317 'InsertWidth': 'time',
318 'Justify': (LEFT, CENTER, RIGHT),
319 'Label': 'string',
320 'Length': 'pixel',
321 'MenuName': 'widget',
322 'Name': 'readonly',
323 'OffTime': 'time',
324 'OnTime': 'time',
325 'Orient': (HORIZONTAL, VERTICAL),
326 'Pad': 'pixel',
327 'Relief': (RAISED, SUNKEN, FLAT, RIDGE, GROOVE),
328 'RepeatDelay': 'time',
329 'RepeatInterval': 'time',
330 'ScrollCommand': 'command',
331 'ScrollIncrement': 'pixel',
332 'ScrollRegion': 'rectangle',
333 'ShowValue': 'boolean',
334 'SetGrid': 'boolean',
335 'Sliderforeground': 'color',
336 'SliderLength': 'pixel',
337 'Text': 'string',
338 'TickInterval': 'integer',
339 'To': 'integer',
340 'Underline': 'index',
341 'Variable': 'variable',
342 'Value': 'string',
343 'Width': 'pixel',
344 'Wrap': (NONE, CHAR, WORD),
345 }
Guido van Rossum3e395be1994-07-12 08:58:25 +0000346
Tim Peters182b5ac2004-07-18 06:16:08 +0000347 # Classes that (may) differ per widget type
348 _tristate = {'State': (NORMAL, ACTIVE, DISABLED)}
349 _bistate = {'State': (NORMAL, DISABLED)}
350 addclasses = {
351 'Button': _tristate,
352 'Radiobutton': _tristate,
353 'Checkbutton': _tristate,
354 'Entry': _bistate,
355 'Text': _bistate,
356 'Menubutton': _tristate,
357 'Slider': _bistate,
358 }
Guido van Rossum50f437c1994-07-12 16:37:21 +0000359
360class RemoteWidgetDialog(WidgetDialog):
361
Tim Peters182b5ac2004-07-18 06:16:08 +0000362 def __init__(self, master, app, widget):
363 self.app = app
364 self.widget = widget
365 self.klass = master.send(self.app,
366 'winfo',
367 'class',
368 self.widget)
369 Dialog.__init__(self, master)
Guido van Rossum50f437c1994-07-12 16:37:21 +0000370
Tim Peters182b5ac2004-07-18 06:16:08 +0000371 def refresh(self):
372 try:
373 items = self.master.tk.splitlist(
374 self.master.send(self.app,
375 self.widget,
376 'config'))
Guido van Rossumb940e112007-01-10 16:19:56 +0000377 except TclError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000378 print(msg)
Tim Peters182b5ac2004-07-18 06:16:08 +0000379 return
380 dict = {}
381 for item in items:
382 words = self.master.tk.splitlist(item)
383 key = words[0][1:]
384 value = (key,) + words[1:]
385 dict[key] = value
386 self.configuration = dict
387 self.update()
388 self.current['.class'] = self.klass
389 self.current['.name'] = self.widget
Guido van Rossum50f437c1994-07-12 16:37:21 +0000390
Tim Peters182b5ac2004-07-18 06:16:08 +0000391 class remotewidgetoption: # Mix-in class
392 def set(self, e=None):
393 self.current = self.var.get()
394 try:
395 self.dialog.master.send(
396 self.dialog.app,
397 self.dialog.widget,
398 'config',
399 '-'+self.option,
400 self.current)
Guido van Rossumb940e112007-01-10 16:19:56 +0000401 except TclError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000402 print(msg)
Tim Peters182b5ac2004-07-18 06:16:08 +0000403 self.refresh()
Guido van Rossum50f437c1994-07-12 16:37:21 +0000404
Tim Peters182b5ac2004-07-18 06:16:08 +0000405 class booleanoption(remotewidgetoption, BooleanOption): pass
406 class enumoption(remotewidgetoption, EnumOption): pass
407 class stringoption(remotewidgetoption, StringOption): pass
408 class readonlyoption(remotewidgetoption, ReadonlyOption): pass
Guido van Rossum3e395be1994-07-12 08:58:25 +0000409
Guido van Rossum3e395be1994-07-12 08:58:25 +0000410def test():
Tim Peters182b5ac2004-07-18 06:16:08 +0000411 import sys
412 root = Tk()
413 root.minsize(1, 1)
414 if sys.argv[1:]:
415 remotetest(root, sys.argv[1])
416 else:
417 frame = Frame(root, name='frame')
418 frame.pack(expand=1, fill=BOTH)
419 button = Button(frame, name='button', text='button')
420 button.pack(expand=1)
421 canvas = Canvas(frame, name='canvas')
422 canvas.pack()
423 fpd = PackDialog(frame)
424 fwd = WidgetDialog(frame)
425 bpd = PackDialog(button)
426 bwd = WidgetDialog(button)
427 cpd = PackDialog(canvas)
428 cwd = WidgetDialog(canvas)
429 root.mainloop()
Guido van Rossum3e395be1994-07-12 08:58:25 +0000430
Guido van Rossume7e8d1e1994-07-13 12:56:10 +0000431def remotetest(root, app):
Tim Peters182b5ac2004-07-18 06:16:08 +0000432 from listtree import listtree
433 list = listtree(root, app)
434 list.bind('<Any-Double-1>', opendialogs)
435 list.app = app # Pass it on to handler
Guido van Rossume7e8d1e1994-07-13 12:56:10 +0000436
437def opendialogs(e):
Tim Peters182b5ac2004-07-18 06:16:08 +0000438 import string
439 list = e.widget
440 sel = list.curselection()
441 for i in sel:
442 item = list.get(i)
443 widget = string.split(item)[0]
444 RemoteWidgetDialog(list, list.app, widget)
445 if widget == '.': continue
446 try:
447 RemotePackDialog(list, list.app, widget)
Guido van Rossumb940e112007-01-10 16:19:56 +0000448 except TclError as msg:
Collin Winter6f2df4d2007-07-17 20:59:35 +0000449 print(msg)
Guido van Rossume7e8d1e1994-07-13 12:56:10 +0000450
Guido van Rossum3e395be1994-07-12 08:58:25 +0000451test()