blob: c2907c9559427edb065b434812528046c54b61fc [file] [log] [blame]
Guilherme Polocda93aa2009-01-28 13:09:03 +00001"""Ttk wrapper.
2
3This module provides classes to allow using Tk themed widget set.
4
5Ttk is based on a revised and enhanced version of
6TIP #48 (http://tip.tcl.tk/48) specified style engine.
7
8Its basic idea is to separate, to the extent possible, the code
9implementing a widget's behavior from the code implementing its
10appearance. Widget class bindings are primarily responsible for
11maintaining the widget state and invoking callbacks, all aspects
12of the widgets appearance lies at Themes.
13"""
14
15__version__ = "0.3.1"
16
17__author__ = "Guilherme Polo <ggpolo@gmail.com>"
18
19__all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label",
20 "Labelframe", "LabelFrame", "Menubutton", "Notebook", "Panedwindow",
21 "PanedWindow", "Progressbar", "Radiobutton", "Scale", "Scrollbar",
22 "Separator", "Sizegrip", "Style", "Treeview",
23 # Extensions
24 "LabeledScale", "OptionMenu",
25 # functions
26 "tclobjs_to_py"]
27
28import Tkinter
29
30_flatten = Tkinter._flatten
31
Guilherme Polo8e5e4382009-02-07 02:20:29 +000032# Verify if Tk is new enough to not need the Tile package
Guilherme Polocda93aa2009-01-28 13:09:03 +000033_REQUIRE_TILE = True if Tkinter.TkVersion < 8.5 else False
34
Guilherme Polo8e5e4382009-02-07 02:20:29 +000035def _load_tile(master):
36 if _REQUIRE_TILE:
37 import os
38 tilelib = os.environ.get('TILE_LIBRARY')
39 if tilelib:
40 # append custom tile path to the the list of directories that
41 # Tcl uses when attempting to resolve packages with the package
42 # command
43 master.tk.eval(
44 'global auto_path; '
45 'lappend auto_path {%s}' % tilelib)
Guilherme Polocda93aa2009-01-28 13:09:03 +000046
Guilherme Polo8e5e4382009-02-07 02:20:29 +000047 master.tk.eval('package require tile') # TclError may be raised here
48 master._tile_loaded = True
Guilherme Polocda93aa2009-01-28 13:09:03 +000049
Guilherme Polocda93aa2009-01-28 13:09:03 +000050
Guilherme Polo8e5e4382009-02-07 02:20:29 +000051def _setup_master(master=None):
52 """If master is not None, itself is returned. If master is None,
53 the default master is returned if there is one, otherwise a new
54 master is created and returned.
55
56 If it is not allowed to use the default root and master is None,
57 RuntimeError is raised."""
58 if master is None:
59 if Tkinter._support_default_root:
60 master = Tkinter._default_root or Tkinter.Tk()
61 else:
62 raise RuntimeError(
63 "No master specified and Tkinter is "
64 "configured to not support default root")
65 return master
66
Guilherme Polocda93aa2009-01-28 13:09:03 +000067
68
69def _format_optdict(optdict, script=False, ignore=None):
70 """Formats optdict to a tuple to pass it to tk.call.
71
72 E.g. (script=False):
73 {'foreground': 'blue', 'padding': [1, 2, 3, 4]} returns:
74 ('-foreground', 'blue', '-padding', '1 2 3 4')"""
75 format = "%s" if not script else "{%s}"
76
77 opts = []
78 for opt, value in optdict.iteritems():
79 if ignore and opt in ignore:
80 continue
81
82 if isinstance(value, (list, tuple)):
83 v = []
84 for val in value:
85 if isinstance(val, basestring):
86 v.append(unicode(val) if val else '{}')
87 else:
88 v.append(str(val))
89
90 # format v according to the script option, but also check for
91 # space in any value in v in order to group them correctly
92 value = format % ' '.join(
93 ('{%s}' if ' ' in val else '%s') % val for val in v)
94
95 if script and value == '':
96 value = '{}' # empty string in Python is equivalent to {} in Tcl
97
98 opts.append(("-%s" % opt, value))
99
100 # Remember: _flatten skips over None
101 return _flatten(opts)
102
103def _format_mapdict(mapdict, script=False):
104 """Formats mapdict to pass it to tk.call.
105
106 E.g. (script=False):
107 {'expand': [('active', 'selected', 'grey'), ('focus', [1, 2, 3, 4])]}
108
109 returns:
110
111 ('-expand', '{active selected} grey focus {1, 2, 3, 4}')"""
112 # if caller passes a Tcl script to tk.call, all the values need to
113 # be grouped into words (arguments to a command in Tcl dialect)
114 format = "%s" if not script else "{%s}"
115
116 opts = []
117 for opt, value in mapdict.iteritems():
118
119 opt_val = []
120 # each value in mapdict is expected to be a sequence, where each item
121 # is another sequence containing a state (or several) and a value
122 for statespec in value:
123 state, val = statespec[:-1], statespec[-1]
124
125 if len(state) > 1: # group multiple states
126 state = "{%s}" % ' '.join(state)
127 else: # single state
128 # if it is empty (something that evaluates to False), then
129 # format it to Tcl code to denote the "normal" state
130 state = state[0] or '{}'
131
132 if isinstance(val, (list, tuple)): # val needs to be grouped
133 val = "{%s}" % ' '.join(map(str, val))
134
135 opt_val.append("%s %s" % (state, val))
136
137 opts.append(("-%s" % opt, format % ' '.join(opt_val)))
138
139 return _flatten(opts)
140
141def _format_elemcreate(etype, script=False, *args, **kw):
142 """Formats args and kw according to the given element factory etype."""
143 spec = None
144 opts = ()
145 if etype in ("image", "vsapi"):
146 if etype == "image": # define an element based on an image
147 # first arg should be the default image name
148 iname = args[0]
149 # next args, if any, are statespec/value pairs which is almost
150 # a mapdict, but we just need the value
151 imagespec = _format_mapdict({None: args[1:]})[1]
152 spec = "%s %s" % (iname, imagespec)
153
154 else:
155 # define an element whose visual appearance is drawn using the
156 # Microsoft Visual Styles API which is responsible for the
157 # themed styles on Windows XP and Vista.
158 # Availability: Tk 8.6, Windows XP and Vista.
159 class_name, part_id = args[:2]
160 statemap = _format_mapdict({None: args[2:]})[1]
161 spec = "%s %s %s" % (class_name, part_id, statemap)
162
163 opts = _format_optdict(kw, script)
164
165 elif etype == "from": # clone an element
166 # it expects a themename and optionally an element to clone from,
167 # otherwise it will clone {} (empty element)
168 spec = args[0] # theme name
169 if len(args) > 1: # elementfrom specified
170 opts = (args[1], )
171
172 if script:
173 spec = '{%s}' % spec
174 opts = ' '.join(map(str, opts))
175
176 return spec, opts
177
178def _format_layoutlist(layout, indent=0, indent_size=2):
179 """Formats a layout list so we can pass the result to ttk::style
180 layout and ttk::style settings. Note that the layout doesn't has to
181 be a list necessarily.
182
183 E.g.:
184 [("Menubutton.background", None),
185 ("Menubutton.button", {"children":
186 [("Menubutton.focus", {"children":
187 [("Menubutton.padding", {"children":
188 [("Menubutton.label", {"side": "left", "expand": 1})]
189 })]
190 })]
191 }),
192 ("Menubutton.indicator", {"side": "right"})
193 ]
194
195 returns:
196
197 Menubutton.background
198 Menubutton.button -children {
199 Menubutton.focus -children {
200 Menubutton.padding -children {
201 Menubutton.label -side left -expand 1
202 }
203 }
204 }
205 Menubutton.indicator -side right"""
206 script = []
207
208 for layout_elem in layout:
209 elem, opts = layout_elem
210 opts = opts or {}
211 fopts = ' '.join(map(str, _format_optdict(opts, True, "children")))
212 head = "%s%s%s" % (' ' * indent, elem, (" %s" % fopts) if fopts else '')
213
214 if "children" in opts:
215 script.append(head + " -children {")
216 indent += indent_size
217 newscript, indent = _format_layoutlist(opts['children'], indent,
218 indent_size)
219 script.append(newscript)
220 indent -= indent_size
221 script.append('%s}' % (' ' * indent))
222 else:
223 script.append(head)
224
225 return '\n'.join(script), indent
226
227def _script_from_settings(settings):
228 """Returns an appropriate script, based on settings, according to
229 theme_settings definition to be used by theme_settings and
230 theme_create."""
231 script = []
232 # a script will be generated according to settings passed, which
233 # will then be evaluated by Tcl
234 for name, opts in settings.iteritems():
235 # will format specific keys according to Tcl code
236 if opts.get('configure'): # format 'configure'
237 s = ' '.join(map(unicode, _format_optdict(opts['configure'], True)))
238 script.append("ttk::style configure %s %s;" % (name, s))
239
240 if opts.get('map'): # format 'map'
241 s = ' '.join(map(unicode, _format_mapdict(opts['map'], True)))
242 script.append("ttk::style map %s %s;" % (name, s))
243
244 if 'layout' in opts: # format 'layout' which may be empty
245 if not opts['layout']:
246 s = 'null' # could be any other word, but this one makes sense
247 else:
248 s, _ = _format_layoutlist(opts['layout'])
249 script.append("ttk::style layout %s {\n%s\n}" % (name, s))
250
251 if opts.get('element create'): # format 'element create'
252 eopts = opts['element create']
253 etype = eopts[0]
254
255 # find where args end, and where kwargs start
256 argc = 1 # etype was the first one
257 while argc < len(eopts) and not hasattr(eopts[argc], 'iteritems'):
258 argc += 1
259
260 elemargs = eopts[1:argc]
261 elemkw = eopts[argc] if argc < len(eopts) and eopts[argc] else {}
262 spec, opts = _format_elemcreate(etype, True, *elemargs, **elemkw)
263
264 script.append("ttk::style element create %s %s %s %s" % (
265 name, etype, spec, opts))
266
267 return '\n'.join(script)
268
269def _dict_from_tcltuple(ttuple, cut_minus=True):
270 """Break tuple in pairs, format it properly, then build the return
271 dict. If cut_minus is True, the supposed '-' prefixing options will
272 be removed.
273
274 ttuple is expected to contain an even number of elements."""
275 opt_start = 1 if cut_minus else 0
276
277 retdict = {}
278 it = iter(ttuple)
279 for opt, val in zip(it, it):
280 retdict[str(opt)[opt_start:]] = val
281
282 return tclobjs_to_py(retdict)
283
284def _list_from_statespec(stuple):
285 """Construct a list from the given statespec tuple according to the
286 accepted statespec accepted by _format_mapdict."""
287 nval = []
288 for val in stuple:
289 typename = getattr(val, 'typename', None)
290 if typename is None:
291 nval.append(val)
292 else: # this is a Tcl object
293 val = str(val)
294 if typename == 'StateSpec':
295 val = val.split()
296 nval.append(val)
297
298 it = iter(nval)
299 return [_flatten(spec) for spec in zip(it, it)]
300
301def _list_from_layouttuple(ltuple):
302 """Construct a list from the tuple returned by ttk::layout, this is
303 somewhat the reverse of _format_layoutlist."""
304 res = []
305
306 indx = 0
307 while indx < len(ltuple):
308 name = ltuple[indx]
309 opts = {}
310 res.append((name, opts))
311 indx += 1
312
313 while indx < len(ltuple): # grab name's options
314 opt, val = ltuple[indx:indx + 2]
315 if not opt.startswith('-'): # found next name
316 break
317
318 opt = opt[1:] # remove the '-' from the option
319 indx += 2
320
321 if opt == 'children':
322 val = _list_from_layouttuple(val)
323
324 opts[opt] = val
325
326 return res
327
328def _val_or_dict(options, func, *args):
329 """Format options then call func with args and options and return
330 the appropriate result.
331
332 If no option is specified, a dict is returned. If a option is
333 specified with the None value, the value for that option is returned.
334 Otherwise, the function just sets the passed options and the caller
335 shouldn't be expecting a return value anyway."""
336 options = _format_optdict(options)
337 res = func(*(args + options))
338
339 if len(options) % 2: # option specified without a value, return its value
340 return res
341
342 return _dict_from_tcltuple(res)
343
344def _convert_stringval(value):
345 """Converts a value to, hopefully, a more appropriate Python object."""
346 value = unicode(value)
347 try:
348 value = int(value)
349 except (ValueError, TypeError):
350 pass
351
352 return value
353
354def tclobjs_to_py(adict):
355 """Returns adict with its values converted from Tcl objects to Python
356 objects."""
357 for opt, val in adict.iteritems():
358 if val and hasattr(val, '__len__') and not isinstance(val, basestring):
359 if getattr(val[0], 'typename', None) == 'StateSpec':
360 val = _list_from_statespec(val)
361 else:
362 val = map(_convert_stringval, val)
363
364 elif hasattr(val, 'typename'): # some other (single) Tcl object
365 val = _convert_stringval(val)
366
367 adict[opt] = val
368
369 return adict
370
371
372class Style(object):
373 """Manipulate style database."""
374
375 _name = "ttk::style"
376
377 def __init__(self, master=None):
Guilherme Polo8e5e4382009-02-07 02:20:29 +0000378 master = _setup_master(master)
379
380 if not getattr(master, '_tile_loaded', False):
381 # Load tile now, if needed
382 _load_tile(master)
Guilherme Polocda93aa2009-01-28 13:09:03 +0000383
384 self.master = master
385 self.tk = self.master.tk
386
387
388 def configure(self, style, query_opt=None, **kw):
389 """Query or sets the default value of the specified option(s) in
390 style.
391
392 Each key in kw is an option and each value is either a string or
393 a sequence identifying the value for that option."""
394 if query_opt is not None:
395 kw[query_opt] = None
396 return _val_or_dict(kw, self.tk.call, self._name, "configure", style)
397
398
399 def map(self, style, query_opt=None, **kw):
400 """Query or sets dynamic values of the specified option(s) in
401 style.
402
403 Each key in kw is an option and each value should be a list or a
404 tuple (usually) containing statespecs grouped in tuples, or list,
405 or something else of your preference. A statespec is compound of
406 one or more states and then a value."""
407 if query_opt is not None:
408 return _list_from_statespec(
409 self.tk.call(self._name, "map", style, '-%s' % query_opt))
410
411 return _dict_from_tcltuple(
412 self.tk.call(self._name, "map", style, *(_format_mapdict(kw))))
413
414
415 def lookup(self, style, option, state=None, default=None):
416 """Returns the value specified for option in style.
417
418 If state is specified it is expected to be a sequence of one
419 or more states. If the default argument is set, it is used as
420 a fallback value in case no specification for option is found."""
421 state = ' '.join(state) if state else ''
422
423 return self.tk.call(self._name, "lookup", style, '-%s' % option,
424 state, default)
425
426
427 def layout(self, style, layoutspec=None):
428 """Define the widget layout for given style. If layoutspec is
429 omitted, return the layout specification for given style.
430
431 layoutspec is expected to be a list or an object different than
432 None that evaluates to False if you want to "turn off" that style.
433 If it is a list (or tuple, or something else), each item should be
434 a tuple where the first item is the layout name and the second item
435 should have the format described below:
436
437 LAYOUTS
438
439 A layout can contain the value None, if takes no options, or
440 a dict of options specifying how to arrange the element.
441 The layout mechanism uses a simplified version of the pack
442 geometry manager: given an initial cavity, each element is
443 allocated a parcel. Valid options/values are:
444
445 side: whichside
446 Specifies which side of the cavity to place the
447 element; one of top, right, bottom or left. If
448 omitted, the element occupies the entire cavity.
449
450 sticky: nswe
451 Specifies where the element is placed inside its
452 allocated parcel.
453
454 children: [sublayout... ]
455 Specifies a list of elements to place inside the
456 element. Each element is a tuple (or other sequence)
457 where the first item is the layout name, and the other
458 is a LAYOUT."""
459 lspec = None
460 if layoutspec:
461 lspec = _format_layoutlist(layoutspec)[0]
462 elif layoutspec is not None: # will disable the layout ({}, '', etc)
463 lspec = "null" # could be any other word, but this may make sense
464 # when calling layout(style) later
465
466 return _list_from_layouttuple(
467 self.tk.call(self._name, "layout", style, lspec))
468
469
470 def element_create(self, elementname, etype, *args, **kw):
471 """Create a new element in the current theme of given etype."""
472 spec, opts = _format_elemcreate(etype, False, *args, **kw)
473 self.tk.call(self._name, "element", "create", elementname, etype,
474 spec, *opts)
475
476
477 def element_names(self):
478 """Returns the list of elements defined in the current theme."""
479 return self.tk.call(self._name, "element", "names")
480
481
482 def element_options(self, elementname):
483 """Return the list of elementname's options."""
484 return self.tk.call(self._name, "element", "options", elementname)
485
486
487 def theme_create(self, themename, parent=None, settings=None):
488 """Creates a new theme.
489
490 It is an error if themename already exists. If parent is
491 specified, the new theme will inherit styles, elements and
492 layouts from the specified parent theme. If settings are present,
493 they are expected to have the same syntax used for theme_settings."""
494 script = _script_from_settings(settings) if settings else ''
495
496 if parent:
497 self.tk.call(self._name, "theme", "create", themename,
498 "-parent", parent, "-settings", script)
499 else:
500 self.tk.call(self._name, "theme", "create", themename,
501 "-settings", script)
502
503
504 def theme_settings(self, themename, settings):
505 """Temporarily sets the current theme to themename, apply specified
506 settings and then restore the previous theme.
507
508 Each key in settings is a style and each value may contain the
509 keys 'configure', 'map', 'layout' and 'element create' and they
510 are expected to have the same format as specified by the methods
511 configure, map, layout and element_create respectively."""
512 script = _script_from_settings(settings)
513 self.tk.call(self._name, "theme", "settings", themename, script)
514
515
516 def theme_names(self):
517 """Returns a list of all known themes."""
518 return self.tk.call(self._name, "theme", "names")
519
520
521 def theme_use(self, themename=None):
522 """If themename is None, returns the theme in use, otherwise, set
523 the current theme to themename, refreshes all widgets and emits
524 a <<ThemeChanged>> event."""
525 if themename is None:
526 # Starting on Tk 8.6, checking this global is no longer needed
527 # since it allows doing self.tk.call(self._name, "theme", "use")
528 return self.tk.eval("return $ttk::currentTheme")
529
530 # using "ttk::setTheme" instead of "ttk::style theme use" causes
531 # the variable currentTheme to be updated, also, ttk::setTheme calls
532 # "ttk::style theme use" in order to change theme.
533 self.tk.call("ttk::setTheme", themename)
534
535
536class Widget(Tkinter.Widget):
537 """Base class for Tk themed widgets."""
538
539 def __init__(self, master, widgetname, kw=None):
540 """Constructs a Ttk Widget with the parent master.
541
542 STANDARD OPTIONS
543
544 class, cursor, takefocus, style
545
546 SCROLLABLE WIDGET OPTIONS
547
548 xscrollcommand, yscrollcommand
549
550 LABEL WIDGET OPTIONS
551
552 text, textvariable, underline, image, compound, width
553
554 WIDGET STATES
555
556 active, disabled, focus, pressed, selected, background,
557 readonly, alternate, invalid
558 """
Guilherme Polo8e5e4382009-02-07 02:20:29 +0000559 master = _setup_master(master)
560 if not getattr(master, '_tile_loaded', False):
561 # Load tile now, if needed
562 _load_tile(master)
Guilherme Polocda93aa2009-01-28 13:09:03 +0000563 Tkinter.Widget.__init__(self, master, widgetname, kw=kw)
564
565
566 def identify(self, x, y):
567 """Returns the name of the element at position x, y, or the empty
568 string if the point does not lie within any element.
569
570 x and y are pixel coordinates relative to the widget."""
571 return self.tk.call(self._w, "identify", x, y)
572
573
574 def instate(self, statespec, callback=None, *args, **kw):
575 """Test the widget's state.
576
577 If callback is not specified, returns True if the widget state
578 matches statespec and False otherwise. If callback is specified,
579 then it will be invoked with *args, **kw if the widget state
580 matches statespec. statespec is expected to be a sequence."""
581 ret = self.tk.call(self._w, "instate", ' '.join(statespec))
582 if ret and callback:
583 return callback(*args, **kw)
584
585 return bool(ret)
586
587
588 def state(self, statespec=None):
589 """Modify or inquire widget state.
590
591 Widget state is returned if statespec is None, otherwise it is
592 set according to the statespec flags and then a new state spec
593 is returned indicating which flags were changed. statespec is
594 expected to be a sequence."""
595 if statespec is not None:
596 statespec = ' '.join(statespec)
597
598 return self.tk.splitlist(str(self.tk.call(self._w, "state", statespec)))
599
600
601class Button(Widget):
602 """Ttk Button widget, displays a textual label and/or image, and
603 evaluates a command when pressed."""
604
605 def __init__(self, master=None, **kw):
606 """Construct a Ttk Button widget with the parent master.
607
608 STANDARD OPTIONS
609
610 class, compound, cursor, image, state, style, takefocus,
611 text, textvariable, underline, width
612
613 WIDGET-SPECIFIC OPTIONS
614
615 command, default, width
616 """
617 Widget.__init__(self, master, "ttk::button", kw)
618
619
620 def invoke(self):
621 """Invokes the command associated with the button."""
622 return self.tk.call(self._w, "invoke")
623
624
625class Checkbutton(Widget):
626 """Ttk Checkbutton widget which is either in on- or off-state."""
627
628 def __init__(self, master=None, **kw):
629 """Construct a Ttk Checkbutton widget with the parent master.
630
631 STANDARD OPTIONS
632
633 class, compound, cursor, image, state, style, takefocus,
634 text, textvariable, underline, width
635
636 WIDGET-SPECIFIC OPTIONS
637
638 command, offvalue, onvalue, variable
639 """
640 Widget.__init__(self, master, "ttk::checkbutton", kw)
641
642
643 def invoke(self):
644 """Toggles between the selected and deselected states and
645 invokes the associated command. If the widget is currently
646 selected, sets the option variable to the offvalue option
647 and deselects the widget; otherwise, sets the option variable
648 to the option onvalue.
649
650 Returns the result of the associated command."""
651 return self.tk.call(self._w, "invoke")
652
653
654class Entry(Widget, Tkinter.Entry):
655 """Ttk Entry widget displays a one-line text string and allows that
656 string to be edited by the user."""
657
658 def __init__(self, master=None, widget=None, **kw):
659 """Constructs a Ttk Entry widget with the parent master.
660
661 STANDARD OPTIONS
662
663 class, cursor, style, takefocus, xscrollcommand
664
665 WIDGET-SPECIFIC OPTIONS
666
667 exportselection, invalidcommand, justify, show, state,
668 textvariable, validate, validatecommand, width
669
670 VALIDATION MODES
671
672 none, key, focus, focusin, focusout, all
673 """
674 Widget.__init__(self, master, widget or "ttk::entry", kw)
675
676
677 def bbox(self, index):
678 """Return a tuple of (x, y, width, height) which describes the
679 bounding box of the character given by index."""
680 return self.tk.call(self._w, "bbox", index)
681
682
683 def identify(self, x, y):
684 """Returns the name of the element at position x, y, or the
685 empty string if the coordinates are outside the window."""
686 return self.tk.call(self._w, "identify", x, y)
687
688
689 def validate(self):
690 """Force revalidation, independent of the conditions specified
691 by the validate option. Returns False if validation fails, True
692 if it succeeds. Sets or clears the invalid state accordingly."""
693 return bool(self.tk.call(self._w, "validate"))
694
695
696class Combobox(Entry):
697 """Ttk Combobox widget combines a text field with a pop-down list of
698 values."""
699
700 def __init__(self, master=None, **kw):
701 """Construct a Ttk Combobox widget with the parent master.
702
703 STANDARD OPTIONS
704
705 class, cursor, style, takefocus
706
707 WIDGET-SPECIFIC OPTIONS
708
709 exportselection, justify, height, postcommand, state,
710 textvariable, values, width
711 """
712 # The "values" option may need special formatting, so leave to
713 # _format_optdict the responsability to format it
714 if "values" in kw:
715 kw["values"] = _format_optdict({'v': kw["values"]})[1]
716
717 Entry.__init__(self, master, "ttk::combobox", **kw)
718
719
720 def __setitem__(self, item, value):
721 if item == "values":
722 value = _format_optdict({item: value})[1]
723
724 Entry.__setitem__(self, item, value)
725
726
727 def configure(self, cnf=None, **kw):
728 """Custom Combobox configure, created to properly format the values
729 option."""
730 if "values" in kw:
731 kw["values"] = _format_optdict({'v': kw["values"]})[1]
732
733 return Entry.configure(self, cnf, **kw)
734
735
736 def current(self, newindex=None):
737 """If newindex is supplied, sets the combobox value to the
738 element at position newindex in the list of values. Otherwise,
739 returns the index of the current value in the list of values
740 or -1 if the current value does not appear in the list."""
741 return self.tk.call(self._w, "current", newindex)
742
743
744 def set(self, value):
745 """Sets the value of the combobox to value."""
746 self.tk.call(self._w, "set", value)
747
748
749class Frame(Widget):
750 """Ttk Frame widget is a container, used to group other widgets
751 together."""
752
753 def __init__(self, master=None, **kw):
754 """Construct a Ttk Frame with parent master.
755
756 STANDARD OPTIONS
757
758 class, cursor, style, takefocus
759
760 WIDGET-SPECIFIC OPTIONS
761
762 borderwidth, relief, padding, width, height
763 """
764 Widget.__init__(self, master, "ttk::frame", kw)
765
766
767class Label(Widget):
768 """Ttk Label widget displays a textual label and/or image."""
769
770 def __init__(self, master=None, **kw):
771 """Construct a Ttk Label with parent master.
772
773 STANDARD OPTIONS
774
775 class, compound, cursor, image, style, takefocus, text,
776 textvariable, underline, width
777
778 WIDGET-SPECIFIC OPTIONS
779
780 anchor, background, font, foreground, justify, padding,
781 relief, text, wraplength
782 """
783 Widget.__init__(self, master, "ttk::label", kw)
784
785
786class Labelframe(Widget):
787 """Ttk Labelframe widget is a container used to group other widgets
788 together. It has an optional label, which may be a plain text string
789 or another widget."""
790
791 def __init__(self, master=None, **kw):
792 """Construct a Ttk Labelframe with parent master.
793
794 STANDARD OPTIONS
795
796 class, cursor, style, takefocus
797
798 WIDGET-SPECIFIC OPTIONS
799 labelanchor, text, underline, padding, labelwidget, width,
800 height
801 """
802 Widget.__init__(self, master, "ttk::labelframe", kw)
803
804LabelFrame = Labelframe # Tkinter name compatibility
805
806
807class Menubutton(Widget):
808 """Ttk Menubutton widget displays a textual label and/or image, and
809 displays a menu when pressed."""
810
811 def __init__(self, master=None, **kw):
812 """Construct a Ttk Menubutton with parent master.
813
814 STANDARD OPTIONS
815
816 class, compound, cursor, image, state, style, takefocus,
817 text, textvariable, underline, width
818
819 WIDGET-SPECIFIC OPTIONS
820
821 direction, menu
822 """
823 Widget.__init__(self, master, "ttk::menubutton", kw)
824
825
826class Notebook(Widget):
827 """Ttk Notebook widget manages a collection of windows and displays
828 a single one at a time. Each child window is associated with a tab,
829 which the user may select to change the currently-displayed window."""
830
831 def __init__(self, master=None, **kw):
832 """Construct a Ttk Notebook with parent master.
833
834 STANDARD OPTIONS
835
836 class, cursor, style, takefocus
837
838 WIDGET-SPECIFIC OPTIONS
839
840 height, padding, width
841
842 TAB OPTIONS
843
844 state, sticky, padding, text, image, compound, underline
845
846 TAB IDENTIFIERS (tab_id)
847
848 The tab_id argument found in several methods may take any of
849 the following forms:
850
851 * An integer between zero and the number of tabs
852 * The name of a child window
853 * A positional specification of the form "@x,y", which
854 defines the tab
855 * The string "current", which identifies the
856 currently-selected tab
857 * The string "end", which returns the number of tabs (only
858 valid for method index)
859 """
860 Widget.__init__(self, master, "ttk::notebook", kw)
861
862
863 def add(self, child, **kw):
864 """Adds a new tab to the notebook.
865
866 If window is currently managed by the notebook but hidden, it is
867 restored to its previous position."""
868 self.tk.call(self._w, "add", child, *(_format_optdict(kw)))
869
870
871 def forget(self, tab_id):
872 """Removes the tab specified by tab_id, unmaps and unmanages the
873 associated window."""
874 self.tk.call(self._w, "forget", tab_id)
875
876
877 def hide(self, tab_id):
878 """Hides the tab specified by tab_id.
879
880 The tab will not be displayed, but the associated window remains
881 managed by the notebook and its configuration remembered. Hidden
882 tabs may be restored with the add command."""
883 self.tk.call(self._w, "hide", tab_id)
884
885
886 def identify(self, x, y):
887 """Returns the name of the tab element at position x, y, or the
888 empty string if none."""
889 return self.tk.call(self._w, "identify", x, y)
890
891
892 def index(self, tab_id):
893 """Returns the numeric index of the tab specified by tab_id, or
894 the total number of tabs if tab_id is the string "end"."""
895 return self.tk.call(self._w, "index", tab_id)
896
897
898 def insert(self, pos, child, **kw):
899 """Inserts a pane at the specified position.
900
901 pos is either the string end, an integer index, or the name of
902 a managed child. If child is already managed by the notebook,
903 moves it to the specified position."""
904 self.tk.call(self._w, "insert", pos, child, *(_format_optdict(kw)))
905
906
907 def select(self, tab_id=None):
908 """Selects the specified tab.
909
910 The associated child window will be displayed, and the
911 previously-selected window (if different) is unmapped. If tab_id
912 is omitted, returns the widget name of the currently selected
913 pane."""
914 return self.tk.call(self._w, "select", tab_id)
915
916
917 def tab(self, tab_id, option=None, **kw):
918 """Query or modify the options of the specific tab_id.
919
920 If kw is not given, returns a dict of the tab option values. If option
921 is specified, returns the value of that option. Otherwise, sets the
922 options to the corresponding values."""
923 if option is not None:
924 kw[option] = None
925 return _val_or_dict(kw, self.tk.call, self._w, "tab", tab_id)
926
927
928 def tabs(self):
929 """Returns a list of windows managed by the notebook."""
930 return self.tk.call(self._w, "tabs") or ()
931
932
933 def enable_traversal(self):
934 """Enable keyboard traversal for a toplevel window containing
935 this notebook.
936
937 This will extend the bindings for the toplevel window containing
938 this notebook as follows:
939
940 Control-Tab: selects the tab following the currently selected
941 one
942
943 Shift-Control-Tab: selects the tab preceding the currently
944 selected one
945
946 Alt-K: where K is the mnemonic (underlined) character of any
947 tab, will select that tab.
948
949 Multiple notebooks in a single toplevel may be enabled for
950 traversal, including nested notebooks. However, notebook traversal
951 only works properly if all panes are direct children of the
952 notebook."""
953 # The only, and good, difference I see is about mnemonics, which works
954 # after calling this method. Control-Tab and Shift-Control-Tab always
955 # works (here at least).
956 self.tk.call("ttk::notebook::enableTraversal", self._w)
957
958
959class Panedwindow(Widget, Tkinter.PanedWindow):
960 """Ttk Panedwindow widget displays a number of subwindows, stacked
961 either vertically or horizontally."""
962
963 def __init__(self, master=None, **kw):
964 """Construct a Ttk Panedwindow with parent master.
965
966 STANDARD OPTIONS
967
968 class, cursor, style, takefocus
969
970 WIDGET-SPECIFIC OPTIONS
971
972 orient, width, height
973
974 PANE OPTIONS
975
976 weight
977 """
978 Widget.__init__(self, master, "ttk::panedwindow", kw)
979
980
981 forget = Tkinter.PanedWindow.forget # overrides Pack.forget
982
983
984 def insert(self, pos, child, **kw):
985 """Inserts a pane at the specified positions.
986
987 pos is either the string end, and integer index, or the name
988 of a child. If child is already managed by the paned window,
989 moves it to the specified position."""
990 self.tk.call(self._w, "insert", pos, child, *(_format_optdict(kw)))
991
992
993 def pane(self, pane, option=None, **kw):
994 """Query or modify the options of the specified pane.
995
996 pane is either an integer index or the name of a managed subwindow.
997 If kw is not given, returns a dict of the pane option values. If
998 option is specified then the value for that option is returned.
999 Otherwise, sets the options to the correspoding values."""
1000 if option is not None:
1001 kw[option] = None
1002 return _val_or_dict(kw, self.tk.call, self._w, "pane", pane)
1003
1004
1005 def sashpos(self, index, newpos=None):
1006 """If newpos is specified, sets the position of sash number index.
1007
1008 May adjust the positions of adjacent sashes to ensure that
1009 positions are monotonically increasing. Sash positions are further
1010 constrained to be between 0 and the total size of the widget.
1011
1012 Returns the new position of sash number index."""
1013 return self.tk.call(self._w, "sashpos", index, newpos)
1014
1015PanedWindow = Panedwindow # Tkinter name compatibility
1016
1017
1018class Progressbar(Widget):
1019 """Ttk Progressbar widget shows the status of a long-running
1020 operation. They can operate in two modes: determinate mode shows the
1021 amount completed relative to the total amount of work to be done, and
1022 indeterminate mode provides an animated display to let the user know
1023 that something is happening."""
1024
1025 def __init__(self, master=None, **kw):
1026 """Construct a Ttk Progressbar with parent master.
1027
1028 STANDARD OPTIONS
1029
1030 class, cursor, style, takefocus
1031
1032 WIDGET-SPECIFIC OPTIONS
1033
1034 orient, length, mode, maximum, value, variable, phase
1035 """
1036 Widget.__init__(self, master, "ttk::progressbar", kw)
1037
1038
1039 def start(self, interval=None):
1040 """Begin autoincrement mode: schedules a recurring timer event
1041 that calls method step every interval milliseconds.
1042
1043 interval defaults to 50 milliseconds (20 steps/second) if ommited."""
1044 self.tk.call(self._w, "start", interval)
1045
1046
1047 def step(self, amount=None):
1048 """Increments the value option by amount.
1049
1050 amount defaults to 1.0 if omitted."""
1051 self.tk.call(self._w, "step", amount)
1052
1053
1054 def stop(self):
1055 """Stop autoincrement mode: cancels any recurring timer event
1056 initiated by start."""
1057 self.tk.call(self._w, "stop")
1058
1059
1060class Radiobutton(Widget):
1061 """Ttk Radiobutton widgets are used in groups to show or change a
1062 set of mutually-exclusive options."""
1063
1064 def __init__(self, master=None, **kw):
1065 """Construct a Ttk Radiobutton with parent master.
1066
1067 STANDARD OPTIONS
1068
1069 class, compound, cursor, image, state, style, takefocus,
1070 text, textvariable, underline, width
1071
1072 WIDGET-SPECIFIC OPTIONS
1073
1074 command, value, variable
1075 """
1076 Widget.__init__(self, master, "ttk::radiobutton", kw)
1077
1078
1079 def invoke(self):
1080 """Sets the option variable to the option value, selects the
1081 widget, and invokes the associated command.
1082
1083 Returns the result of the command, or an empty string if
1084 no command is specified."""
1085 return self.tk.call(self._w, "invoke")
1086
1087
1088class Scale(Widget, Tkinter.Scale):
1089 """Ttk Scale widget is typically used to control the numeric value of
1090 a linked variable that varies uniformly over some range."""
1091
1092 def __init__(self, master=None, **kw):
1093 """Construct a Ttk Scale with parent master.
1094
1095 STANDARD OPTIONS
1096
1097 class, cursor, style, takefocus
1098
1099 WIDGET-SPECIFIC OPTIONS
1100
1101 command, from, length, orient, to, value, variable
1102 """
1103 Widget.__init__(self, master, "ttk::scale", kw)
1104
1105
1106 def configure(self, cnf=None, **kw):
1107 """Modify or query scale options.
1108
1109 Setting a value for any of the "from", "from_" or "to" options
1110 generates a <<RangeChanged>> event."""
1111 if cnf:
1112 kw.update(cnf)
1113 Widget.configure(self, **kw)
1114 if any(['from' in kw, 'from_' in kw, 'to' in kw]):
1115 self.event_generate('<<RangeChanged>>')
1116
1117
1118 def get(self, x=None, y=None):
1119 """Get the current value of the value option, or the value
1120 corresponding to the coordinates x, y if they are specified.
1121
1122 x and y are pixel coordinates relative to the scale widget
1123 origin."""
1124 return self.tk.call(self._w, 'get', x, y)
1125
1126
1127class Scrollbar(Widget, Tkinter.Scrollbar):
1128 """Ttk Scrollbar controls the viewport of a scrollable widget."""
1129
1130 def __init__(self, master=None, **kw):
1131 """Construct a Ttk Scrollbar with parent master.
1132
1133 STANDARD OPTIONS
1134
1135 class, cursor, style, takefocus
1136
1137 WIDGET-SPECIFIC OPTIONS
1138
1139 command, orient
1140 """
1141 Widget.__init__(self, master, "ttk::scrollbar", kw)
1142
1143
1144class Separator(Widget):
1145 """Ttk Separator widget displays a horizontal or vertical separator
1146 bar."""
1147
1148 def __init__(self, master=None, **kw):
1149 """Construct a Ttk Separator with parent master.
1150
1151 STANDARD OPTIONS
1152
1153 class, cursor, style, takefocus
1154
1155 WIDGET-SPECIFIC OPTIONS
1156
1157 orient
1158 """
1159 Widget.__init__(self, master, "ttk::separator", kw)
1160
1161
1162class Sizegrip(Widget):
1163 """Ttk Sizegrip allows the user to resize the containing toplevel
1164 window by pressing and dragging the grip."""
1165
1166 def __init__(self, master=None, **kw):
1167 """Construct a Ttk Sizegrip with parent master.
1168
1169 STANDARD OPTIONS
1170
1171 class, cursor, state, style, takefocus
1172 """
1173 Widget.__init__(self, master, "ttk::sizegrip", kw)
1174
1175
1176class Treeview(Widget):
1177 """Ttk Treeview widget displays a hierarchical collection of items.
1178
1179 Each item has a textual label, an optional image, and an optional list
1180 of data values. The data values are displayed in successive columns
1181 after the tree label."""
1182
1183 def __init__(self, master=None, **kw):
1184 """Construct a Ttk Treeview with parent master.
1185
1186 STANDARD OPTIONS
1187
1188 class, cursor, style, takefocus, xscrollcommand,
1189 yscrollcommand
1190
1191 WIDGET-SPECIFIC OPTIONS
1192
1193 columns, displaycolumns, height, padding, selectmode, show
1194
1195 ITEM OPTIONS
1196
1197 text, image, values, open, tags
1198
1199 TAG OPTIONS
1200
1201 foreground, background, font, image
1202 """
1203 Widget.__init__(self, master, "ttk::treeview", kw)
1204
1205
1206 def bbox(self, item, column=None):
1207 """Returns the bounding box (relative to the treeview widget's
1208 window) of the specified item in the form x y width height.
1209
1210 If column is specified, returns the bounding box of that cell.
1211 If the item is not visible (i.e., if it is a descendant of a
1212 closed item or is scrolled offscreen), returns an empty string."""
1213 return self.tk.call(self._w, "bbox", item, column)
1214
1215
1216 def get_children(self, item=None):
1217 """Returns a tuple of children belonging to item.
1218
1219 If item is not specified, returns root children."""
1220 return self.tk.call(self._w, "children", item or '') or ()
1221
1222
1223 def set_children(self, item, *newchildren):
1224 """Replaces item's child with newchildren.
1225
1226 Children present in item that are not present in newchildren
1227 are detached from tree. No items in newchildren may be an
1228 ancestor of item."""
1229 self.tk.call(self._w, "children", item, newchildren)
1230
1231
1232 def column(self, column, option=None, **kw):
1233 """Query or modify the options for the specified column.
1234
1235 If kw is not given, returns a dict of the column option values. If
1236 option is specified then the value for that option is returned.
1237 Otherwise, sets the options to the corresponding values."""
1238 if option is not None:
1239 kw[option] = None
1240 return _val_or_dict(kw, self.tk.call, self._w, "column", column)
1241
1242
1243 def delete(self, *items):
1244 """Delete all specified items and all their descendants. The root
1245 item may not be deleted."""
1246 self.tk.call(self._w, "delete", items)
1247
1248
1249 def detach(self, *items):
1250 """Unlinks all of the specified items from the tree.
1251
1252 The items and all of their descendants are still present, and may
1253 be reinserted at another point in the tree, but will not be
1254 displayed. The root item may not be detached."""
1255 self.tk.call(self._w, "detach", items)
1256
1257
1258 def exists(self, item):
1259 """Returns True if the specified item is present in the three,
1260 False otherwise."""
1261 return bool(self.tk.call(self._w, "exists", item))
1262
1263
1264 def focus(self, item=None):
1265 """If item is specified, sets the focus item to item. Otherwise,
1266 returns the current focus item, or '' if there is none."""
1267 return self.tk.call(self._w, "focus", item)
1268
1269
1270 def heading(self, column, option=None, **kw):
1271 """Query or modify the heading options for the specified column.
1272
1273 If kw is not given, returns a dict of the heading option values. If
1274 option is specified then the value for that option is returned.
1275 Otherwise, sets the options to the corresponding values.
1276
1277 Valid options/values are:
1278 text: text
1279 The text to display in the column heading
1280 image: image_name
1281 Specifies an image to display to the right of the column
1282 heading
1283 anchor: anchor
1284 Specifies how the heading text should be aligned. One of
1285 the standard Tk anchor values
1286 command: callback
1287 A callback to be invoked when the heading label is
1288 pressed.
1289
1290 To configure the tree column heading, call this with column = "#0" """
1291 cmd = kw.get('command')
1292 if cmd and not isinstance(cmd, basestring):
1293 # callback not registered yet, do it now
1294 kw['command'] = self.master.register(cmd, self._substitute)
1295
1296 if option is not None:
1297 kw[option] = None
1298
1299 return _val_or_dict(kw, self.tk.call, self._w, 'heading', column)
1300
1301
1302 def identify(self, component, x, y):
1303 """Returns a description of the specified component under the
1304 point given by x and y, or the empty string if no such component
1305 is present at that position."""
1306 return self.tk.call(self._w, "identify", component, x, y)
1307
1308
1309 def identify_row(self, y):
1310 """Returns the item ID of the item at position y."""
1311 return self.identify("row", 0, y)
1312
1313
1314 def identify_column(self, x):
1315 """Returns the data column identifier of the cell at position x.
1316
1317 The tree column has ID #0."""
1318 return self.identify("column", x, 0)
1319
1320
1321 def identify_region(self, x, y):
1322 """Returns one of:
1323
1324 heading: Tree heading area.
1325 separator: Space between two columns headings;
1326 tree: The tree area.
1327 cell: A data cell.
1328
1329 * Availability: Tk 8.6"""
1330 return self.identify("region", x, y)
1331
1332
1333 def identify_element(self, x, y):
1334 """Returns the element at position x, y.
1335
1336 * Availability: Tk 8.6"""
1337 return self.identify("element", x, y)
1338
1339
1340 def index(self, item):
1341 """Returns the integer index of item within its parent's list
1342 of children."""
1343 return self.tk.call(self._w, "index", item)
1344
1345
1346 def insert(self, parent, index, iid=None, **kw):
1347 """Creates a new item and return the item identifier of the newly
1348 created item.
1349
1350 parent is the item ID of the parent item, or the empty string
1351 to create a new top-level item. index is an integer, or the value
1352 end, specifying where in the list of parent's children to insert
1353 the new item. If index is less than or equal to zero, the new node
1354 is inserted at the beginning, if index is greater than or equal to
1355 the current number of children, it is inserted at the end. If iid
1356 is specified, it is used as the item identifier, iid must not
1357 already exist in the tree. Otherwise, a new unique identifier
1358 is generated."""
1359 opts = _format_optdict(kw)
1360 if iid:
1361 res = self.tk.call(self._w, "insert", parent, index,
1362 "-id", iid, *opts)
1363 else:
1364 res = self.tk.call(self._w, "insert", parent, index, *opts)
1365
1366 return res
1367
1368
1369 def item(self, item, option=None, **kw):
1370 """Query or modify the options for the specified item.
1371
1372 If no options are given, a dict with options/values for the item
1373 is returned. If option is specified then the value for that option
1374 is returned. Otherwise, sets the options to the corresponding
1375 values as given by kw."""
1376 if option is not None:
1377 kw[option] = None
1378 return _val_or_dict(kw, self.tk.call, self._w, "item", item)
1379
1380
1381 def move(self, item, parent, index):
1382 """Moves item to position index in parent's list of children.
1383
1384 It is illegal to move an item under one of its descendants. If
1385 index is less than or equal to zero, item is moved to the
1386 beginning, if greater than or equal to the number of children,
1387 it is moved to the end. If item was detached it is reattached."""
1388 self.tk.call(self._w, "move", item, parent, index)
1389
1390 reattach = move # A sensible method name for reattaching detached items
1391
1392
1393 def next(self, item):
1394 """Returns the identifier of item's next sibling, or '' if item
1395 is the last child of its parent."""
1396 return self.tk.call(self._w, "next", item)
1397
1398
1399 def parent(self, item):
1400 """Returns the ID of the parent of item, or '' if item is at the
1401 top level of the hierarchy."""
1402 return self.tk.call(self._w, "parent", item)
1403
1404
1405 def prev(self, item):
1406 """Returns the identifier of item's previous sibling, or '' if
1407 item is the first child of its parent."""
1408 return self.tk.call(self._w, "prev", item)
1409
1410
1411 def see(self, item):
1412 """Ensure that item is visible.
1413
1414 Sets all of item's ancestors open option to True, and scrolls
1415 the widget if necessary so that item is within the visible
1416 portion of the tree."""
1417 self.tk.call(self._w, "see", item)
1418
1419
1420 def selection(self, selop=None, items=None):
1421 """If selop is not specified, returns selected items."""
1422 return self.tk.call(self._w, "selection", selop, items)
1423
1424
1425 def selection_set(self, items):
1426 """items becomes the new selection."""
1427 self.selection("set", items)
1428
1429
1430 def selection_add(self, items):
1431 """Add items to the selection."""
1432 self.selection("add", items)
1433
1434
1435 def selection_remove(self, items):
1436 """Remove items from the selection."""
1437 self.selection("remove", items)
1438
1439
1440 def selection_toggle(self, items):
1441 """Toggle the selection state of each item in items."""
1442 self.selection("toggle", items)
1443
1444
1445 def set(self, item, column=None, value=None):
1446 """With one argument, returns a dictionary of column/value pairs
1447 for the specified item. With two arguments, returns the current
1448 value of the specified column. With three arguments, sets the
1449 value of given column in given item to the specified value."""
1450 res = self.tk.call(self._w, "set", item, column, value)
1451 if column is None and value is None:
1452 return _dict_from_tcltuple(res, False)
1453 else:
1454 return res
1455
1456
1457 def tag_bind(self, tagname, sequence=None, callback=None):
1458 """Bind a callback for the given event sequence to the tag tagname.
1459 When an event is delivered to an item, the callbacks for each
1460 of the item's tags option are called."""
1461 self._bind((self._w, "tag", "bind", tagname), sequence, callback, add=0)
1462
1463
1464 def tag_configure(self, tagname, option=None, **kw):
1465 """Query or modify the options for the specified tagname.
1466
1467 If kw is not given, returns a dict of the option settings for tagname.
1468 If option is specified, returns the value for that option for the
1469 specified tagname. Otherwise, sets the options to the corresponding
1470 values for the given tagname."""
1471 if option is not None:
1472 kw[option] = None
1473 return _val_or_dict(kw, self.tk.call, self._w, "tag", "configure",
1474 tagname)
1475
1476
1477 def tag_has(self, tagname, item=None):
1478 """If item is specified, returns 1 or 0 depending on whether the
1479 specified item has the given tagname. Otherwise, returns a list of
1480 all items which have the specified tag.
1481
1482 * Availability: Tk 8.6"""
1483 return self.tk.call(self._w, "tag", "has", tagname, item)
1484
1485
1486 def xview(self, *args):
1487 """Query or modify horizontal position of the treeview."""
1488 return self.tk.call(self._w, "xview", *args)
1489
1490
1491 def yview(self, *args):
1492 """Query or modify vertical position of the treeview."""
1493 return self.tk.call(self._w, "yview", *args)
1494
1495
1496# Extensions
1497
1498class LabeledScale(Frame, object):
1499 """A Ttk Scale widget with a Ttk Label widget indicating its
1500 current value.
1501
1502 The Ttk Scale can be accessed through instance.scale, and Ttk Label
1503 can be accessed through instance.label"""
1504
1505 def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
1506 """Construct an horizontal LabeledScale with parent master, a
1507 variable to be associated with the Ttk Scale widget and its range.
1508 If variable is not specified, a Tkinter.IntVar is created.
1509
1510 WIDGET-SPECIFIC OPTIONS
1511
1512 compound: 'top' or 'bottom'
1513 Specifies how to display the label relative to the scale.
1514 Defaults to 'top'.
1515 """
1516 self._label_top = kw.pop('compound', 'top') == 'top'
1517
1518 Frame.__init__(self, master, **kw)
1519 self._variable = variable or Tkinter.IntVar(master)
1520 self._variable.set(from_)
1521 self._last_valid = from_
1522
1523 self.label = Label(self)
1524 self.scale = Scale(self, variable=self._variable, from_=from_, to=to)
1525 self.scale.bind('<<RangeChanged>>', self._adjust)
1526
1527 # position scale and label according to the compound option
1528 scale_side = 'bottom' if self._label_top else 'top'
1529 label_side = 'top' if scale_side == 'bottom' else 'bottom'
1530 self.scale.pack(side=scale_side, fill='x')
1531 tmp = Label(self).pack(side=label_side) # place holder
1532 self.label.place(anchor='n' if label_side == 'top' else 's')
1533
1534 # update the label as scale or variable changes
1535 self.__tracecb = self._variable.trace_variable('w', self._adjust)
1536 self.bind('<Configure>', self._adjust)
1537 self.bind('<Map>', self._adjust)
1538
1539
1540 def destroy(self):
1541 """Destroy this widget and possibly its associated variable."""
1542 try:
1543 self._variable.trace_vdelete('w', self.__tracecb)
1544 except AttributeError:
1545 # widget has been destroyed already
1546 pass
1547 else:
1548 del self._variable
1549 Frame.destroy(self)
1550
1551
1552 def _adjust(self, *args):
1553 """Adjust the label position according to the scale."""
1554 def adjust_label():
1555 self.update_idletasks() # "force" scale redraw
1556
1557 x, y = self.scale.coords()
1558 if self._label_top:
1559 y = self.scale.winfo_y() - self.label.winfo_reqheight()
1560 else:
1561 y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
1562
1563 self.label.place_configure(x=x, y=y)
1564
1565 from_, to = self.scale['from'], self.scale['to']
1566 if to < from_:
1567 from_, to = to, from_
1568 newval = self._variable.get()
1569 if not from_ <= newval <= to:
1570 # value outside range, set value back to the last valid one
1571 self.value = self._last_valid
1572 return
1573
1574 self._last_valid = newval
1575 self.label['text'] = newval
1576 self.after_idle(adjust_label)
1577
1578
1579 def _get_value(self):
1580 """Return current scale value."""
1581 return self._variable.get()
1582
1583
1584 def _set_value(self, val):
1585 """Set new scale value."""
1586 self._variable.set(val)
1587
1588
1589 value = property(_get_value, _set_value)
1590
1591
1592class OptionMenu(Menubutton):
1593 """Themed OptionMenu, based after Tkinter's OptionMenu, which allows
1594 the user to select a value from a menu."""
1595
1596 def __init__(self, master, variable, default=None, *values, **kwargs):
1597 """Construct a themed OptionMenu widget with master as the parent,
1598 the resource textvariable set to variable, the initially selected
1599 value specified by the default parameter, the menu values given by
1600 *values and additional keywords.
1601
1602 WIDGET-SPECIFIC OPTIONS
1603
1604 style: stylename
1605 Menubutton style.
1606 direction: 'above', 'below', 'left', 'right', or 'flush'
1607 Menubutton direction.
1608 command: callback
1609 A callback that will be invoked after selecting an item.
1610 """
1611 kw = {'textvariable': variable, 'style': kwargs.pop('style', None),
1612 'direction': kwargs.pop('direction', None)}
1613 Menubutton.__init__(self, master, **kw)
1614 self['menu'] = Tkinter.Menu(self, tearoff=False)
1615
1616 self._variable = variable
1617 self._callback = kwargs.pop('command', None)
1618 if kwargs:
1619 raise Tkinter.TclError('unknown option -%s' % (
1620 kwargs.iterkeys().next()))
1621
1622 self.set_menu(default, *values)
1623
1624
1625 def __getitem__(self, item):
1626 if item == 'menu':
1627 return self.nametowidget(Menubutton.__getitem__(self, item))
1628
1629 return Menubutton.__getitem__(self, item)
1630
1631
1632 def set_menu(self, default=None, *values):
1633 """Build a new menu of radiobuttons with *values and optionally
1634 a default value."""
1635 menu = self['menu']
1636 menu.delete(0, 'end')
1637 for val in values:
1638 menu.add_radiobutton(label=val,
1639 command=Tkinter._setit(self._variable, val, self._callback))
1640
1641 if default:
1642 self._variable.set(default)
1643
1644
1645 def destroy(self):
1646 """Destroy this widget and its associated variable."""
1647 del self._variable
1648 Menubutton.destroy(self)