diff --git a/Demo/tkinter/guido/AttrDialog.py b/Demo/tkinter/guido/AttrDialog.py new file mode 100755 index 00000000000..6eba09a324a --- /dev/null +++ b/Demo/tkinter/guido/AttrDialog.py @@ -0,0 +1,283 @@ + +# The options of a widget are described by the following attributes +# of the Pack and Widget dialogs: +# +# Dialog.current: {name: value} +# -- changes during Widget's lifetime +# +# Dialog.options: {name: (default, klass)} +# -- depends on widget class only +# +# Dialog.classes: {klass: (v0, v1, v2, ...) | 'boolean' | 'other'} +# -- totally static, though different between PackDialog and WidgetDialog +# (but even that could be unified) + + +from Tkinter import * + +class Option: + + def __init__(self, packdialog, option, varclass): + self.packdialog = packdialog + self.option = option + self.master = packdialog.top + self.default, self.klass = packdialog.options[option] + self.var = varclass(self.master) + self.frame = Frame(self.master, + {Pack: {'expand': 0, 'fill': 'x'}}) + self.label = Label(self.frame, + {'text': option + ':', + Pack: {'side': 'left'}, + }) + self.update() + + def refresh(self): + self.packdialog.refresh() + self.update() + + def update(self): + try: + self.current = self.packdialog.current[self.option] + except KeyError: + self.current = self.default + self.var.set(self.current) + + def set(self, e=None): + pass + +class BooleanOption(Option): + + def __init__(self, packdialog, option): + Option.__init__(self, packdialog, option, BooleanVar) + self.button = Checkbutton(self.frame, + {'text': 'on/off', + 'onvalue': '1', + 'offvalue': '0', + 'variable': self.var, + 'relief': 'raised', + 'borderwidth': 2, + 'command': self.set, + Pack: {'side': 'right'}, + }) + +class EnumOption(Option): + + def __init__(self, packdialog, option): + Option.__init__(self, packdialog, option, StringVar) + self.button = Menubutton(self.frame, + {'textvariable': self.var, + 'relief': 'raised', + 'borderwidth': 2, + Pack: {'side': 'right'}, + }) + self.menu = Menu(self.button) + self.button['menu'] = self.menu + for v in self.packdialog.classes[self.klass]: + label = v + if v == self.default: label = label + ' (default)' + self.menu.add_radiobutton( + {'label': label, + 'variable': self.var, + 'value': v, + 'command': self.set, + }) + +class StringOption(Option): + + def __init__(self, packdialog, option): + Option.__init__(self, packdialog, option, StringVar) + self.entry = Entry(self.frame, + {'textvariable': self.var, + 'width': 10, + 'relief': 'sunken', + 'borderwidth': 2, + Pack: {'side': 'right', + 'fill': 'x', 'expand': 1}, + }) + self.entry.bind('', self.set) + +class PackOption: # Mix-in class + + def set(self, e=None): + self.current = self.var.get() + try: + Pack.config(self.packdialog.widget, + {self.option: self.current}) + except TclError: + self.refresh() + +class BooleanPackOption(PackOption, BooleanOption): pass +class EnumPackOption(PackOption, EnumOption): pass +class StringPackOption(PackOption, StringOption): pass + +class PackDialog: + + options = { + 'after': (None, 'Widet'), + 'anchor': ('center', 'Anchor'), + 'before': (None, 'Widget'), + 'expand': ('no', 'Boolean'), + 'fill': ('none', 'Fill'), + 'in': (None, 'Widget'), + 'ipadx': (0, 'Pad'), + 'ipady': (0, 'Pad'), + 'padx': (0, 'Pad'), + 'pady': (0, 'Pad'), + 'side': ('top', 'Side'), + } + + classes = { + 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'), + 'Fill': ('none', 'x', 'y', 'both'), + 'Side': ('top', 'right', 'bottom', 'left'), + 'Expand': 'boolean', + 'Pad': 'pixel', + 'Widget': 'widget', + } + + def __init__(self, widget): + self.widget = widget + self.refresh() + self.top = Toplevel(self.widget) + self.top.title('Pack: %s' % widget.widgetName) + self.top.minsize(1, 1) # XXX + self.anchor = EnumPackOption(self, 'anchor') + self.side = EnumPackOption(self, 'side') + self.fill = EnumPackOption(self, 'fill') + self.expand = BooleanPackOption(self, 'expand') + self.ipadx = StringPackOption(self, 'ipadx') + self.ipady = StringPackOption(self, 'ipady') + self.padx = StringPackOption(self, 'padx') + self.pady = StringPackOption(self, 'pady') + # XXX after, before, in + + def refresh(self): + self.current = self.widget.newinfo() + +class WidgetOption: # Mix-in class + + def set(self, e=None): + self.current = self.var.get() + try: + self.packdialog.widget[self.option] = self.current + except TclError: + self.refresh() + +class BooleanWidgetOption(WidgetOption, BooleanOption): pass +class EnumWidgetOption(WidgetOption, EnumOption): pass +class StringWidgetOption(WidgetOption, StringOption): pass + +class WidgetDialog: + + # Universal classes + classes = { + 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'), + 'Aspect': 'integer', + 'Background': 'color', + 'Bitmap': 'bitmap', + 'BorderWidth': 'pixel', + 'CloseEnough': 'double', + 'Command': 'command', + 'Confine': 'boolean', + 'Cursor': 'cursor', + 'CursorWidth': 'pixel', + 'DisabledForeground': 'color', + 'ExportSelection': 'boolean', + 'Font': 'font', + 'Foreground': 'color', + 'From': 'integer', + 'Geometry': 'geometry', + 'Height': 'pixel', + 'InsertWidth': 'time', + 'Justify': ('left', 'center', 'right'), + 'Label': 'string', + 'Length': 'pixel', + 'MenuName': 'widget', + 'OffTime': 'time', + 'OnTime': 'time', + 'Orient': ('horizontal', 'vertical'), + 'Pad': 'pixel', + 'Relief': ('raised', 'sunken', 'flat', 'ridge', 'groove'), + 'RepeatDelay': 'time', + 'RepeatInterval': 'time', + 'ScrollCommand': 'command', + 'ScrollIncrement': 'pixel', + 'ScrollRegion': 'rectangle', + 'ShowValue': 'boolean', + 'SetGrid': 'boolean', + 'Sliderforeground': 'color', + 'SliderLength': 'pixel', + 'Text': 'string', + 'TickInterval': 'integer', + 'To': 'integer', + 'Underline': 'index', + 'Variable': 'variable', + 'Value': 'string', + 'Width': 'pixel', + 'Wrap': ('none', 'char', 'word'), + } + + # Classes that (may) differ per widget type + _tristate = {'State': ('normal', 'active', 'disabled')} + _bistate = {'State': ('normal', 'disabled')} + addclasses = { + 'button': _tristate, + 'radiobutton': _tristate, + 'checkbutton': _tristate, + 'entry': _bistate, + 'text': _bistate, + 'menubutton': _tristate, + 'slider': _bistate, + } + + + def __init__(self, widget): + self.widget = widget + if self.addclasses.has_key(self.widget.widgetName): + classes = {} + for c in (self.classes, + self.addclasses[self.widget.widgetName]): + for k in c.keys(): + classes[k] = c[k] + self.classes = classes + self.refresh() + self.top = Toplevel(self.widget) + self.top.title('Widget: %s' % widget.widgetName) + self.top.minsize(1, 1) + self.choices = {} + for k, (d, c) in self.options.items(): + try: + cl = self.classes[c] + except KeyError: + cl = 'unknown' + if type(cl) == TupleType: + cl = EnumWidgetOption + elif cl == 'boolean': + cl = BooleanWidgetOption + else: + cl = StringWidgetOption + self.choices[k] = cl(self, k) + + def refresh(self): + self.configuration = self.widget.config() + self.current = {} + self.options = {} + for k, v in self.configuration.items(): + if len(v) > 4: + self.current[k] = v[4] + self.options[k] = v[3], v[2] # default, klass + +def test(): + root = Tk() + root.minsize(1, 1) + frame = Frame(root, {Pack: {'expand': 1, 'fill': 'both'}}) + button = Button(frame, {'text': 'button', + Pack: {'expand': 1}}) + canvas = Canvas(frame, {Pack: {}}) + bpd = PackDialog(button) + bwd = WidgetDialog(button) + cpd = PackDialog(canvas) + cwd = WidgetDialog(canvas) + root.mainloop() + +test()