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