Issue #7117: Update float formatting testcases to match those in py3k.
[python.git] / Lib / lib-tk / ttk.py
blobaf7878837ad2f863073e4d1545627a9f75599b61
1 """Ttk wrapper.
3 This module provides classes to allow using Tk themed widget set.
5 Ttk is based on a revised and enhanced version of
6 TIP #48 (http://tip.tcl.tk/48) specified style engine.
8 Its basic idea is to separate, to the extent possible, the code
9 implementing a widget's behavior from the code implementing its
10 appearance. Widget class bindings are primarily responsible for
11 maintaining the widget state and invoking callbacks, all aspects
12 of the widgets appearance lies at Themes.
13 """
15 __version__ = "0.3.1"
17 __author__ = "Guilherme Polo <ggpolo@gmail.com>"
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", "setup_master"]
28 import Tkinter
30 _flatten = Tkinter._flatten
32 # Verify if Tk is new enough to not need the Tile package
33 _REQUIRE_TILE = True if Tkinter.TkVersion < 8.5 else False
35 def _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 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)
47 master.tk.eval('package require tile') # TclError may be raised here
48 master._tile_loaded = True
50 def _format_optdict(optdict, script=False, ignore=None):
51 """Formats optdict to a tuple to pass it to tk.call.
53 E.g. (script=False):
54 {'foreground': 'blue', 'padding': [1, 2, 3, 4]} returns:
55 ('-foreground', 'blue', '-padding', '1 2 3 4')"""
56 format = "%s" if not script else "{%s}"
58 opts = []
59 for opt, value in optdict.iteritems():
60 if ignore and opt in ignore:
61 continue
63 if isinstance(value, (list, tuple)):
64 v = []
65 for val in value:
66 if isinstance(val, basestring):
67 v.append(unicode(val) if val else '{}')
68 else:
69 v.append(str(val))
71 # format v according to the script option, but also check for
72 # space in any value in v in order to group them correctly
73 value = format % ' '.join(
74 ('{%s}' if ' ' in val else '%s') % val for val in v)
76 if script and value == '':
77 value = '{}' # empty string in Python is equivalent to {} in Tcl
79 opts.append(("-%s" % opt, value))
81 # Remember: _flatten skips over None
82 return _flatten(opts)
84 def _format_mapdict(mapdict, script=False):
85 """Formats mapdict to pass it to tk.call.
87 E.g. (script=False):
88 {'expand': [('active', 'selected', 'grey'), ('focus', [1, 2, 3, 4])]}
90 returns:
92 ('-expand', '{active selected} grey focus {1, 2, 3, 4}')"""
93 # if caller passes a Tcl script to tk.call, all the values need to
94 # be grouped into words (arguments to a command in Tcl dialect)
95 format = "%s" if not script else "{%s}"
97 opts = []
98 for opt, value in mapdict.iteritems():
100 opt_val = []
101 # each value in mapdict is expected to be a sequence, where each item
102 # is another sequence containing a state (or several) and a value
103 for statespec in value:
104 state, val = statespec[:-1], statespec[-1]
106 if len(state) > 1: # group multiple states
107 state = "{%s}" % ' '.join(state)
108 else: # single state
109 # if it is empty (something that evaluates to False), then
110 # format it to Tcl code to denote the "normal" state
111 state = state[0] or '{}'
113 if isinstance(val, (list, tuple)): # val needs to be grouped
114 val = "{%s}" % ' '.join(map(str, val))
116 opt_val.append("%s %s" % (state, val))
118 opts.append(("-%s" % opt, format % ' '.join(opt_val)))
120 return _flatten(opts)
122 def _format_elemcreate(etype, script=False, *args, **kw):
123 """Formats args and kw according to the given element factory etype."""
124 spec = None
125 opts = ()
126 if etype in ("image", "vsapi"):
127 if etype == "image": # define an element based on an image
128 # first arg should be the default image name
129 iname = args[0]
130 # next args, if any, are statespec/value pairs which is almost
131 # a mapdict, but we just need the value
132 imagespec = _format_mapdict({None: args[1:]})[1]
133 spec = "%s %s" % (iname, imagespec)
135 else:
136 # define an element whose visual appearance is drawn using the
137 # Microsoft Visual Styles API which is responsible for the
138 # themed styles on Windows XP and Vista.
139 # Availability: Tk 8.6, Windows XP and Vista.
140 class_name, part_id = args[:2]
141 statemap = _format_mapdict({None: args[2:]})[1]
142 spec = "%s %s %s" % (class_name, part_id, statemap)
144 opts = _format_optdict(kw, script)
146 elif etype == "from": # clone an element
147 # it expects a themename and optionally an element to clone from,
148 # otherwise it will clone {} (empty element)
149 spec = args[0] # theme name
150 if len(args) > 1: # elementfrom specified
151 opts = (args[1], )
153 if script:
154 spec = '{%s}' % spec
155 opts = ' '.join(map(str, opts))
157 return spec, opts
159 def _format_layoutlist(layout, indent=0, indent_size=2):
160 """Formats a layout list so we can pass the result to ttk::style
161 layout and ttk::style settings. Note that the layout doesn't has to
162 be a list necessarily.
164 E.g.:
165 [("Menubutton.background", None),
166 ("Menubutton.button", {"children":
167 [("Menubutton.focus", {"children":
168 [("Menubutton.padding", {"children":
169 [("Menubutton.label", {"side": "left", "expand": 1})]
173 ("Menubutton.indicator", {"side": "right"})
176 returns:
178 Menubutton.background
179 Menubutton.button -children {
180 Menubutton.focus -children {
181 Menubutton.padding -children {
182 Menubutton.label -side left -expand 1
186 Menubutton.indicator -side right"""
187 script = []
189 for layout_elem in layout:
190 elem, opts = layout_elem
191 opts = opts or {}
192 fopts = ' '.join(map(str, _format_optdict(opts, True, "children")))
193 head = "%s%s%s" % (' ' * indent, elem, (" %s" % fopts) if fopts else '')
195 if "children" in opts:
196 script.append(head + " -children {")
197 indent += indent_size
198 newscript, indent = _format_layoutlist(opts['children'], indent,
199 indent_size)
200 script.append(newscript)
201 indent -= indent_size
202 script.append('%s}' % (' ' * indent))
203 else:
204 script.append(head)
206 return '\n'.join(script), indent
208 def _script_from_settings(settings):
209 """Returns an appropriate script, based on settings, according to
210 theme_settings definition to be used by theme_settings and
211 theme_create."""
212 script = []
213 # a script will be generated according to settings passed, which
214 # will then be evaluated by Tcl
215 for name, opts in settings.iteritems():
216 # will format specific keys according to Tcl code
217 if opts.get('configure'): # format 'configure'
218 s = ' '.join(map(unicode, _format_optdict(opts['configure'], True)))
219 script.append("ttk::style configure %s %s;" % (name, s))
221 if opts.get('map'): # format 'map'
222 s = ' '.join(map(unicode, _format_mapdict(opts['map'], True)))
223 script.append("ttk::style map %s %s;" % (name, s))
225 if 'layout' in opts: # format 'layout' which may be empty
226 if not opts['layout']:
227 s = 'null' # could be any other word, but this one makes sense
228 else:
229 s, _ = _format_layoutlist(opts['layout'])
230 script.append("ttk::style layout %s {\n%s\n}" % (name, s))
232 if opts.get('element create'): # format 'element create'
233 eopts = opts['element create']
234 etype = eopts[0]
236 # find where args end, and where kwargs start
237 argc = 1 # etype was the first one
238 while argc < len(eopts) and not hasattr(eopts[argc], 'iteritems'):
239 argc += 1
241 elemargs = eopts[1:argc]
242 elemkw = eopts[argc] if argc < len(eopts) and eopts[argc] else {}
243 spec, opts = _format_elemcreate(etype, True, *elemargs, **elemkw)
245 script.append("ttk::style element create %s %s %s %s" % (
246 name, etype, spec, opts))
248 return '\n'.join(script)
250 def _dict_from_tcltuple(ttuple, cut_minus=True):
251 """Break tuple in pairs, format it properly, then build the return
252 dict. If cut_minus is True, the supposed '-' prefixing options will
253 be removed.
255 ttuple is expected to contain an even number of elements."""
256 opt_start = 1 if cut_minus else 0
258 retdict = {}
259 it = iter(ttuple)
260 for opt, val in zip(it, it):
261 retdict[str(opt)[opt_start:]] = val
263 return tclobjs_to_py(retdict)
265 def _list_from_statespec(stuple):
266 """Construct a list from the given statespec tuple according to the
267 accepted statespec accepted by _format_mapdict."""
268 nval = []
269 for val in stuple:
270 typename = getattr(val, 'typename', None)
271 if typename is None:
272 nval.append(val)
273 else: # this is a Tcl object
274 val = str(val)
275 if typename == 'StateSpec':
276 val = val.split()
277 nval.append(val)
279 it = iter(nval)
280 return [_flatten(spec) for spec in zip(it, it)]
282 def _list_from_layouttuple(ltuple):
283 """Construct a list from the tuple returned by ttk::layout, this is
284 somewhat the reverse of _format_layoutlist."""
285 res = []
287 indx = 0
288 while indx < len(ltuple):
289 name = ltuple[indx]
290 opts = {}
291 res.append((name, opts))
292 indx += 1
294 while indx < len(ltuple): # grab name's options
295 opt, val = ltuple[indx:indx + 2]
296 if not opt.startswith('-'): # found next name
297 break
299 opt = opt[1:] # remove the '-' from the option
300 indx += 2
302 if opt == 'children':
303 val = _list_from_layouttuple(val)
305 opts[opt] = val
307 return res
309 def _val_or_dict(options, func, *args):
310 """Format options then call func with args and options and return
311 the appropriate result.
313 If no option is specified, a dict is returned. If a option is
314 specified with the None value, the value for that option is returned.
315 Otherwise, the function just sets the passed options and the caller
316 shouldn't be expecting a return value anyway."""
317 options = _format_optdict(options)
318 res = func(*(args + options))
320 if len(options) % 2: # option specified without a value, return its value
321 return res
323 return _dict_from_tcltuple(res)
325 def _convert_stringval(value):
326 """Converts a value to, hopefully, a more appropriate Python object."""
327 value = unicode(value)
328 try:
329 value = int(value)
330 except (ValueError, TypeError):
331 pass
333 return value
335 def tclobjs_to_py(adict):
336 """Returns adict with its values converted from Tcl objects to Python
337 objects."""
338 for opt, val in adict.iteritems():
339 if val and hasattr(val, '__len__') and not isinstance(val, basestring):
340 if getattr(val[0], 'typename', None) == 'StateSpec':
341 val = _list_from_statespec(val)
342 else:
343 val = map(_convert_stringval, val)
345 elif hasattr(val, 'typename'): # some other (single) Tcl object
346 val = _convert_stringval(val)
348 adict[opt] = val
350 return adict
352 def setup_master(master=None):
353 """If master is not None, itself is returned. If master is None,
354 the default master is returned if there is one, otherwise a new
355 master is created and returned.
357 If it is not allowed to use the default root and master is None,
358 RuntimeError is raised."""
359 if master is None:
360 if Tkinter._support_default_root:
361 master = Tkinter._default_root or Tkinter.Tk()
362 else:
363 raise RuntimeError(
364 "No master specified and Tkinter is "
365 "configured to not support default root")
366 return master
369 class Style(object):
370 """Manipulate style database."""
372 _name = "ttk::style"
374 def __init__(self, master=None):
375 master = setup_master(master)
377 if not getattr(master, '_tile_loaded', False):
378 # Load tile now, if needed
379 _load_tile(master)
381 self.master = master
382 self.tk = self.master.tk
385 def configure(self, style, query_opt=None, **kw):
386 """Query or sets the default value of the specified option(s) in
387 style.
389 Each key in kw is an option and each value is either a string or
390 a sequence identifying the value for that option."""
391 if query_opt is not None:
392 kw[query_opt] = None
393 return _val_or_dict(kw, self.tk.call, self._name, "configure", style)
396 def map(self, style, query_opt=None, **kw):
397 """Query or sets dynamic values of the specified option(s) in
398 style.
400 Each key in kw is an option and each value should be a list or a
401 tuple (usually) containing statespecs grouped in tuples, or list,
402 or something else of your preference. A statespec is compound of
403 one or more states and then a value."""
404 if query_opt is not None:
405 return _list_from_statespec(
406 self.tk.call(self._name, "map", style, '-%s' % query_opt))
408 return _dict_from_tcltuple(
409 self.tk.call(self._name, "map", style, *(_format_mapdict(kw))))
412 def lookup(self, style, option, state=None, default=None):
413 """Returns the value specified for option in style.
415 If state is specified it is expected to be a sequence of one
416 or more states. If the default argument is set, it is used as
417 a fallback value in case no specification for option is found."""
418 state = ' '.join(state) if state else ''
420 return self.tk.call(self._name, "lookup", style, '-%s' % option,
421 state, default)
424 def layout(self, style, layoutspec=None):
425 """Define the widget layout for given style. If layoutspec is
426 omitted, return the layout specification for given style.
428 layoutspec is expected to be a list or an object different than
429 None that evaluates to False if you want to "turn off" that style.
430 If it is a list (or tuple, or something else), each item should be
431 a tuple where the first item is the layout name and the second item
432 should have the format described below:
434 LAYOUTS
436 A layout can contain the value None, if takes no options, or
437 a dict of options specifying how to arrange the element.
438 The layout mechanism uses a simplified version of the pack
439 geometry manager: given an initial cavity, each element is
440 allocated a parcel. Valid options/values are:
442 side: whichside
443 Specifies which side of the cavity to place the
444 element; one of top, right, bottom or left. If
445 omitted, the element occupies the entire cavity.
447 sticky: nswe
448 Specifies where the element is placed inside its
449 allocated parcel.
451 children: [sublayout... ]
452 Specifies a list of elements to place inside the
453 element. Each element is a tuple (or other sequence)
454 where the first item is the layout name, and the other
455 is a LAYOUT."""
456 lspec = None
457 if layoutspec:
458 lspec = _format_layoutlist(layoutspec)[0]
459 elif layoutspec is not None: # will disable the layout ({}, '', etc)
460 lspec = "null" # could be any other word, but this may make sense
461 # when calling layout(style) later
463 return _list_from_layouttuple(
464 self.tk.call(self._name, "layout", style, lspec))
467 def element_create(self, elementname, etype, *args, **kw):
468 """Create a new element in the current theme of given etype."""
469 spec, opts = _format_elemcreate(etype, False, *args, **kw)
470 self.tk.call(self._name, "element", "create", elementname, etype,
471 spec, *opts)
474 def element_names(self):
475 """Returns the list of elements defined in the current theme."""
476 return self.tk.call(self._name, "element", "names")
479 def element_options(self, elementname):
480 """Return the list of elementname's options."""
481 return self.tk.call(self._name, "element", "options", elementname)
484 def theme_create(self, themename, parent=None, settings=None):
485 """Creates a new theme.
487 It is an error if themename already exists. If parent is
488 specified, the new theme will inherit styles, elements and
489 layouts from the specified parent theme. If settings are present,
490 they are expected to have the same syntax used for theme_settings."""
491 script = _script_from_settings(settings) if settings else ''
493 if parent:
494 self.tk.call(self._name, "theme", "create", themename,
495 "-parent", parent, "-settings", script)
496 else:
497 self.tk.call(self._name, "theme", "create", themename,
498 "-settings", script)
501 def theme_settings(self, themename, settings):
502 """Temporarily sets the current theme to themename, apply specified
503 settings and then restore the previous theme.
505 Each key in settings is a style and each value may contain the
506 keys 'configure', 'map', 'layout' and 'element create' and they
507 are expected to have the same format as specified by the methods
508 configure, map, layout and element_create respectively."""
509 script = _script_from_settings(settings)
510 self.tk.call(self._name, "theme", "settings", themename, script)
513 def theme_names(self):
514 """Returns a list of all known themes."""
515 return self.tk.call(self._name, "theme", "names")
518 def theme_use(self, themename=None):
519 """If themename is None, returns the theme in use, otherwise, set
520 the current theme to themename, refreshes all widgets and emits
521 a <<ThemeChanged>> event."""
522 if themename is None:
523 # Starting on Tk 8.6, checking this global is no longer needed
524 # since it allows doing self.tk.call(self._name, "theme", "use")
525 return self.tk.eval("return $ttk::currentTheme")
527 # using "ttk::setTheme" instead of "ttk::style theme use" causes
528 # the variable currentTheme to be updated, also, ttk::setTheme calls
529 # "ttk::style theme use" in order to change theme.
530 self.tk.call("ttk::setTheme", themename)
533 class Widget(Tkinter.Widget):
534 """Base class for Tk themed widgets."""
536 def __init__(self, master, widgetname, kw=None):
537 """Constructs a Ttk Widget with the parent master.
539 STANDARD OPTIONS
541 class, cursor, takefocus, style
543 SCROLLABLE WIDGET OPTIONS
545 xscrollcommand, yscrollcommand
547 LABEL WIDGET OPTIONS
549 text, textvariable, underline, image, compound, width
551 WIDGET STATES
553 active, disabled, focus, pressed, selected, background,
554 readonly, alternate, invalid
556 master = setup_master(master)
557 if not getattr(master, '_tile_loaded', False):
558 # Load tile now, if needed
559 _load_tile(master)
560 Tkinter.Widget.__init__(self, master, widgetname, kw=kw)
563 def identify(self, x, y):
564 """Returns the name of the element at position x, y, or the empty
565 string if the point does not lie within any element.
567 x and y are pixel coordinates relative to the widget."""
568 return self.tk.call(self._w, "identify", x, y)
571 def instate(self, statespec, callback=None, *args, **kw):
572 """Test the widget's state.
574 If callback is not specified, returns True if the widget state
575 matches statespec and False otherwise. If callback is specified,
576 then it will be invoked with *args, **kw if the widget state
577 matches statespec. statespec is expected to be a sequence."""
578 ret = self.tk.call(self._w, "instate", ' '.join(statespec))
579 if ret and callback:
580 return callback(*args, **kw)
582 return bool(ret)
585 def state(self, statespec=None):
586 """Modify or inquire widget state.
588 Widget state is returned if statespec is None, otherwise it is
589 set according to the statespec flags and then a new state spec
590 is returned indicating which flags were changed. statespec is
591 expected to be a sequence."""
592 if statespec is not None:
593 statespec = ' '.join(statespec)
595 return self.tk.splitlist(str(self.tk.call(self._w, "state", statespec)))
598 class Button(Widget):
599 """Ttk Button widget, displays a textual label and/or image, and
600 evaluates a command when pressed."""
602 def __init__(self, master=None, **kw):
603 """Construct a Ttk Button widget with the parent master.
605 STANDARD OPTIONS
607 class, compound, cursor, image, state, style, takefocus,
608 text, textvariable, underline, width
610 WIDGET-SPECIFIC OPTIONS
612 command, default, width
614 Widget.__init__(self, master, "ttk::button", kw)
617 def invoke(self):
618 """Invokes the command associated with the button."""
619 return self.tk.call(self._w, "invoke")
622 class Checkbutton(Widget):
623 """Ttk Checkbutton widget which is either in on- or off-state."""
625 def __init__(self, master=None, **kw):
626 """Construct a Ttk Checkbutton widget with the parent master.
628 STANDARD OPTIONS
630 class, compound, cursor, image, state, style, takefocus,
631 text, textvariable, underline, width
633 WIDGET-SPECIFIC OPTIONS
635 command, offvalue, onvalue, variable
637 Widget.__init__(self, master, "ttk::checkbutton", kw)
640 def invoke(self):
641 """Toggles between the selected and deselected states and
642 invokes the associated command. If the widget is currently
643 selected, sets the option variable to the offvalue option
644 and deselects the widget; otherwise, sets the option variable
645 to the option onvalue.
647 Returns the result of the associated command."""
648 return self.tk.call(self._w, "invoke")
651 class Entry(Widget, Tkinter.Entry):
652 """Ttk Entry widget displays a one-line text string and allows that
653 string to be edited by the user."""
655 def __init__(self, master=None, widget=None, **kw):
656 """Constructs a Ttk Entry widget with the parent master.
658 STANDARD OPTIONS
660 class, cursor, style, takefocus, xscrollcommand
662 WIDGET-SPECIFIC OPTIONS
664 exportselection, invalidcommand, justify, show, state,
665 textvariable, validate, validatecommand, width
667 VALIDATION MODES
669 none, key, focus, focusin, focusout, all
671 Widget.__init__(self, master, widget or "ttk::entry", kw)
674 def bbox(self, index):
675 """Return a tuple of (x, y, width, height) which describes the
676 bounding box of the character given by index."""
677 return self.tk.call(self._w, "bbox", index)
680 def identify(self, x, y):
681 """Returns the name of the element at position x, y, or the
682 empty string if the coordinates are outside the window."""
683 return self.tk.call(self._w, "identify", x, y)
686 def validate(self):
687 """Force revalidation, independent of the conditions specified
688 by the validate option. Returns False if validation fails, True
689 if it succeeds. Sets or clears the invalid state accordingly."""
690 return bool(self.tk.call(self._w, "validate"))
693 class Combobox(Entry):
694 """Ttk Combobox widget combines a text field with a pop-down list of
695 values."""
697 def __init__(self, master=None, **kw):
698 """Construct a Ttk Combobox widget with the parent master.
700 STANDARD OPTIONS
702 class, cursor, style, takefocus
704 WIDGET-SPECIFIC OPTIONS
706 exportselection, justify, height, postcommand, state,
707 textvariable, values, width
709 # The "values" option may need special formatting, so leave to
710 # _format_optdict the responsability to format it
711 if "values" in kw:
712 kw["values"] = _format_optdict({'v': kw["values"]})[1]
714 Entry.__init__(self, master, "ttk::combobox", **kw)
717 def __setitem__(self, item, value):
718 if item == "values":
719 value = _format_optdict({item: value})[1]
721 Entry.__setitem__(self, item, value)
724 def configure(self, cnf=None, **kw):
725 """Custom Combobox configure, created to properly format the values
726 option."""
727 if "values" in kw:
728 kw["values"] = _format_optdict({'v': kw["values"]})[1]
730 return Entry.configure(self, cnf, **kw)
733 def current(self, newindex=None):
734 """If newindex is supplied, sets the combobox value to the
735 element at position newindex in the list of values. Otherwise,
736 returns the index of the current value in the list of values
737 or -1 if the current value does not appear in the list."""
738 return self.tk.call(self._w, "current", newindex)
741 def set(self, value):
742 """Sets the value of the combobox to value."""
743 self.tk.call(self._w, "set", value)
746 class Frame(Widget):
747 """Ttk Frame widget is a container, used to group other widgets
748 together."""
750 def __init__(self, master=None, **kw):
751 """Construct a Ttk Frame with parent master.
753 STANDARD OPTIONS
755 class, cursor, style, takefocus
757 WIDGET-SPECIFIC OPTIONS
759 borderwidth, relief, padding, width, height
761 Widget.__init__(self, master, "ttk::frame", kw)
764 class Label(Widget):
765 """Ttk Label widget displays a textual label and/or image."""
767 def __init__(self, master=None, **kw):
768 """Construct a Ttk Label with parent master.
770 STANDARD OPTIONS
772 class, compound, cursor, image, style, takefocus, text,
773 textvariable, underline, width
775 WIDGET-SPECIFIC OPTIONS
777 anchor, background, font, foreground, justify, padding,
778 relief, text, wraplength
780 Widget.__init__(self, master, "ttk::label", kw)
783 class Labelframe(Widget):
784 """Ttk Labelframe widget is a container used to group other widgets
785 together. It has an optional label, which may be a plain text string
786 or another widget."""
788 def __init__(self, master=None, **kw):
789 """Construct a Ttk Labelframe with parent master.
791 STANDARD OPTIONS
793 class, cursor, style, takefocus
795 WIDGET-SPECIFIC OPTIONS
796 labelanchor, text, underline, padding, labelwidget, width,
797 height
799 Widget.__init__(self, master, "ttk::labelframe", kw)
801 LabelFrame = Labelframe # Tkinter name compatibility
804 class Menubutton(Widget):
805 """Ttk Menubutton widget displays a textual label and/or image, and
806 displays a menu when pressed."""
808 def __init__(self, master=None, **kw):
809 """Construct a Ttk Menubutton with parent master.
811 STANDARD OPTIONS
813 class, compound, cursor, image, state, style, takefocus,
814 text, textvariable, underline, width
816 WIDGET-SPECIFIC OPTIONS
818 direction, menu
820 Widget.__init__(self, master, "ttk::menubutton", kw)
823 class Notebook(Widget):
824 """Ttk Notebook widget manages a collection of windows and displays
825 a single one at a time. Each child window is associated with a tab,
826 which the user may select to change the currently-displayed window."""
828 def __init__(self, master=None, **kw):
829 """Construct a Ttk Notebook with parent master.
831 STANDARD OPTIONS
833 class, cursor, style, takefocus
835 WIDGET-SPECIFIC OPTIONS
837 height, padding, width
839 TAB OPTIONS
841 state, sticky, padding, text, image, compound, underline
843 TAB IDENTIFIERS (tab_id)
845 The tab_id argument found in several methods may take any of
846 the following forms:
848 * An integer between zero and the number of tabs
849 * The name of a child window
850 * A positional specification of the form "@x,y", which
851 defines the tab
852 * The string "current", which identifies the
853 currently-selected tab
854 * The string "end", which returns the number of tabs (only
855 valid for method index)
857 Widget.__init__(self, master, "ttk::notebook", kw)
860 def add(self, child, **kw):
861 """Adds a new tab to the notebook.
863 If window is currently managed by the notebook but hidden, it is
864 restored to its previous position."""
865 self.tk.call(self._w, "add", child, *(_format_optdict(kw)))
868 def forget(self, tab_id):
869 """Removes the tab specified by tab_id, unmaps and unmanages the
870 associated window."""
871 self.tk.call(self._w, "forget", tab_id)
874 def hide(self, tab_id):
875 """Hides the tab specified by tab_id.
877 The tab will not be displayed, but the associated window remains
878 managed by the notebook and its configuration remembered. Hidden
879 tabs may be restored with the add command."""
880 self.tk.call(self._w, "hide", tab_id)
883 def identify(self, x, y):
884 """Returns the name of the tab element at position x, y, or the
885 empty string if none."""
886 return self.tk.call(self._w, "identify", x, y)
889 def index(self, tab_id):
890 """Returns the numeric index of the tab specified by tab_id, or
891 the total number of tabs if tab_id is the string "end"."""
892 return self.tk.call(self._w, "index", tab_id)
895 def insert(self, pos, child, **kw):
896 """Inserts a pane at the specified position.
898 pos is either the string end, an integer index, or the name of
899 a managed child. If child is already managed by the notebook,
900 moves it to the specified position."""
901 self.tk.call(self._w, "insert", pos, child, *(_format_optdict(kw)))
904 def select(self, tab_id=None):
905 """Selects the specified tab.
907 The associated child window will be displayed, and the
908 previously-selected window (if different) is unmapped. If tab_id
909 is omitted, returns the widget name of the currently selected
910 pane."""
911 return self.tk.call(self._w, "select", tab_id)
914 def tab(self, tab_id, option=None, **kw):
915 """Query or modify the options of the specific tab_id.
917 If kw is not given, returns a dict of the tab option values. If option
918 is specified, returns the value of that option. Otherwise, sets the
919 options to the corresponding values."""
920 if option is not None:
921 kw[option] = None
922 return _val_or_dict(kw, self.tk.call, self._w, "tab", tab_id)
925 def tabs(self):
926 """Returns a list of windows managed by the notebook."""
927 return self.tk.call(self._w, "tabs") or ()
930 def enable_traversal(self):
931 """Enable keyboard traversal for a toplevel window containing
932 this notebook.
934 This will extend the bindings for the toplevel window containing
935 this notebook as follows:
937 Control-Tab: selects the tab following the currently selected
940 Shift-Control-Tab: selects the tab preceding the currently
941 selected one
943 Alt-K: where K is the mnemonic (underlined) character of any
944 tab, will select that tab.
946 Multiple notebooks in a single toplevel may be enabled for
947 traversal, including nested notebooks. However, notebook traversal
948 only works properly if all panes are direct children of the
949 notebook."""
950 # The only, and good, difference I see is about mnemonics, which works
951 # after calling this method. Control-Tab and Shift-Control-Tab always
952 # works (here at least).
953 self.tk.call("ttk::notebook::enableTraversal", self._w)
956 class Panedwindow(Widget, Tkinter.PanedWindow):
957 """Ttk Panedwindow widget displays a number of subwindows, stacked
958 either vertically or horizontally."""
960 def __init__(self, master=None, **kw):
961 """Construct a Ttk Panedwindow with parent master.
963 STANDARD OPTIONS
965 class, cursor, style, takefocus
967 WIDGET-SPECIFIC OPTIONS
969 orient, width, height
971 PANE OPTIONS
973 weight
975 Widget.__init__(self, master, "ttk::panedwindow", kw)
978 forget = Tkinter.PanedWindow.forget # overrides Pack.forget
981 def insert(self, pos, child, **kw):
982 """Inserts a pane at the specified positions.
984 pos is either the string end, and integer index, or the name
985 of a child. If child is already managed by the paned window,
986 moves it to the specified position."""
987 self.tk.call(self._w, "insert", pos, child, *(_format_optdict(kw)))
990 def pane(self, pane, option=None, **kw):
991 """Query or modify the options of the specified pane.
993 pane is either an integer index or the name of a managed subwindow.
994 If kw is not given, returns a dict of the pane option values. If
995 option is specified then the value for that option is returned.
996 Otherwise, sets the options to the correspoding values."""
997 if option is not None:
998 kw[option] = None
999 return _val_or_dict(kw, self.tk.call, self._w, "pane", pane)
1002 def sashpos(self, index, newpos=None):
1003 """If newpos is specified, sets the position of sash number index.
1005 May adjust the positions of adjacent sashes to ensure that
1006 positions are monotonically increasing. Sash positions are further
1007 constrained to be between 0 and the total size of the widget.
1009 Returns the new position of sash number index."""
1010 return self.tk.call(self._w, "sashpos", index, newpos)
1012 PanedWindow = Panedwindow # Tkinter name compatibility
1015 class Progressbar(Widget):
1016 """Ttk Progressbar widget shows the status of a long-running
1017 operation. They can operate in two modes: determinate mode shows the
1018 amount completed relative to the total amount of work to be done, and
1019 indeterminate mode provides an animated display to let the user know
1020 that something is happening."""
1022 def __init__(self, master=None, **kw):
1023 """Construct a Ttk Progressbar with parent master.
1025 STANDARD OPTIONS
1027 class, cursor, style, takefocus
1029 WIDGET-SPECIFIC OPTIONS
1031 orient, length, mode, maximum, value, variable, phase
1033 Widget.__init__(self, master, "ttk::progressbar", kw)
1036 def start(self, interval=None):
1037 """Begin autoincrement mode: schedules a recurring timer event
1038 that calls method step every interval milliseconds.
1040 interval defaults to 50 milliseconds (20 steps/second) if ommited."""
1041 self.tk.call(self._w, "start", interval)
1044 def step(self, amount=None):
1045 """Increments the value option by amount.
1047 amount defaults to 1.0 if omitted."""
1048 self.tk.call(self._w, "step", amount)
1051 def stop(self):
1052 """Stop autoincrement mode: cancels any recurring timer event
1053 initiated by start."""
1054 self.tk.call(self._w, "stop")
1057 class Radiobutton(Widget):
1058 """Ttk Radiobutton widgets are used in groups to show or change a
1059 set of mutually-exclusive options."""
1061 def __init__(self, master=None, **kw):
1062 """Construct a Ttk Radiobutton with parent master.
1064 STANDARD OPTIONS
1066 class, compound, cursor, image, state, style, takefocus,
1067 text, textvariable, underline, width
1069 WIDGET-SPECIFIC OPTIONS
1071 command, value, variable
1073 Widget.__init__(self, master, "ttk::radiobutton", kw)
1076 def invoke(self):
1077 """Sets the option variable to the option value, selects the
1078 widget, and invokes the associated command.
1080 Returns the result of the command, or an empty string if
1081 no command is specified."""
1082 return self.tk.call(self._w, "invoke")
1085 class Scale(Widget, Tkinter.Scale):
1086 """Ttk Scale widget is typically used to control the numeric value of
1087 a linked variable that varies uniformly over some range."""
1089 def __init__(self, master=None, **kw):
1090 """Construct a Ttk Scale with parent master.
1092 STANDARD OPTIONS
1094 class, cursor, style, takefocus
1096 WIDGET-SPECIFIC OPTIONS
1098 command, from, length, orient, to, value, variable
1100 Widget.__init__(self, master, "ttk::scale", kw)
1103 def configure(self, cnf=None, **kw):
1104 """Modify or query scale options.
1106 Setting a value for any of the "from", "from_" or "to" options
1107 generates a <<RangeChanged>> event."""
1108 if cnf:
1109 kw.update(cnf)
1110 Widget.configure(self, **kw)
1111 if any(['from' in kw, 'from_' in kw, 'to' in kw]):
1112 self.event_generate('<<RangeChanged>>')
1115 def get(self, x=None, y=None):
1116 """Get the current value of the value option, or the value
1117 corresponding to the coordinates x, y if they are specified.
1119 x and y are pixel coordinates relative to the scale widget
1120 origin."""
1121 return self.tk.call(self._w, 'get', x, y)
1124 class Scrollbar(Widget, Tkinter.Scrollbar):
1125 """Ttk Scrollbar controls the viewport of a scrollable widget."""
1127 def __init__(self, master=None, **kw):
1128 """Construct a Ttk Scrollbar with parent master.
1130 STANDARD OPTIONS
1132 class, cursor, style, takefocus
1134 WIDGET-SPECIFIC OPTIONS
1136 command, orient
1138 Widget.__init__(self, master, "ttk::scrollbar", kw)
1141 class Separator(Widget):
1142 """Ttk Separator widget displays a horizontal or vertical separator
1143 bar."""
1145 def __init__(self, master=None, **kw):
1146 """Construct a Ttk Separator with parent master.
1148 STANDARD OPTIONS
1150 class, cursor, style, takefocus
1152 WIDGET-SPECIFIC OPTIONS
1154 orient
1156 Widget.__init__(self, master, "ttk::separator", kw)
1159 class Sizegrip(Widget):
1160 """Ttk Sizegrip allows the user to resize the containing toplevel
1161 window by pressing and dragging the grip."""
1163 def __init__(self, master=None, **kw):
1164 """Construct a Ttk Sizegrip with parent master.
1166 STANDARD OPTIONS
1168 class, cursor, state, style, takefocus
1170 Widget.__init__(self, master, "ttk::sizegrip", kw)
1173 class Treeview(Widget, Tkinter.XView, Tkinter.YView):
1174 """Ttk Treeview widget displays a hierarchical collection of items.
1176 Each item has a textual label, an optional image, and an optional list
1177 of data values. The data values are displayed in successive columns
1178 after the tree label."""
1180 def __init__(self, master=None, **kw):
1181 """Construct a Ttk Treeview with parent master.
1183 STANDARD OPTIONS
1185 class, cursor, style, takefocus, xscrollcommand,
1186 yscrollcommand
1188 WIDGET-SPECIFIC OPTIONS
1190 columns, displaycolumns, height, padding, selectmode, show
1192 ITEM OPTIONS
1194 text, image, values, open, tags
1196 TAG OPTIONS
1198 foreground, background, font, image
1200 Widget.__init__(self, master, "ttk::treeview", kw)
1203 def bbox(self, item, column=None):
1204 """Returns the bounding box (relative to the treeview widget's
1205 window) of the specified item in the form x y width height.
1207 If column is specified, returns the bounding box of that cell.
1208 If the item is not visible (i.e., if it is a descendant of a
1209 closed item or is scrolled offscreen), returns an empty string."""
1210 return self.tk.call(self._w, "bbox", item, column)
1213 def get_children(self, item=None):
1214 """Returns a tuple of children belonging to item.
1216 If item is not specified, returns root children."""
1217 return self.tk.call(self._w, "children", item or '') or ()
1220 def set_children(self, item, *newchildren):
1221 """Replaces item's child with newchildren.
1223 Children present in item that are not present in newchildren
1224 are detached from tree. No items in newchildren may be an
1225 ancestor of item."""
1226 self.tk.call(self._w, "children", item, newchildren)
1229 def column(self, column, option=None, **kw):
1230 """Query or modify the options for the specified column.
1232 If kw is not given, returns a dict of the column option values. If
1233 option is specified then the value for that option is returned.
1234 Otherwise, sets the options to the corresponding values."""
1235 if option is not None:
1236 kw[option] = None
1237 return _val_or_dict(kw, self.tk.call, self._w, "column", column)
1240 def delete(self, *items):
1241 """Delete all specified items and all their descendants. The root
1242 item may not be deleted."""
1243 self.tk.call(self._w, "delete", items)
1246 def detach(self, *items):
1247 """Unlinks all of the specified items from the tree.
1249 The items and all of their descendants are still present, and may
1250 be reinserted at another point in the tree, but will not be
1251 displayed. The root item may not be detached."""
1252 self.tk.call(self._w, "detach", items)
1255 def exists(self, item):
1256 """Returns True if the specified item is present in the three,
1257 False otherwise."""
1258 return bool(self.tk.call(self._w, "exists", item))
1261 def focus(self, item=None):
1262 """If item is specified, sets the focus item to item. Otherwise,
1263 returns the current focus item, or '' if there is none."""
1264 return self.tk.call(self._w, "focus", item)
1267 def heading(self, column, option=None, **kw):
1268 """Query or modify the heading options for the specified column.
1270 If kw is not given, returns a dict of the heading option values. If
1271 option is specified then the value for that option is returned.
1272 Otherwise, sets the options to the corresponding values.
1274 Valid options/values are:
1275 text: text
1276 The text to display in the column heading
1277 image: image_name
1278 Specifies an image to display to the right of the column
1279 heading
1280 anchor: anchor
1281 Specifies how the heading text should be aligned. One of
1282 the standard Tk anchor values
1283 command: callback
1284 A callback to be invoked when the heading label is
1285 pressed.
1287 To configure the tree column heading, call this with column = "#0" """
1288 cmd = kw.get('command')
1289 if cmd and not isinstance(cmd, basestring):
1290 # callback not registered yet, do it now
1291 kw['command'] = self.master.register(cmd, self._substitute)
1293 if option is not None:
1294 kw[option] = None
1296 return _val_or_dict(kw, self.tk.call, self._w, 'heading', column)
1299 def identify(self, component, x, y):
1300 """Returns a description of the specified component under the
1301 point given by x and y, or the empty string if no such component
1302 is present at that position."""
1303 return self.tk.call(self._w, "identify", component, x, y)
1306 def identify_row(self, y):
1307 """Returns the item ID of the item at position y."""
1308 return self.identify("row", 0, y)
1311 def identify_column(self, x):
1312 """Returns the data column identifier of the cell at position x.
1314 The tree column has ID #0."""
1315 return self.identify("column", x, 0)
1318 def identify_region(self, x, y):
1319 """Returns one of:
1321 heading: Tree heading area.
1322 separator: Space between two columns headings;
1323 tree: The tree area.
1324 cell: A data cell.
1326 * Availability: Tk 8.6"""
1327 return self.identify("region", x, y)
1330 def identify_element(self, x, y):
1331 """Returns the element at position x, y.
1333 * Availability: Tk 8.6"""
1334 return self.identify("element", x, y)
1337 def index(self, item):
1338 """Returns the integer index of item within its parent's list
1339 of children."""
1340 return self.tk.call(self._w, "index", item)
1343 def insert(self, parent, index, iid=None, **kw):
1344 """Creates a new item and return the item identifier of the newly
1345 created item.
1347 parent is the item ID of the parent item, or the empty string
1348 to create a new top-level item. index is an integer, or the value
1349 end, specifying where in the list of parent's children to insert
1350 the new item. If index is less than or equal to zero, the new node
1351 is inserted at the beginning, if index is greater than or equal to
1352 the current number of children, it is inserted at the end. If iid
1353 is specified, it is used as the item identifier, iid must not
1354 already exist in the tree. Otherwise, a new unique identifier
1355 is generated."""
1356 opts = _format_optdict(kw)
1357 if iid:
1358 res = self.tk.call(self._w, "insert", parent, index,
1359 "-id", iid, *opts)
1360 else:
1361 res = self.tk.call(self._w, "insert", parent, index, *opts)
1363 return res
1366 def item(self, item, option=None, **kw):
1367 """Query or modify the options for the specified item.
1369 If no options are given, a dict with options/values for the item
1370 is returned. If option is specified then the value for that option
1371 is returned. Otherwise, sets the options to the corresponding
1372 values as given by kw."""
1373 if option is not None:
1374 kw[option] = None
1375 return _val_or_dict(kw, self.tk.call, self._w, "item", item)
1378 def move(self, item, parent, index):
1379 """Moves item to position index in parent's list of children.
1381 It is illegal to move an item under one of its descendants. If
1382 index is less than or equal to zero, item is moved to the
1383 beginning, if greater than or equal to the number of children,
1384 it is moved to the end. If item was detached it is reattached."""
1385 self.tk.call(self._w, "move", item, parent, index)
1387 reattach = move # A sensible method name for reattaching detached items
1390 def next(self, item):
1391 """Returns the identifier of item's next sibling, or '' if item
1392 is the last child of its parent."""
1393 return self.tk.call(self._w, "next", item)
1396 def parent(self, item):
1397 """Returns the ID of the parent of item, or '' if item is at the
1398 top level of the hierarchy."""
1399 return self.tk.call(self._w, "parent", item)
1402 def prev(self, item):
1403 """Returns the identifier of item's previous sibling, or '' if
1404 item is the first child of its parent."""
1405 return self.tk.call(self._w, "prev", item)
1408 def see(self, item):
1409 """Ensure that item is visible.
1411 Sets all of item's ancestors open option to True, and scrolls
1412 the widget if necessary so that item is within the visible
1413 portion of the tree."""
1414 self.tk.call(self._w, "see", item)
1417 def selection(self, selop=None, items=None):
1418 """If selop is not specified, returns selected items."""
1419 return self.tk.call(self._w, "selection", selop, items)
1422 def selection_set(self, items):
1423 """items becomes the new selection."""
1424 self.selection("set", items)
1427 def selection_add(self, items):
1428 """Add items to the selection."""
1429 self.selection("add", items)
1432 def selection_remove(self, items):
1433 """Remove items from the selection."""
1434 self.selection("remove", items)
1437 def selection_toggle(self, items):
1438 """Toggle the selection state of each item in items."""
1439 self.selection("toggle", items)
1442 def set(self, item, column=None, value=None):
1443 """With one argument, returns a dictionary of column/value pairs
1444 for the specified item. With two arguments, returns the current
1445 value of the specified column. With three arguments, sets the
1446 value of given column in given item to the specified value."""
1447 res = self.tk.call(self._w, "set", item, column, value)
1448 if column is None and value is None:
1449 return _dict_from_tcltuple(res, False)
1450 else:
1451 return res
1454 def tag_bind(self, tagname, sequence=None, callback=None):
1455 """Bind a callback for the given event sequence to the tag tagname.
1456 When an event is delivered to an item, the callbacks for each
1457 of the item's tags option are called."""
1458 self._bind((self._w, "tag", "bind", tagname), sequence, callback, add=0)
1461 def tag_configure(self, tagname, option=None, **kw):
1462 """Query or modify the options for the specified tagname.
1464 If kw is not given, returns a dict of the option settings for tagname.
1465 If option is specified, returns the value for that option for the
1466 specified tagname. Otherwise, sets the options to the corresponding
1467 values for the given tagname."""
1468 if option is not None:
1469 kw[option] = None
1470 return _val_or_dict(kw, self.tk.call, self._w, "tag", "configure",
1471 tagname)
1474 def tag_has(self, tagname, item=None):
1475 """If item is specified, returns 1 or 0 depending on whether the
1476 specified item has the given tagname. Otherwise, returns a list of
1477 all items which have the specified tag.
1479 * Availability: Tk 8.6"""
1480 return self.tk.call(self._w, "tag", "has", tagname, item)
1483 # Extensions
1485 class LabeledScale(Frame, object):
1486 """A Ttk Scale widget with a Ttk Label widget indicating its
1487 current value.
1489 The Ttk Scale can be accessed through instance.scale, and Ttk Label
1490 can be accessed through instance.label"""
1492 def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
1493 """Construct an horizontal LabeledScale with parent master, a
1494 variable to be associated with the Ttk Scale widget and its range.
1495 If variable is not specified, a Tkinter.IntVar is created.
1497 WIDGET-SPECIFIC OPTIONS
1499 compound: 'top' or 'bottom'
1500 Specifies how to display the label relative to the scale.
1501 Defaults to 'top'.
1503 self._label_top = kw.pop('compound', 'top') == 'top'
1505 Frame.__init__(self, master, **kw)
1506 self._variable = variable or Tkinter.IntVar(master)
1507 self._variable.set(from_)
1508 self._last_valid = from_
1510 self.label = Label(self)
1511 self.scale = Scale(self, variable=self._variable, from_=from_, to=to)
1512 self.scale.bind('<<RangeChanged>>', self._adjust)
1514 # position scale and label according to the compound option
1515 scale_side = 'bottom' if self._label_top else 'top'
1516 label_side = 'top' if scale_side == 'bottom' else 'bottom'
1517 self.scale.pack(side=scale_side, fill='x')
1518 tmp = Label(self).pack(side=label_side) # place holder
1519 self.label.place(anchor='n' if label_side == 'top' else 's')
1521 # update the label as scale or variable changes
1522 self.__tracecb = self._variable.trace_variable('w', self._adjust)
1523 self.bind('<Configure>', self._adjust)
1524 self.bind('<Map>', self._adjust)
1527 def destroy(self):
1528 """Destroy this widget and possibly its associated variable."""
1529 try:
1530 self._variable.trace_vdelete('w', self.__tracecb)
1531 except AttributeError:
1532 # widget has been destroyed already
1533 pass
1534 else:
1535 del self._variable
1536 Frame.destroy(self)
1539 def _adjust(self, *args):
1540 """Adjust the label position according to the scale."""
1541 def adjust_label():
1542 self.update_idletasks() # "force" scale redraw
1544 x, y = self.scale.coords()
1545 if self._label_top:
1546 y = self.scale.winfo_y() - self.label.winfo_reqheight()
1547 else:
1548 y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
1550 self.label.place_configure(x=x, y=y)
1552 from_, to = self.scale['from'], self.scale['to']
1553 if to < from_:
1554 from_, to = to, from_
1555 newval = self._variable.get()
1556 if not from_ <= newval <= to:
1557 # value outside range, set value back to the last valid one
1558 self.value = self._last_valid
1559 return
1561 self._last_valid = newval
1562 self.label['text'] = newval
1563 self.after_idle(adjust_label)
1566 def _get_value(self):
1567 """Return current scale value."""
1568 return self._variable.get()
1571 def _set_value(self, val):
1572 """Set new scale value."""
1573 self._variable.set(val)
1576 value = property(_get_value, _set_value)
1579 class OptionMenu(Menubutton):
1580 """Themed OptionMenu, based after Tkinter's OptionMenu, which allows
1581 the user to select a value from a menu."""
1583 def __init__(self, master, variable, default=None, *values, **kwargs):
1584 """Construct a themed OptionMenu widget with master as the parent,
1585 the resource textvariable set to variable, the initially selected
1586 value specified by the default parameter, the menu values given by
1587 *values and additional keywords.
1589 WIDGET-SPECIFIC OPTIONS
1591 style: stylename
1592 Menubutton style.
1593 direction: 'above', 'below', 'left', 'right', or 'flush'
1594 Menubutton direction.
1595 command: callback
1596 A callback that will be invoked after selecting an item.
1598 kw = {'textvariable': variable, 'style': kwargs.pop('style', None),
1599 'direction': kwargs.pop('direction', None)}
1600 Menubutton.__init__(self, master, **kw)
1601 self['menu'] = Tkinter.Menu(self, tearoff=False)
1603 self._variable = variable
1604 self._callback = kwargs.pop('command', None)
1605 if kwargs:
1606 raise Tkinter.TclError('unknown option -%s' % (
1607 kwargs.iterkeys().next()))
1609 self.set_menu(default, *values)
1612 def __getitem__(self, item):
1613 if item == 'menu':
1614 return self.nametowidget(Menubutton.__getitem__(self, item))
1616 return Menubutton.__getitem__(self, item)
1619 def set_menu(self, default=None, *values):
1620 """Build a new menu of radiobuttons with *values and optionally
1621 a default value."""
1622 menu = self['menu']
1623 menu.delete(0, 'end')
1624 for val in values:
1625 menu.add_radiobutton(label=val,
1626 command=Tkinter._setit(self._variable, val, self._callback))
1628 if default:
1629 self._variable.set(default)
1632 def destroy(self):
1633 """Destroy this widget and its associated variable."""
1634 del self._variable
1635 Menubutton.destroy(self)