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