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