Guido van Rossum | 3e395be | 1994-07-12 08:58:25 +0000 | [diff] [blame^] | 1 | |
| 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 | |
| 16 | from Tkinter import * |
| 17 | |
| 18 | class Option: |
| 19 | |
| 20 | def __init__(self, packdialog, option, varclass): |
| 21 | self.packdialog = packdialog |
| 22 | self.option = option |
| 23 | self.master = packdialog.top |
| 24 | self.default, self.klass = packdialog.options[option] |
| 25 | self.var = varclass(self.master) |
| 26 | self.frame = Frame(self.master, |
| 27 | {Pack: {'expand': 0, 'fill': 'x'}}) |
| 28 | self.label = Label(self.frame, |
| 29 | {'text': option + ':', |
| 30 | Pack: {'side': 'left'}, |
| 31 | }) |
| 32 | self.update() |
| 33 | |
| 34 | def refresh(self): |
| 35 | self.packdialog.refresh() |
| 36 | self.update() |
| 37 | |
| 38 | def update(self): |
| 39 | try: |
| 40 | self.current = self.packdialog.current[self.option] |
| 41 | except KeyError: |
| 42 | self.current = self.default |
| 43 | self.var.set(self.current) |
| 44 | |
| 45 | def set(self, e=None): |
| 46 | pass |
| 47 | |
| 48 | class BooleanOption(Option): |
| 49 | |
| 50 | def __init__(self, packdialog, option): |
| 51 | Option.__init__(self, packdialog, option, BooleanVar) |
| 52 | self.button = Checkbutton(self.frame, |
| 53 | {'text': 'on/off', |
| 54 | 'onvalue': '1', |
| 55 | 'offvalue': '0', |
| 56 | 'variable': self.var, |
| 57 | 'relief': 'raised', |
| 58 | 'borderwidth': 2, |
| 59 | 'command': self.set, |
| 60 | Pack: {'side': 'right'}, |
| 61 | }) |
| 62 | |
| 63 | class EnumOption(Option): |
| 64 | |
| 65 | def __init__(self, packdialog, option): |
| 66 | Option.__init__(self, packdialog, option, StringVar) |
| 67 | self.button = Menubutton(self.frame, |
| 68 | {'textvariable': self.var, |
| 69 | 'relief': 'raised', |
| 70 | 'borderwidth': 2, |
| 71 | Pack: {'side': 'right'}, |
| 72 | }) |
| 73 | self.menu = Menu(self.button) |
| 74 | self.button['menu'] = self.menu |
| 75 | for v in self.packdialog.classes[self.klass]: |
| 76 | label = v |
| 77 | if v == self.default: label = label + ' (default)' |
| 78 | self.menu.add_radiobutton( |
| 79 | {'label': label, |
| 80 | 'variable': self.var, |
| 81 | 'value': v, |
| 82 | 'command': self.set, |
| 83 | }) |
| 84 | |
| 85 | class StringOption(Option): |
| 86 | |
| 87 | def __init__(self, packdialog, option): |
| 88 | Option.__init__(self, packdialog, option, StringVar) |
| 89 | 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 | |
| 99 | class PackOption: # Mix-in class |
| 100 | |
| 101 | def set(self, e=None): |
| 102 | self.current = self.var.get() |
| 103 | try: |
| 104 | Pack.config(self.packdialog.widget, |
| 105 | {self.option: self.current}) |
| 106 | except TclError: |
| 107 | self.refresh() |
| 108 | |
| 109 | class BooleanPackOption(PackOption, BooleanOption): pass |
| 110 | class EnumPackOption(PackOption, EnumOption): pass |
| 111 | class StringPackOption(PackOption, StringOption): pass |
| 112 | |
| 113 | class PackDialog: |
| 114 | |
| 115 | options = { |
| 116 | 'after': (None, 'Widet'), |
| 117 | 'anchor': ('center', 'Anchor'), |
| 118 | 'before': (None, 'Widget'), |
| 119 | 'expand': ('no', 'Boolean'), |
| 120 | 'fill': ('none', 'Fill'), |
| 121 | 'in': (None, 'Widget'), |
| 122 | 'ipadx': (0, 'Pad'), |
| 123 | 'ipady': (0, 'Pad'), |
| 124 | 'padx': (0, 'Pad'), |
| 125 | 'pady': (0, 'Pad'), |
| 126 | 'side': ('top', 'Side'), |
| 127 | } |
| 128 | |
| 129 | classes = { |
| 130 | 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'), |
| 131 | 'Fill': ('none', 'x', 'y', 'both'), |
| 132 | 'Side': ('top', 'right', 'bottom', 'left'), |
| 133 | 'Expand': 'boolean', |
| 134 | 'Pad': 'pixel', |
| 135 | 'Widget': 'widget', |
| 136 | } |
| 137 | |
| 138 | def __init__(self, widget): |
| 139 | self.widget = widget |
| 140 | self.refresh() |
| 141 | self.top = Toplevel(self.widget) |
| 142 | self.top.title('Pack: %s' % widget.widgetName) |
| 143 | self.top.minsize(1, 1) # XXX |
| 144 | self.anchor = EnumPackOption(self, 'anchor') |
| 145 | self.side = EnumPackOption(self, 'side') |
| 146 | self.fill = EnumPackOption(self, 'fill') |
| 147 | self.expand = BooleanPackOption(self, 'expand') |
| 148 | self.ipadx = StringPackOption(self, 'ipadx') |
| 149 | self.ipady = StringPackOption(self, 'ipady') |
| 150 | self.padx = StringPackOption(self, 'padx') |
| 151 | self.pady = StringPackOption(self, 'pady') |
| 152 | # XXX after, before, in |
| 153 | |
| 154 | def refresh(self): |
| 155 | self.current = self.widget.newinfo() |
| 156 | |
| 157 | class WidgetOption: # Mix-in class |
| 158 | |
| 159 | def set(self, e=None): |
| 160 | self.current = self.var.get() |
| 161 | try: |
| 162 | self.packdialog.widget[self.option] = self.current |
| 163 | except TclError: |
| 164 | self.refresh() |
| 165 | |
| 166 | class BooleanWidgetOption(WidgetOption, BooleanOption): pass |
| 167 | class EnumWidgetOption(WidgetOption, EnumOption): pass |
| 168 | class StringWidgetOption(WidgetOption, StringOption): pass |
| 169 | |
| 170 | class WidgetDialog: |
| 171 | |
| 172 | # Universal classes |
| 173 | classes = { |
| 174 | 'Anchor': ('n','ne', 'e','se', 's','sw', 'w','nw', 'center'), |
| 175 | 'Aspect': 'integer', |
| 176 | 'Background': 'color', |
| 177 | 'Bitmap': 'bitmap', |
| 178 | 'BorderWidth': 'pixel', |
| 179 | 'CloseEnough': 'double', |
| 180 | 'Command': 'command', |
| 181 | 'Confine': 'boolean', |
| 182 | 'Cursor': 'cursor', |
| 183 | 'CursorWidth': 'pixel', |
| 184 | 'DisabledForeground': 'color', |
| 185 | 'ExportSelection': 'boolean', |
| 186 | 'Font': 'font', |
| 187 | 'Foreground': 'color', |
| 188 | 'From': 'integer', |
| 189 | 'Geometry': 'geometry', |
| 190 | 'Height': 'pixel', |
| 191 | 'InsertWidth': 'time', |
| 192 | 'Justify': ('left', 'center', 'right'), |
| 193 | 'Label': 'string', |
| 194 | 'Length': 'pixel', |
| 195 | 'MenuName': 'widget', |
| 196 | 'OffTime': 'time', |
| 197 | 'OnTime': 'time', |
| 198 | 'Orient': ('horizontal', 'vertical'), |
| 199 | 'Pad': 'pixel', |
| 200 | 'Relief': ('raised', 'sunken', 'flat', 'ridge', 'groove'), |
| 201 | 'RepeatDelay': 'time', |
| 202 | 'RepeatInterval': 'time', |
| 203 | 'ScrollCommand': 'command', |
| 204 | 'ScrollIncrement': 'pixel', |
| 205 | 'ScrollRegion': 'rectangle', |
| 206 | 'ShowValue': 'boolean', |
| 207 | 'SetGrid': 'boolean', |
| 208 | 'Sliderforeground': 'color', |
| 209 | 'SliderLength': 'pixel', |
| 210 | 'Text': 'string', |
| 211 | 'TickInterval': 'integer', |
| 212 | 'To': 'integer', |
| 213 | 'Underline': 'index', |
| 214 | 'Variable': 'variable', |
| 215 | 'Value': 'string', |
| 216 | 'Width': 'pixel', |
| 217 | 'Wrap': ('none', 'char', 'word'), |
| 218 | } |
| 219 | |
| 220 | # Classes that (may) differ per widget type |
| 221 | _tristate = {'State': ('normal', 'active', 'disabled')} |
| 222 | _bistate = {'State': ('normal', 'disabled')} |
| 223 | addclasses = { |
| 224 | 'button': _tristate, |
| 225 | 'radiobutton': _tristate, |
| 226 | 'checkbutton': _tristate, |
| 227 | 'entry': _bistate, |
| 228 | 'text': _bistate, |
| 229 | 'menubutton': _tristate, |
| 230 | 'slider': _bistate, |
| 231 | } |
| 232 | |
| 233 | |
| 234 | def __init__(self, widget): |
| 235 | self.widget = widget |
| 236 | if self.addclasses.has_key(self.widget.widgetName): |
| 237 | classes = {} |
| 238 | for c in (self.classes, |
| 239 | self.addclasses[self.widget.widgetName]): |
| 240 | for k in c.keys(): |
| 241 | classes[k] = c[k] |
| 242 | self.classes = classes |
| 243 | self.refresh() |
| 244 | self.top = Toplevel(self.widget) |
| 245 | self.top.title('Widget: %s' % widget.widgetName) |
| 246 | self.top.minsize(1, 1) |
| 247 | self.choices = {} |
| 248 | for k, (d, c) in self.options.items(): |
| 249 | try: |
| 250 | cl = self.classes[c] |
| 251 | except KeyError: |
| 252 | cl = 'unknown' |
| 253 | if type(cl) == TupleType: |
| 254 | cl = EnumWidgetOption |
| 255 | elif cl == 'boolean': |
| 256 | cl = BooleanWidgetOption |
| 257 | else: |
| 258 | cl = StringWidgetOption |
| 259 | self.choices[k] = cl(self, k) |
| 260 | |
| 261 | def refresh(self): |
| 262 | self.configuration = self.widget.config() |
| 263 | self.current = {} |
| 264 | self.options = {} |
| 265 | for k, v in self.configuration.items(): |
| 266 | if len(v) > 4: |
| 267 | self.current[k] = v[4] |
| 268 | self.options[k] = v[3], v[2] # default, klass |
| 269 | |
| 270 | def test(): |
| 271 | root = Tk() |
| 272 | root.minsize(1, 1) |
| 273 | frame = Frame(root, {Pack: {'expand': 1, 'fill': 'both'}}) |
| 274 | button = Button(frame, {'text': 'button', |
| 275 | Pack: {'expand': 1}}) |
| 276 | canvas = Canvas(frame, {Pack: {}}) |
| 277 | bpd = PackDialog(button) |
| 278 | bwd = WidgetDialog(button) |
| 279 | cpd = PackDialog(canvas) |
| 280 | cwd = WidgetDialog(canvas) |
| 281 | root.mainloop() |
| 282 | |
| 283 | test() |