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", "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 bakward 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 Treeview(Widget, tkinter.XView, tkinter.YView):
   1155     """Ttk Treeview widget displays a hierarchical collection of items.
   1156 
   1157     Each item has a textual label, an optional image, and an optional list
   1158     of data values. The data values are displayed in successive columns
   1159     after the tree label."""
   1160 
   1161     def __init__(self, master=None, **kw):
   1162         """Construct a Ttk Treeview with parent master.
   1163 
   1164         STANDARD OPTIONS
   1165 
   1166             class, cursor, style, takefocus, xscrollcommand,
   1167             yscrollcommand
   1168 
   1169         WIDGET-SPECIFIC OPTIONS
   1170 
   1171             columns, displaycolumns, height, padding, selectmode, show
   1172 
   1173         ITEM OPTIONS
   1174 
   1175             text, image, values, open, tags
   1176 
   1177         TAG OPTIONS
   1178 
   1179             foreground, background, font, image
   1180         """
   1181         Widget.__init__(self, master, "ttk::treeview", kw)
   1182 
   1183 
   1184     def bbox(self, item, column=None):
   1185         """Returns the bounding box (relative to the treeview widget's
   1186         window) of the specified item in the form x y width height.
   1187 
   1188         If column is specified, returns the bounding box of that cell.
   1189         If the item is not visible (i.e., if it is a descendant of a
   1190         closed item or is scrolled offscreen), returns an empty string."""
   1191         return self._getints(self.tk.call(self._w, "bbox", item, column)) or ''
   1192 
   1193 
   1194     def get_children(self, item=None):
   1195         """Returns a tuple of children belonging to item.
   1196 
   1197         If item is not specified, returns root children."""
   1198         return self.tk.splitlist(
   1199                 self.tk.call(self._w, "children", item or '') or ())
   1200 
   1201 
   1202     def set_children(self, item, *newchildren):
   1203         """Replaces item's child with newchildren.
   1204 
   1205         Children present in item that are not present in newchildren
   1206         are detached from tree. No items in newchildren may be an
   1207         ancestor of item."""
   1208         self.tk.call(self._w, "children", item, newchildren)
   1209 
   1210 
   1211     def column(self, column, option=None, **kw):
   1212         """Query or modify the options for the specified column.
   1213 
   1214         If kw is not given, returns a dict of the column option values. If
   1215         option is specified then the value for that option is returned.
   1216         Otherwise, sets the options to the corresponding values."""
   1217         if option is not None:
   1218             kw[option] = None
   1219         return _val_or_dict(self.tk, kw, self._w, "column", column)
   1220 
   1221 
   1222     def delete(self, *items):
   1223         """Delete all specified items and all their descendants. The root
   1224         item may not be deleted."""
   1225         self.tk.call(self._w, "delete", items)
   1226 
   1227 
   1228     def detach(self, *items):
   1229         """Unlinks all of the specified items from the tree.
   1230 
   1231         The items and all of their descendants are still present, and may
   1232         be reinserted at another point in the tree, but will not be
   1233         displayed. The root item may not be detached."""
   1234         self.tk.call(self._w, "detach", items)
   1235 
   1236 
   1237     def exists(self, item):
   1238         """Returns True if the specified item is present in the tree,
   1239         False otherwise."""
   1240         return self.tk.getboolean(self.tk.call(self._w, "exists", item))
   1241 
   1242 
   1243     def focus(self, item=None):
   1244         """If item is specified, sets the focus item to item. Otherwise,
   1245         returns the current focus item, or '' if there is none."""
   1246         return self.tk.call(self._w, "focus", item)
   1247 
   1248 
   1249     def heading(self, column, option=None, **kw):
   1250         """Query or modify the heading options for the specified column.
   1251 
   1252         If kw is not given, returns a dict of the heading option values. If
   1253         option is specified then the value for that option is returned.
   1254         Otherwise, sets the options to the corresponding values.
   1255 
   1256         Valid options/values are:
   1257             text: text
   1258                 The text to display in the column heading
   1259             image: image_name
   1260                 Specifies an image to display to the right of the column
   1261                 heading
   1262             anchor: anchor
   1263                 Specifies how the heading text should be aligned. One of
   1264                 the standard Tk anchor values
   1265             command: callback
   1266                 A callback to be invoked when the heading label is
   1267                 pressed.
   1268 
   1269         To configure the tree column heading, call this with column = "#0" """
   1270         cmd = kw.get('command')
   1271         if cmd and not isinstance(cmd, str):
   1272             # callback not registered yet, do it now
   1273             kw['command'] = self.master.register(cmd, self._substitute)
   1274 
   1275         if option is not None:
   1276             kw[option] = None
   1277 
   1278         return _val_or_dict(self.tk, kw, self._w, 'heading', column)
   1279 
   1280 
   1281     def identify(self, component, x, y):
   1282         """Returns a description of the specified component under the
   1283         point given by x and y, or the empty string if no such component
   1284         is present at that position."""
   1285         return self.tk.call(self._w, "identify", component, x, y)
   1286 
   1287 
   1288     def identify_row(self, y):
   1289         """Returns the item ID of the item at position y."""
   1290         return self.identify("row", 0, y)
   1291 
   1292 
   1293     def identify_column(self, x):
   1294         """Returns the data column identifier of the cell at position x.
   1295 
   1296         The tree column has ID #0."""
   1297         return self.identify("column", x, 0)
   1298 
   1299 
   1300     def identify_region(self, x, y):
   1301         """Returns one of:
   1302 
   1303         heading: Tree heading area.
   1304         separator: Space between two columns headings;
   1305         tree: The tree area.
   1306         cell: A data cell.
   1307 
   1308         * Availability: Tk 8.6"""
   1309         return self.identify("region", x, y)
   1310 
   1311 
   1312     def identify_element(self, x, y):
   1313         """Returns the element at position x, y.
   1314 
   1315         * Availability: Tk 8.6"""
   1316         return self.identify("element", x, y)
   1317 
   1318 
   1319     def index(self, item):
   1320         """Returns the integer index of item within its parent's list
   1321         of children."""
   1322         return self.tk.getint(self.tk.call(self._w, "index", item))
   1323 
   1324 
   1325     def insert(self, parent, index, iid=None, **kw):
   1326         """Creates a new item and return the item identifier of the newly
   1327         created item.
   1328 
   1329         parent is the item ID of the parent item, or the empty string
   1330         to create a new top-level item. index is an integer, or the value
   1331         end, specifying where in the list of parent's children to insert
   1332         the new item. If index is less than or equal to zero, the new node
   1333         is inserted at the beginning, if index is greater than or equal to
   1334         the current number of children, it is inserted at the end. If iid
   1335         is specified, it is used as the item identifier, iid must not
   1336         already exist in the tree. Otherwise, a new unique identifier
   1337         is generated."""
   1338         opts = _format_optdict(kw)
   1339         if iid:
   1340             res = self.tk.call(self._w, "insert", parent, index,
   1341                 "-id", iid, *opts)
   1342         else:
   1343             res = self.tk.call(self._w, "insert", parent, index, *opts)
   1344 
   1345         return res
   1346 
   1347 
   1348     def item(self, item, option=None, **kw):
   1349         """Query or modify the options for the specified item.
   1350 
   1351         If no options are given, a dict with options/values for the item
   1352         is returned. If option is specified then the value for that option
   1353         is returned. Otherwise, sets the options to the corresponding
   1354         values as given by kw."""
   1355         if option is not None:
   1356             kw[option] = None
   1357         return _val_or_dict(self.tk, kw, self._w, "item", item)
   1358 
   1359 
   1360     def move(self, item, parent, index):
   1361         """Moves item to position index in parent's list of children.
   1362 
   1363         It is illegal to move an item under one of its descendants. If
   1364         index is less than or equal to zero, item is moved to the
   1365         beginning, if greater than or equal to the number of children,
   1366         it is moved to the end. If item was detached it is reattached."""
   1367         self.tk.call(self._w, "move", item, parent, index)
   1368 
   1369     reattach = move # A sensible method name for reattaching detached items
   1370 
   1371 
   1372     def next(self, item):
   1373         """Returns the identifier of item's next sibling, or '' if item
   1374         is the last child of its parent."""
   1375         return self.tk.call(self._w, "next", item)
   1376 
   1377 
   1378     def parent(self, item):
   1379         """Returns the ID of the parent of item, or '' if item is at the
   1380         top level of the hierarchy."""
   1381         return self.tk.call(self._w, "parent", item)
   1382 
   1383 
   1384     def prev(self, item):
   1385         """Returns the identifier of item's previous sibling, or '' if
   1386         item is the first child of its parent."""
   1387         return self.tk.call(self._w, "prev", item)
   1388 
   1389 
   1390     def see(self, item):
   1391         """Ensure that item is visible.
   1392 
   1393         Sets all of item's ancestors open option to True, and scrolls
   1394         the widget if necessary so that item is within the visible
   1395         portion of the tree."""
   1396         self.tk.call(self._w, "see", item)
   1397 
   1398 
   1399     def selection(self, selop=_sentinel, items=None):
   1400         """Returns the tuple of selected items."""
   1401         if selop is _sentinel:
   1402             selop = None
   1403         elif selop is None:
   1404             import warnings
   1405             warnings.warn(
   1406                 "The selop=None argument of selection() is deprecated "
   1407                 "and will be removed in Python 3.7",
   1408                 DeprecationWarning, 3)
   1409         elif selop in ('set', 'add', 'remove', 'toggle'):
   1410             import warnings
   1411             warnings.warn(
   1412                 "The selop argument of selection() is deprecated "
   1413                 "and will be removed in Python 3.7, "
   1414                 "use selection_%s() instead" % (selop,),
   1415                 DeprecationWarning, 3)
   1416         else:
   1417             raise TypeError('Unsupported operation')
   1418         return self.tk.splitlist(self.tk.call(self._w, "selection", selop, items))
   1419 
   1420 
   1421     def _selection(self, selop, items):
   1422         if len(items) == 1 and isinstance(items[0], (tuple, list)):
   1423             items = items[0]
   1424 
   1425         self.tk.call(self._w, "selection", selop, items)
   1426 
   1427 
   1428     def selection_set(self, *items):
   1429         """The specified items becomes the new selection."""
   1430         self._selection("set", items)
   1431 
   1432 
   1433     def selection_add(self, *items):
   1434         """Add all of the specified items to the selection."""
   1435         self._selection("add", items)
   1436 
   1437 
   1438     def selection_remove(self, *items):
   1439         """Remove all of the specified items from the selection."""
   1440         self._selection("remove", items)
   1441 
   1442 
   1443     def selection_toggle(self, *items):
   1444         """Toggle the selection state of each specified item."""
   1445         self._selection("toggle", items)
   1446 
   1447 
   1448     def set(self, item, column=None, value=None):
   1449         """Query or set the value of given item.
   1450 
   1451         With one argument, return a dictionary of column/value pairs
   1452         for the specified item. With two arguments, return the current
   1453         value of the specified column. With three arguments, set the
   1454         value of given column in given item to the specified value."""
   1455         res = self.tk.call(self._w, "set", item, column, value)
   1456         if column is None and value is None:
   1457             return _splitdict(self.tk, res,
   1458                               cut_minus=False, conv=_tclobj_to_py)
   1459         else:
   1460             return res
   1461 
   1462 
   1463     def tag_bind(self, tagname, sequence=None, callback=None):
   1464         """Bind a callback for the given event sequence to the tag tagname.
   1465         When an event is delivered to an item, the callbacks for each
   1466         of the item's tags option are called."""
   1467         self._bind((self._w, "tag", "bind", tagname), sequence, callback, add=0)
   1468 
   1469 
   1470     def tag_configure(self, tagname, option=None, **kw):
   1471         """Query or modify the options for the specified tagname.
   1472 
   1473         If kw is not given, returns a dict of the option settings for tagname.
   1474         If option is specified, returns the value for that option for the
   1475         specified tagname. Otherwise, sets the options to the corresponding
   1476         values for the given tagname."""
   1477         if option is not None:
   1478             kw[option] = None
   1479         return _val_or_dict(self.tk, kw, self._w, "tag", "configure",
   1480             tagname)
   1481 
   1482 
   1483     def tag_has(self, tagname, item=None):
   1484         """If item is specified, returns 1 or 0 depending on whether the
   1485         specified item has the given tagname. Otherwise, returns a list of
   1486         all items which have the specified tag.
   1487 
   1488         * Availability: Tk 8.6"""
   1489         if item is None:
   1490             return self.tk.splitlist(
   1491                 self.tk.call(self._w, "tag", "has", tagname))
   1492         else:
   1493             return self.tk.getboolean(
   1494                 self.tk.call(self._w, "tag", "has", tagname, item))
   1495 
   1496 
   1497 # Extensions
   1498 
   1499 class LabeledScale(Frame):
   1500     """A Ttk Scale widget with a Ttk Label widget indicating its
   1501     current value.
   1502 
   1503     The Ttk Scale can be accessed through instance.scale, and Ttk Label
   1504     can be accessed through instance.label"""
   1505 
   1506     def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
   1507         """Construct a horizontal LabeledScale with parent master, a
   1508         variable to be associated with the Ttk Scale widget and its range.
   1509         If variable is not specified, a tkinter.IntVar is created.
   1510 
   1511         WIDGET-SPECIFIC OPTIONS
   1512 
   1513             compound: 'top' or 'bottom'
   1514                 Specifies how to display the label relative to the scale.
   1515                 Defaults to 'top'.
   1516         """
   1517         self._label_top = kw.pop('compound', 'top') == 'top'
   1518 
   1519         Frame.__init__(self, master, **kw)
   1520         self._variable = variable or tkinter.IntVar(master)
   1521         self._variable.set(from_)
   1522         self._last_valid = from_
   1523 
   1524         self.label = Label(self)
   1525         self.scale = Scale(self, variable=self._variable, from_=from_, to=to)
   1526         self.scale.bind('<<RangeChanged>>', self._adjust)
   1527 
   1528         # position scale and label according to the compound option
   1529         scale_side = 'bottom' if self._label_top else 'top'
   1530         label_side = 'top' if scale_side == 'bottom' else 'bottom'
   1531         self.scale.pack(side=scale_side, fill='x')
   1532         tmp = Label(self).pack(side=label_side) # place holder
   1533         self.label.place(anchor='n' if label_side == 'top' else 's')
   1534 
   1535         # update the label as scale or variable changes
   1536         self.__tracecb = self._variable.trace_variable('w', self._adjust)
   1537         self.bind('<Configure>', self._adjust)
   1538         self.bind('<Map>', self._adjust)
   1539 
   1540 
   1541     def destroy(self):
   1542         """Destroy this widget and possibly its associated variable."""
   1543         try:
   1544             self._variable.trace_vdelete('w', self.__tracecb)
   1545         except AttributeError:
   1546             # widget has been destroyed already
   1547             pass
   1548         else:
   1549             del self._variable
   1550             Frame.destroy(self)
   1551 
   1552 
   1553     def _adjust(self, *args):
   1554         """Adjust the label position according to the scale."""
   1555         def adjust_label():
   1556             self.update_idletasks() # "force" scale redraw
   1557 
   1558             x, y = self.scale.coords()
   1559             if self._label_top:
   1560                 y = self.scale.winfo_y() - self.label.winfo_reqheight()
   1561             else:
   1562                 y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
   1563 
   1564             self.label.place_configure(x=x, y=y)
   1565 
   1566         from_ = _to_number(self.scale['from'])
   1567         to = _to_number(self.scale['to'])
   1568         if to < from_:
   1569             from_, to = to, from_
   1570         newval = self._variable.get()
   1571         if not from_ <= newval <= to:
   1572             # value outside range, set value back to the last valid one
   1573             self.value = self._last_valid
   1574             return
   1575 
   1576         self._last_valid = newval
   1577         self.label['text'] = newval
   1578         self.after_idle(adjust_label)
   1579 
   1580 
   1581     def _get_value(self):
   1582         """Return current scale value."""
   1583         return self._variable.get()
   1584 
   1585 
   1586     def _set_value(self, val):
   1587         """Set new scale value."""
   1588         self._variable.set(val)
   1589 
   1590 
   1591     value = property(_get_value, _set_value)
   1592 
   1593 
   1594 class OptionMenu(Menubutton):
   1595     """Themed OptionMenu, based after tkinter's OptionMenu, which allows
   1596     the user to select a value from a menu."""
   1597 
   1598     def __init__(self, master, variable, default=None, *values, **kwargs):
   1599         """Construct a themed OptionMenu widget with master as the parent,
   1600         the resource textvariable set to variable, the initially selected
   1601         value specified by the default parameter, the menu values given by
   1602         *values and additional keywords.
   1603 
   1604         WIDGET-SPECIFIC OPTIONS
   1605 
   1606             style: stylename
   1607                 Menubutton style.
   1608             direction: 'above', 'below', 'left', 'right', or 'flush'
   1609                 Menubutton direction.
   1610             command: callback
   1611                 A callback that will be invoked after selecting an item.
   1612         """
   1613         kw = {'textvariable': variable, 'style': kwargs.pop('style', None),
   1614               'direction': kwargs.pop('direction', None)}
   1615         Menubutton.__init__(self, master, **kw)
   1616         self['menu'] = tkinter.Menu(self, tearoff=False)
   1617 
   1618         self._variable = variable
   1619         self._callback = kwargs.pop('command', None)
   1620         if kwargs:
   1621             raise tkinter.TclError('unknown option -%s' % (
   1622                 next(iter(kwargs.keys()))))
   1623 
   1624         self.set_menu(default, *values)
   1625 
   1626 
   1627     def __getitem__(self, item):
   1628         if item == 'menu':
   1629             return self.nametowidget(Menubutton.__getitem__(self, item))
   1630 
   1631         return Menubutton.__getitem__(self, item)
   1632 
   1633 
   1634     def set_menu(self, default=None, *values):
   1635         """Build a new menu of radiobuttons with *values and optionally
   1636         a default value."""
   1637         menu = self['menu']
   1638         menu.delete(0, 'end')
   1639         for val in values:
   1640             menu.add_radiobutton(label=val,
   1641                 command=tkinter._setit(self._variable, val, self._callback))
   1642 
   1643         if default:
   1644             self._variable.set(default)
   1645 
   1646 
   1647     def destroy(self):
   1648         """Destroy this widget and its associated variable."""
   1649         del self._variable
   1650         Menubutton.destroy(self)
   1651