blob: ab7aeb15e8ff222e3b99bc56badba70ea0f17098 [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",
Alan D Moorea48e78a2018-02-08 18:03:55 -060022 "Separator", "Sizegrip", "Spinbox", "Style", "Treeview",
Guilherme Polo5f238482009-01-28 14:41:10 +000023 # 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 Storchaka8f0a1d02014-09-06 22:47:58 +030029from tkinter import _flatten, _join, _stringify, _splitdict
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:
Serhiy Storchakab1396522013-01-15 17:56:08 +020084 if len(state) == 1:
85 # if it is empty (something that evaluates to False), then
86 # format it to Tcl code to denote the "normal" state
87 state = state[0] or ''
88 else:
89 # group multiple states
90 state = ' '.join(state) # raise TypeError if not str
91 opt_val.append(state)
92 if val is not None:
93 opt_val.append(val)
94 return opt_val
95
Guilherme Polo5f238482009-01-28 14:41:10 +000096def _format_mapdict(mapdict, script=False):
97 """Formats mapdict to pass it to tk.call.
98
99 E.g. (script=False):
100 {'expand': [('active', 'selected', 'grey'), ('focus', [1, 2, 3, 4])]}
101
102 returns:
103
104 ('-expand', '{active selected} grey focus {1, 2, 3, 4}')"""
Guilherme Polo5f238482009-01-28 14:41:10 +0000105
106 opts = []
107 for opt, value in mapdict.items():
Serhiy Storchakab1396522013-01-15 17:56:08 +0200108 opts.extend(("-%s" % opt,
109 _format_optvalue(_mapdict_values(value), script)))
Guilherme Polo5f238482009-01-28 14:41:10 +0000110
111 return _flatten(opts)
112
113def _format_elemcreate(etype, script=False, *args, **kw):
114 """Formats args and kw according to the given element factory etype."""
115 spec = None
116 opts = ()
117 if etype in ("image", "vsapi"):
118 if etype == "image": # define an element based on an image
119 # first arg should be the default image name
120 iname = args[0]
121 # next args, if any, are statespec/value pairs which is almost
122 # a mapdict, but we just need the value
Serhiy Storchakab1396522013-01-15 17:56:08 +0200123 imagespec = _join(_mapdict_values(args[1:]))
Guilherme Polo5f238482009-01-28 14:41:10 +0000124 spec = "%s %s" % (iname, imagespec)
125
126 else:
127 # define an element whose visual appearance is drawn using the
128 # Microsoft Visual Styles API which is responsible for the
129 # themed styles on Windows XP and Vista.
130 # Availability: Tk 8.6, Windows XP and Vista.
131 class_name, part_id = args[:2]
Serhiy Storchakab1396522013-01-15 17:56:08 +0200132 statemap = _join(_mapdict_values(args[2:]))
Guilherme Polo5f238482009-01-28 14:41:10 +0000133 spec = "%s %s %s" % (class_name, part_id, statemap)
134
135 opts = _format_optdict(kw, script)
136
137 elif etype == "from": # clone an element
138 # it expects a themename and optionally an element to clone from,
139 # otherwise it will clone {} (empty element)
140 spec = args[0] # theme name
141 if len(args) > 1: # elementfrom specified
Serhiy Storchakab1396522013-01-15 17:56:08 +0200142 opts = (_format_optvalue(args[1], script),)
Guilherme Polo5f238482009-01-28 14:41:10 +0000143
144 if script:
145 spec = '{%s}' % spec
Serhiy Storchakab1396522013-01-15 17:56:08 +0200146 opts = ' '.join(opts)
Guilherme Polo5f238482009-01-28 14:41:10 +0000147
148 return spec, opts
149
150def _format_layoutlist(layout, indent=0, indent_size=2):
151 """Formats a layout list so we can pass the result to ttk::style
Martin Panter69332c12016-08-04 13:07:31 +0000152 layout and ttk::style settings. Note that the layout doesn't have to
Guilherme Polo5f238482009-01-28 14:41:10 +0000153 be a list necessarily.
154
155 E.g.:
156 [("Menubutton.background", None),
157 ("Menubutton.button", {"children":
158 [("Menubutton.focus", {"children":
159 [("Menubutton.padding", {"children":
160 [("Menubutton.label", {"side": "left", "expand": 1})]
161 })]
162 })]
163 }),
164 ("Menubutton.indicator", {"side": "right"})
165 ]
166
167 returns:
168
169 Menubutton.background
170 Menubutton.button -children {
171 Menubutton.focus -children {
172 Menubutton.padding -children {
173 Menubutton.label -side left -expand 1
174 }
175 }
176 }
177 Menubutton.indicator -side right"""
178 script = []
179
180 for layout_elem in layout:
181 elem, opts = layout_elem
182 opts = opts or {}
Serhiy Storchakab1396522013-01-15 17:56:08 +0200183 fopts = ' '.join(_format_optdict(opts, True, ("children",)))
Guilherme Polo5f238482009-01-28 14:41:10 +0000184 head = "%s%s%s" % (' ' * indent, elem, (" %s" % fopts) if fopts else '')
185
186 if "children" in opts:
187 script.append(head + " -children {")
188 indent += indent_size
189 newscript, indent = _format_layoutlist(opts['children'], indent,
190 indent_size)
191 script.append(newscript)
192 indent -= indent_size
193 script.append('%s}' % (' ' * indent))
194 else:
195 script.append(head)
196
197 return '\n'.join(script), indent
198
199def _script_from_settings(settings):
200 """Returns an appropriate script, based on settings, according to
201 theme_settings definition to be used by theme_settings and
202 theme_create."""
203 script = []
204 # a script will be generated according to settings passed, which
205 # will then be evaluated by Tcl
206 for name, opts in settings.items():
207 # will format specific keys according to Tcl code
208 if opts.get('configure'): # format 'configure'
Serhiy Storchakab1396522013-01-15 17:56:08 +0200209 s = ' '.join(_format_optdict(opts['configure'], True))
Guilherme Polo5f238482009-01-28 14:41:10 +0000210 script.append("ttk::style configure %s %s;" % (name, s))
211
212 if opts.get('map'): # format 'map'
Serhiy Storchakab1396522013-01-15 17:56:08 +0200213 s = ' '.join(_format_mapdict(opts['map'], True))
Guilherme Polo5f238482009-01-28 14:41:10 +0000214 script.append("ttk::style map %s %s;" % (name, s))
215
216 if 'layout' in opts: # format 'layout' which may be empty
217 if not opts['layout']:
218 s = 'null' # could be any other word, but this one makes sense
219 else:
220 s, _ = _format_layoutlist(opts['layout'])
221 script.append("ttk::style layout %s {\n%s\n}" % (name, s))
222
223 if opts.get('element create'): # format 'element create'
224 eopts = opts['element create']
225 etype = eopts[0]
226
227 # find where args end, and where kwargs start
228 argc = 1 # etype was the first one
229 while argc < len(eopts) and not hasattr(eopts[argc], 'items'):
230 argc += 1
231
232 elemargs = eopts[1:argc]
233 elemkw = eopts[argc] if argc < len(eopts) and eopts[argc] else {}
234 spec, opts = _format_elemcreate(etype, True, *elemargs, **elemkw)
235
236 script.append("ttk::style element create %s %s %s %s" % (
237 name, etype, spec, opts))
238
239 return '\n'.join(script)
240
Guilherme Polo5f238482009-01-28 14:41:10 +0000241def _list_from_statespec(stuple):
242 """Construct a list from the given statespec tuple according to the
243 accepted statespec accepted by _format_mapdict."""
Serhiy Storchakadd844a22020-11-22 22:48:52 +0200244 if isinstance(stuple, str):
245 return stuple
246 result = []
247 it = iter(stuple)
248 for state, val in zip(it, it):
249 if hasattr(state, 'typename'): # this is a Tcl object
250 state = str(state).split()
251 elif isinstance(state, str):
252 state = state.split()
253 elif not isinstance(state, (tuple, list)):
254 state = (state,)
255 if hasattr(val, 'typename'):
Guilherme Polo5f238482009-01-28 14:41:10 +0000256 val = str(val)
Serhiy Storchakadd844a22020-11-22 22:48:52 +0200257 result.append((*state, val))
Guilherme Polo5f238482009-01-28 14:41:10 +0000258
Serhiy Storchakadd844a22020-11-22 22:48:52 +0200259 return result
Guilherme Polo5f238482009-01-28 14:41:10 +0000260
Serhiy Storchakab49eff22014-05-28 18:38:27 +0300261def _list_from_layouttuple(tk, ltuple):
Guilherme Polo5f238482009-01-28 14:41:10 +0000262 """Construct a list from the tuple returned by ttk::layout, this is
263 somewhat the reverse of _format_layoutlist."""
Serhiy Storchaka8381f902014-06-01 11:21:55 +0300264 ltuple = tk.splitlist(ltuple)
Guilherme Polo5f238482009-01-28 14:41:10 +0000265 res = []
266
267 indx = 0
268 while indx < len(ltuple):
269 name = ltuple[indx]
270 opts = {}
271 res.append((name, opts))
272 indx += 1
273
274 while indx < len(ltuple): # grab name's options
275 opt, val = ltuple[indx:indx + 2]
276 if not opt.startswith('-'): # found next name
277 break
278
279 opt = opt[1:] # remove the '-' from the option
280 indx += 2
281
282 if opt == 'children':
Serhiy Storchakab49eff22014-05-28 18:38:27 +0300283 val = _list_from_layouttuple(tk, val)
Guilherme Polo5f238482009-01-28 14:41:10 +0000284
285 opts[opt] = val
286
287 return res
288
Serhiy Storchakab49eff22014-05-28 18:38:27 +0300289def _val_or_dict(tk, options, *args):
290 """Format options then call Tk command with args and options and return
Guilherme Polo5f238482009-01-28 14:41:10 +0000291 the appropriate result.
292
Martin Panter7462b6492015-11-02 03:37:02 +0000293 If no option is specified, a dict is returned. If an option is
Guilherme Polo5f238482009-01-28 14:41:10 +0000294 specified with the None value, the value for that option is returned.
295 Otherwise, the function just sets the passed options and the caller
296 shouldn't be expecting a return value anyway."""
297 options = _format_optdict(options)
Serhiy Storchakab49eff22014-05-28 18:38:27 +0300298 res = tk.call(*(args + options))
Guilherme Polo5f238482009-01-28 14:41:10 +0000299
300 if len(options) % 2: # option specified without a value, return its value
301 return res
302
Serhiy Storchaka8f0a1d02014-09-06 22:47:58 +0300303 return _splitdict(tk, res, conv=_tclobj_to_py)
Guilherme Polo5f238482009-01-28 14:41:10 +0000304
305def _convert_stringval(value):
306 """Converts a value to, hopefully, a more appropriate Python object."""
307 value = str(value)
308 try:
309 value = int(value)
310 except (ValueError, TypeError):
311 pass
312
313 return value
314
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200315def _to_number(x):
316 if isinstance(x, str):
317 if '.' in x:
318 x = float(x)
319 else:
320 x = int(x)
321 return x
322
Serhiy Storchaka8f0a1d02014-09-06 22:47:58 +0300323def _tclobj_to_py(val):
324 """Return value converted from Tcl object to Python object."""
325 if val and hasattr(val, '__len__') and not isinstance(val, str):
326 if getattr(val[0], 'typename', None) == 'StateSpec':
327 val = _list_from_statespec(val)
328 else:
329 val = list(map(_convert_stringval, val))
330
331 elif hasattr(val, 'typename'): # some other (single) Tcl object
332 val = _convert_stringval(val)
333
334 return val
335
Guilherme Polo5f238482009-01-28 14:41:10 +0000336def tclobjs_to_py(adict):
337 """Returns adict with its values converted from Tcl objects to Python
338 objects."""
339 for opt, val in adict.items():
Serhiy Storchaka8f0a1d02014-09-06 22:47:58 +0300340 adict[opt] = _tclobj_to_py(val)
Guilherme Polo5f238482009-01-28 14:41:10 +0000341
342 return adict
343
Guilherme Poloa91790a2009-02-09 20:40:42 +0000344def setup_master(master=None):
345 """If master is not None, itself is returned. If master is None,
346 the default master is returned if there is one, otherwise a new
347 master is created and returned.
348
349 If it is not allowed to use the default root and master is None,
350 RuntimeError is raised."""
351 if master is None:
Serhiy Storchaka3d569fd2020-12-19 12:17:08 +0200352 master = tkinter._get_default_root()
Guilherme Poloa91790a2009-02-09 20:40:42 +0000353 return master
354
Guilherme Polo5f238482009-01-28 14:41:10 +0000355
356class Style(object):
357 """Manipulate style database."""
358
359 _name = "ttk::style"
360
361 def __init__(self, master=None):
Guilherme Poloa91790a2009-02-09 20:40:42 +0000362 master = setup_master(master)
Guilherme Polofa8fba92009-02-07 02:33:47 +0000363
364 if not getattr(master, '_tile_loaded', False):
365 # Load tile now, if needed
366 _load_tile(master)
Guilherme Polo5f238482009-01-28 14:41:10 +0000367
368 self.master = master
369 self.tk = self.master.tk
370
371
372 def configure(self, style, query_opt=None, **kw):
373 """Query or sets the default value of the specified option(s) in
374 style.
375
376 Each key in kw is an option and each value is either a string or
377 a sequence identifying the value for that option."""
378 if query_opt is not None:
379 kw[query_opt] = None
Ethan Furmanad1a3412015-07-21 00:54:19 -0700380 result = _val_or_dict(self.tk, kw, self._name, "configure", style)
381 if result or query_opt:
382 return result
Guilherme Polo5f238482009-01-28 14:41:10 +0000383
384
385 def map(self, style, query_opt=None, **kw):
386 """Query or sets dynamic values of the specified option(s) in
387 style.
388
389 Each key in kw is an option and each value should be a list or a
390 tuple (usually) containing statespecs grouped in tuples, or list,
391 or something else of your preference. A statespec is compound of
392 one or more states and then a value."""
393 if query_opt is not None:
Serhiy Storchakadd844a22020-11-22 22:48:52 +0200394 result = self.tk.call(self._name, "map", style, '-%s' % query_opt)
395 return _list_from_statespec(self.tk.splitlist(result))
Guilherme Polo5f238482009-01-28 14:41:10 +0000396
Serhiy Storchakadd844a22020-11-22 22:48:52 +0200397 result = self.tk.call(self._name, "map", style, *_format_mapdict(kw))
398 return {k: _list_from_statespec(self.tk.splitlist(v))
399 for k, v in _splitdict(self.tk, result).items()}
Guilherme Polo5f238482009-01-28 14:41:10 +0000400
401
402 def lookup(self, style, option, state=None, default=None):
403 """Returns the value specified for option in style.
404
405 If state is specified it is expected to be a sequence of one
406 or more states. If the default argument is set, it is used as
407 a fallback value in case no specification for option is found."""
408 state = ' '.join(state) if state else ''
409
410 return self.tk.call(self._name, "lookup", style, '-%s' % option,
411 state, default)
412
413
414 def layout(self, style, layoutspec=None):
415 """Define the widget layout for given style. If layoutspec is
416 omitted, return the layout specification for given style.
417
418 layoutspec is expected to be a list or an object different than
419 None that evaluates to False if you want to "turn off" that style.
420 If it is a list (or tuple, or something else), each item should be
421 a tuple where the first item is the layout name and the second item
422 should have the format described below:
423
424 LAYOUTS
425
426 A layout can contain the value None, if takes no options, or
427 a dict of options specifying how to arrange the element.
428 The layout mechanism uses a simplified version of the pack
429 geometry manager: given an initial cavity, each element is
430 allocated a parcel. Valid options/values are:
431
432 side: whichside
433 Specifies which side of the cavity to place the
434 element; one of top, right, bottom or left. If
435 omitted, the element occupies the entire cavity.
436
437 sticky: nswe
438 Specifies where the element is placed inside its
439 allocated parcel.
440
441 children: [sublayout... ]
442 Specifies a list of elements to place inside the
443 element. Each element is a tuple (or other sequence)
444 where the first item is the layout name, and the other
445 is a LAYOUT."""
446 lspec = None
447 if layoutspec:
448 lspec = _format_layoutlist(layoutspec)[0]
449 elif layoutspec is not None: # will disable the layout ({}, '', etc)
450 lspec = "null" # could be any other word, but this may make sense
451 # when calling layout(style) later
452
Serhiy Storchaka8381f902014-06-01 11:21:55 +0300453 return _list_from_layouttuple(self.tk,
454 self.tk.call(self._name, "layout", style, lspec))
Guilherme Polo5f238482009-01-28 14:41:10 +0000455
456
457 def element_create(self, elementname, etype, *args, **kw):
458 """Create a new element in the current theme of given etype."""
459 spec, opts = _format_elemcreate(etype, False, *args, **kw)
460 self.tk.call(self._name, "element", "create", elementname, etype,
461 spec, *opts)
462
463
464 def element_names(self):
465 """Returns the list of elements defined in the current theme."""
Ethan Furmanad1a3412015-07-21 00:54:19 -0700466 return tuple(n.lstrip('-') for n in self.tk.splitlist(
467 self.tk.call(self._name, "element", "names")))
Guilherme Polo5f238482009-01-28 14:41:10 +0000468
469
470 def element_options(self, elementname):
471 """Return the list of elementname's options."""
Ethan Furmanad1a3412015-07-21 00:54:19 -0700472 return tuple(o.lstrip('-') for o in self.tk.splitlist(
473 self.tk.call(self._name, "element", "options", elementname)))
Guilherme Polo5f238482009-01-28 14:41:10 +0000474
475
476 def theme_create(self, themename, parent=None, settings=None):
477 """Creates a new theme.
478
479 It is an error if themename already exists. If parent is
480 specified, the new theme will inherit styles, elements and
481 layouts from the specified parent theme. If settings are present,
482 they are expected to have the same syntax used for theme_settings."""
483 script = _script_from_settings(settings) if settings else ''
484
485 if parent:
486 self.tk.call(self._name, "theme", "create", themename,
487 "-parent", parent, "-settings", script)
488 else:
489 self.tk.call(self._name, "theme", "create", themename,
490 "-settings", script)
491
492
493 def theme_settings(self, themename, settings):
494 """Temporarily sets the current theme to themename, apply specified
495 settings and then restore the previous theme.
496
497 Each key in settings is a style and each value may contain the
498 keys 'configure', 'map', 'layout' and 'element create' and they
499 are expected to have the same format as specified by the methods
500 configure, map, layout and element_create respectively."""
501 script = _script_from_settings(settings)
502 self.tk.call(self._name, "theme", "settings", themename, script)
503
504
505 def theme_names(self):
506 """Returns a list of all known themes."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200507 return self.tk.splitlist(self.tk.call(self._name, "theme", "names"))
Guilherme Polo5f238482009-01-28 14:41:10 +0000508
509
510 def theme_use(self, themename=None):
511 """If themename is None, returns the theme in use, otherwise, set
512 the current theme to themename, refreshes all widgets and emits
513 a <<ThemeChanged>> event."""
514 if themename is None:
515 # Starting on Tk 8.6, checking this global is no longer needed
516 # since it allows doing self.tk.call(self._name, "theme", "use")
517 return self.tk.eval("return $ttk::currentTheme")
518
519 # using "ttk::setTheme" instead of "ttk::style theme use" causes
520 # the variable currentTheme to be updated, also, ttk::setTheme calls
521 # "ttk::style theme use" in order to change theme.
522 self.tk.call("ttk::setTheme", themename)
523
524
525class Widget(tkinter.Widget):
526 """Base class for Tk themed widgets."""
527
528 def __init__(self, master, widgetname, kw=None):
529 """Constructs a Ttk Widget with the parent master.
530
531 STANDARD OPTIONS
532
533 class, cursor, takefocus, style
534
535 SCROLLABLE WIDGET OPTIONS
536
537 xscrollcommand, yscrollcommand
538
539 LABEL WIDGET OPTIONS
540
541 text, textvariable, underline, image, compound, width
542
543 WIDGET STATES
544
545 active, disabled, focus, pressed, selected, background,
546 readonly, alternate, invalid
547 """
Guilherme Poloa91790a2009-02-09 20:40:42 +0000548 master = setup_master(master)
Guilherme Polofa8fba92009-02-07 02:33:47 +0000549 if not getattr(master, '_tile_loaded', False):
550 # Load tile now, if needed
551 _load_tile(master)
Guilherme Polo5f238482009-01-28 14:41:10 +0000552 tkinter.Widget.__init__(self, master, widgetname, kw=kw)
553
554
555 def identify(self, x, y):
556 """Returns the name of the element at position x, y, or the empty
557 string if the point does not lie within any element.
558
559 x and y are pixel coordinates relative to the widget."""
560 return self.tk.call(self._w, "identify", x, y)
561
562
563 def instate(self, statespec, callback=None, *args, **kw):
564 """Test the widget's state.
565
566 If callback is not specified, returns True if the widget state
567 matches statespec and False otherwise. If callback is specified,
568 then it will be invoked with *args, **kw if the widget state
569 matches statespec. statespec is expected to be a sequence."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200570 ret = self.tk.getboolean(
571 self.tk.call(self._w, "instate", ' '.join(statespec)))
Guilherme Polo5f238482009-01-28 14:41:10 +0000572 if ret and callback:
573 return callback(*args, **kw)
574
Serhiy Storchaka9a6e2012015-04-04 12:43:01 +0300575 return ret
Guilherme Polo5f238482009-01-28 14:41:10 +0000576
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."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200670 return self._getints(self.tk.call(self._w, "bbox", index))
Guilherme Polo5f238482009-01-28 14:41:10 +0000671
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."""
Serhiy Storchaka9a6e2012015-04-04 12:43:01 +0300683 return self.tk.getboolean(self.tk.call(self._w, "validate"))
Guilherme Polo5f238482009-01-28 14:41:10 +0000684
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."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200710 if newindex is None:
711 return self.tk.getint(self.tk.call(self._w, "current"))
Guilherme Polo5f238482009-01-28 14:41:10 +0000712 return self.tk.call(self._w, "current", newindex)
713
714
715 def set(self, value):
716 """Sets the value of the combobox to value."""
717 self.tk.call(self._w, "set", value)
718
719
720class Frame(Widget):
721 """Ttk Frame widget is a container, used to group other widgets
722 together."""
723
724 def __init__(self, master=None, **kw):
725 """Construct a Ttk Frame with parent master.
726
727 STANDARD OPTIONS
728
729 class, cursor, style, takefocus
730
731 WIDGET-SPECIFIC OPTIONS
732
733 borderwidth, relief, padding, width, height
734 """
735 Widget.__init__(self, master, "ttk::frame", kw)
736
737
738class Label(Widget):
739 """Ttk Label widget displays a textual label and/or image."""
740
741 def __init__(self, master=None, **kw):
742 """Construct a Ttk Label with parent master.
743
744 STANDARD OPTIONS
745
746 class, compound, cursor, image, style, takefocus, text,
747 textvariable, underline, width
748
749 WIDGET-SPECIFIC OPTIONS
750
751 anchor, background, font, foreground, justify, padding,
752 relief, text, wraplength
753 """
754 Widget.__init__(self, master, "ttk::label", kw)
755
756
757class Labelframe(Widget):
758 """Ttk Labelframe widget is a container used to group other widgets
759 together. It has an optional label, which may be a plain text string
760 or another widget."""
761
762 def __init__(self, master=None, **kw):
763 """Construct a Ttk Labelframe with parent master.
764
765 STANDARD OPTIONS
766
767 class, cursor, style, takefocus
768
769 WIDGET-SPECIFIC OPTIONS
770 labelanchor, text, underline, padding, labelwidget, width,
771 height
772 """
773 Widget.__init__(self, master, "ttk::labelframe", kw)
774
775LabelFrame = Labelframe # tkinter name compatibility
776
777
778class Menubutton(Widget):
779 """Ttk Menubutton widget displays a textual label and/or image, and
780 displays a menu when pressed."""
781
782 def __init__(self, master=None, **kw):
783 """Construct a Ttk Menubutton with parent master.
784
785 STANDARD OPTIONS
786
787 class, compound, cursor, image, state, style, takefocus,
788 text, textvariable, underline, width
789
790 WIDGET-SPECIFIC OPTIONS
791
792 direction, menu
793 """
794 Widget.__init__(self, master, "ttk::menubutton", kw)
795
796
797class Notebook(Widget):
798 """Ttk Notebook widget manages a collection of windows and displays
799 a single one at a time. Each child window is associated with a tab,
800 which the user may select to change the currently-displayed window."""
801
802 def __init__(self, master=None, **kw):
803 """Construct a Ttk Notebook with parent master.
804
805 STANDARD OPTIONS
806
807 class, cursor, style, takefocus
808
809 WIDGET-SPECIFIC OPTIONS
810
811 height, padding, width
812
813 TAB OPTIONS
814
815 state, sticky, padding, text, image, compound, underline
816
817 TAB IDENTIFIERS (tab_id)
818
819 The tab_id argument found in several methods may take any of
820 the following forms:
821
822 * An integer between zero and the number of tabs
823 * The name of a child window
824 * A positional specification of the form "@x,y", which
825 defines the tab
826 * The string "current", which identifies the
827 currently-selected tab
828 * The string "end", which returns the number of tabs (only
829 valid for method index)
830 """
831 Widget.__init__(self, master, "ttk::notebook", kw)
832
833
834 def add(self, child, **kw):
835 """Adds a new tab to the notebook.
836
837 If window is currently managed by the notebook but hidden, it is
838 restored to its previous position."""
839 self.tk.call(self._w, "add", child, *(_format_optdict(kw)))
840
841
842 def forget(self, tab_id):
843 """Removes the tab specified by tab_id, unmaps and unmanages the
844 associated window."""
845 self.tk.call(self._w, "forget", tab_id)
846
847
848 def hide(self, tab_id):
849 """Hides the tab specified by tab_id.
850
851 The tab will not be displayed, but the associated window remains
852 managed by the notebook and its configuration remembered. Hidden
853 tabs may be restored with the add command."""
854 self.tk.call(self._w, "hide", tab_id)
855
856
857 def identify(self, x, y):
858 """Returns the name of the tab element at position x, y, or the
859 empty string if none."""
860 return self.tk.call(self._w, "identify", x, y)
861
862
863 def index(self, tab_id):
864 """Returns the numeric index of the tab specified by tab_id, or
865 the total number of tabs if tab_id is the string "end"."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200866 return self.tk.getint(self.tk.call(self._w, "index", tab_id))
Guilherme Polo5f238482009-01-28 14:41:10 +0000867
868
869 def insert(self, pos, child, **kw):
870 """Inserts a pane at the specified position.
871
872 pos is either the string end, an integer index, or the name of
873 a managed child. If child is already managed by the notebook,
874 moves it to the specified position."""
875 self.tk.call(self._w, "insert", pos, child, *(_format_optdict(kw)))
876
877
878 def select(self, tab_id=None):
879 """Selects the specified tab.
880
881 The associated child window will be displayed, and the
882 previously-selected window (if different) is unmapped. If tab_id
883 is omitted, returns the widget name of the currently selected
884 pane."""
885 return self.tk.call(self._w, "select", tab_id)
886
887
888 def tab(self, tab_id, option=None, **kw):
889 """Query or modify the options of the specific tab_id.
890
891 If kw is not given, returns a dict of the tab option values. If option
892 is specified, returns the value of that option. Otherwise, sets the
893 options to the corresponding values."""
894 if option is not None:
895 kw[option] = None
Serhiy Storchakab49eff22014-05-28 18:38:27 +0300896 return _val_or_dict(self.tk, kw, self._w, "tab", tab_id)
Guilherme Polo5f238482009-01-28 14:41:10 +0000897
898
899 def tabs(self):
900 """Returns a list of windows managed by the notebook."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200901 return self.tk.splitlist(self.tk.call(self._w, "tabs") or ())
Guilherme Polo5f238482009-01-28 14:41:10 +0000902
903
904 def enable_traversal(self):
905 """Enable keyboard traversal for a toplevel window containing
906 this notebook.
907
908 This will extend the bindings for the toplevel window containing
909 this notebook as follows:
910
911 Control-Tab: selects the tab following the currently selected
912 one
913
914 Shift-Control-Tab: selects the tab preceding the currently
915 selected one
916
917 Alt-K: where K is the mnemonic (underlined) character of any
918 tab, will select that tab.
919
920 Multiple notebooks in a single toplevel may be enabled for
921 traversal, including nested notebooks. However, notebook traversal
922 only works properly if all panes are direct children of the
923 notebook."""
924 # The only, and good, difference I see is about mnemonics, which works
925 # after calling this method. Control-Tab and Shift-Control-Tab always
926 # works (here at least).
927 self.tk.call("ttk::notebook::enableTraversal", self._w)
928
929
930class Panedwindow(Widget, tkinter.PanedWindow):
931 """Ttk Panedwindow widget displays a number of subwindows, stacked
932 either vertically or horizontally."""
933
934 def __init__(self, master=None, **kw):
935 """Construct a Ttk Panedwindow with parent master.
936
937 STANDARD OPTIONS
938
939 class, cursor, style, takefocus
940
941 WIDGET-SPECIFIC OPTIONS
942
943 orient, width, height
944
945 PANE OPTIONS
946
947 weight
948 """
949 Widget.__init__(self, master, "ttk::panedwindow", kw)
950
951
952 forget = tkinter.PanedWindow.forget # overrides Pack.forget
953
954
955 def insert(self, pos, child, **kw):
956 """Inserts a pane at the specified positions.
957
958 pos is either the string end, and integer index, or the name
959 of a child. If child is already managed by the paned window,
960 moves it to the specified position."""
961 self.tk.call(self._w, "insert", pos, child, *(_format_optdict(kw)))
962
963
964 def pane(self, pane, option=None, **kw):
965 """Query or modify the options of the specified pane.
966
967 pane is either an integer index or the name of a managed subwindow.
968 If kw is not given, returns a dict of the pane option values. If
969 option is specified then the value for that option is returned.
Ezio Melotti42da6632011-03-15 05:18:48 +0200970 Otherwise, sets the options to the corresponding values."""
Guilherme Polo5f238482009-01-28 14:41:10 +0000971 if option is not None:
972 kw[option] = None
Serhiy Storchakab49eff22014-05-28 18:38:27 +0300973 return _val_or_dict(self.tk, kw, self._w, "pane", pane)
Guilherme Polo5f238482009-01-28 14:41:10 +0000974
975
976 def sashpos(self, index, newpos=None):
977 """If newpos is specified, sets the position of sash number index.
978
979 May adjust the positions of adjacent sashes to ensure that
980 positions are monotonically increasing. Sash positions are further
981 constrained to be between 0 and the total size of the widget.
982
983 Returns the new position of sash number index."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +0200984 return self.tk.getint(self.tk.call(self._w, "sashpos", index, newpos))
Guilherme Polo5f238482009-01-28 14:41:10 +0000985
986PanedWindow = Panedwindow # tkinter name compatibility
987
988
989class Progressbar(Widget):
990 """Ttk Progressbar widget shows the status of a long-running
991 operation. They can operate in two modes: determinate mode shows the
992 amount completed relative to the total amount of work to be done, and
993 indeterminate mode provides an animated display to let the user know
994 that something is happening."""
995
996 def __init__(self, master=None, **kw):
997 """Construct a Ttk Progressbar with parent master.
998
999 STANDARD OPTIONS
1000
1001 class, cursor, style, takefocus
1002
1003 WIDGET-SPECIFIC OPTIONS
1004
1005 orient, length, mode, maximum, value, variable, phase
1006 """
1007 Widget.__init__(self, master, "ttk::progressbar", kw)
1008
1009
1010 def start(self, interval=None):
1011 """Begin autoincrement mode: schedules a recurring timer event
1012 that calls method step every interval milliseconds.
1013
Martin Panter46f50722016-05-26 05:35:26 +00001014 interval defaults to 50 milliseconds (20 steps/second) if omitted."""
Guilherme Polo5f238482009-01-28 14:41:10 +00001015 self.tk.call(self._w, "start", interval)
1016
1017
1018 def step(self, amount=None):
1019 """Increments the value option by amount.
1020
1021 amount defaults to 1.0 if omitted."""
1022 self.tk.call(self._w, "step", amount)
1023
1024
1025 def stop(self):
1026 """Stop autoincrement mode: cancels any recurring timer event
1027 initiated by start."""
1028 self.tk.call(self._w, "stop")
1029
1030
1031class Radiobutton(Widget):
1032 """Ttk Radiobutton widgets are used in groups to show or change a
1033 set of mutually-exclusive options."""
1034
1035 def __init__(self, master=None, **kw):
1036 """Construct a Ttk Radiobutton with parent master.
1037
1038 STANDARD OPTIONS
1039
1040 class, compound, cursor, image, state, style, takefocus,
1041 text, textvariable, underline, width
1042
1043 WIDGET-SPECIFIC OPTIONS
1044
1045 command, value, variable
1046 """
1047 Widget.__init__(self, master, "ttk::radiobutton", kw)
1048
1049
1050 def invoke(self):
1051 """Sets the option variable to the option value, selects the
1052 widget, and invokes the associated command.
1053
1054 Returns the result of the command, or an empty string if
1055 no command is specified."""
1056 return self.tk.call(self._w, "invoke")
1057
1058
1059class Scale(Widget, tkinter.Scale):
1060 """Ttk Scale widget is typically used to control the numeric value of
1061 a linked variable that varies uniformly over some range."""
1062
1063 def __init__(self, master=None, **kw):
1064 """Construct a Ttk Scale with parent master.
1065
1066 STANDARD OPTIONS
1067
1068 class, cursor, style, takefocus
1069
1070 WIDGET-SPECIFIC OPTIONS
1071
1072 command, from, length, orient, to, value, variable
1073 """
1074 Widget.__init__(self, master, "ttk::scale", kw)
1075
1076
1077 def configure(self, cnf=None, **kw):
1078 """Modify or query scale options.
1079
1080 Setting a value for any of the "from", "from_" or "to" options
1081 generates a <<RangeChanged>> event."""
Terry Jan Reedy5ea7bb22020-01-05 11:23:58 -05001082 retval = Widget.configure(self, cnf, **kw)
1083 if not isinstance(cnf, (type(None), str)):
Guilherme Polo5f238482009-01-28 14:41:10 +00001084 kw.update(cnf)
Guilherme Polo5f238482009-01-28 14:41:10 +00001085 if any(['from' in kw, 'from_' in kw, 'to' in kw]):
1086 self.event_generate('<<RangeChanged>>')
Terry Jan Reedy5ea7bb22020-01-05 11:23:58 -05001087 return retval
Guilherme Polo5f238482009-01-28 14:41:10 +00001088
1089
1090 def get(self, x=None, y=None):
1091 """Get the current value of the value option, or the value
1092 corresponding to the coordinates x, y if they are specified.
1093
1094 x and y are pixel coordinates relative to the scale widget
1095 origin."""
1096 return self.tk.call(self._w, 'get', x, y)
1097
1098
1099class Scrollbar(Widget, tkinter.Scrollbar):
1100 """Ttk Scrollbar controls the viewport of a scrollable widget."""
1101
1102 def __init__(self, master=None, **kw):
1103 """Construct a Ttk Scrollbar with parent master.
1104
1105 STANDARD OPTIONS
1106
1107 class, cursor, style, takefocus
1108
1109 WIDGET-SPECIFIC OPTIONS
1110
1111 command, orient
1112 """
1113 Widget.__init__(self, master, "ttk::scrollbar", kw)
1114
1115
1116class Separator(Widget):
1117 """Ttk Separator widget displays a horizontal or vertical separator
1118 bar."""
1119
1120 def __init__(self, master=None, **kw):
1121 """Construct a Ttk Separator with parent master.
1122
1123 STANDARD OPTIONS
1124
1125 class, cursor, style, takefocus
1126
1127 WIDGET-SPECIFIC OPTIONS
1128
1129 orient
1130 """
1131 Widget.__init__(self, master, "ttk::separator", kw)
1132
1133
1134class Sizegrip(Widget):
1135 """Ttk Sizegrip allows the user to resize the containing toplevel
1136 window by pressing and dragging the grip."""
1137
1138 def __init__(self, master=None, **kw):
1139 """Construct a Ttk Sizegrip with parent master.
1140
1141 STANDARD OPTIONS
1142
1143 class, cursor, state, style, takefocus
1144 """
1145 Widget.__init__(self, master, "ttk::sizegrip", kw)
1146
1147
Alan D Moorea48e78a2018-02-08 18:03:55 -06001148class Spinbox(Entry):
1149 """Ttk Spinbox is an Entry with increment and decrement arrows
1150
1151 It is commonly used for number entry or to select from a list of
1152 string values.
1153 """
1154
1155 def __init__(self, master=None, **kw):
1156 """Construct a Ttk Spinbox widget with the parent master.
1157
1158 STANDARD OPTIONS
1159
1160 class, cursor, style, takefocus, validate,
1161 validatecommand, xscrollcommand, invalidcommand
1162
1163 WIDGET-SPECIFIC OPTIONS
1164
1165 to, from_, increment, values, wrap, format, command
1166 """
1167 Entry.__init__(self, master, "ttk::spinbox", **kw)
1168
1169
1170 def set(self, value):
1171 """Sets the value of the Spinbox to value."""
1172 self.tk.call(self._w, "set", value)
1173
1174
Guilherme Polo1fff0082009-08-14 15:05:30 +00001175class Treeview(Widget, tkinter.XView, tkinter.YView):
Guilherme Polo5f238482009-01-28 14:41:10 +00001176 """Ttk Treeview widget displays a hierarchical collection of items.
1177
1178 Each item has a textual label, an optional image, and an optional list
1179 of data values. The data values are displayed in successive columns
1180 after the tree label."""
1181
1182 def __init__(self, master=None, **kw):
1183 """Construct a Ttk Treeview with parent master.
1184
1185 STANDARD OPTIONS
1186
1187 class, cursor, style, takefocus, xscrollcommand,
1188 yscrollcommand
1189
1190 WIDGET-SPECIFIC OPTIONS
1191
1192 columns, displaycolumns, height, padding, selectmode, show
1193
1194 ITEM OPTIONS
1195
1196 text, image, values, open, tags
1197
1198 TAG OPTIONS
1199
1200 foreground, background, font, image
1201 """
1202 Widget.__init__(self, master, "ttk::treeview", kw)
1203
1204
1205 def bbox(self, item, column=None):
1206 """Returns the bounding box (relative to the treeview widget's
1207 window) of the specified item in the form x y width height.
1208
1209 If column is specified, returns the bounding box of that cell.
1210 If the item is not visible (i.e., if it is a descendant of a
1211 closed item or is scrolled offscreen), returns an empty string."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +02001212 return self._getints(self.tk.call(self._w, "bbox", item, column)) or ''
Guilherme Polo5f238482009-01-28 14:41:10 +00001213
1214
1215 def get_children(self, item=None):
1216 """Returns a tuple of children belonging to item.
1217
1218 If item is not specified, returns root children."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +02001219 return self.tk.splitlist(
1220 self.tk.call(self._w, "children", item or '') or ())
Guilherme Polo5f238482009-01-28 14:41:10 +00001221
1222
1223 def set_children(self, item, *newchildren):
1224 """Replaces item's child with newchildren.
1225
1226 Children present in item that are not present in newchildren
1227 are detached from tree. No items in newchildren may be an
1228 ancestor of item."""
1229 self.tk.call(self._w, "children", item, newchildren)
1230
1231
1232 def column(self, column, option=None, **kw):
1233 """Query or modify the options for the specified column.
1234
1235 If kw is not given, returns a dict of the column option values. If
1236 option is specified then the value for that option is returned.
1237 Otherwise, sets the options to the corresponding values."""
1238 if option is not None:
1239 kw[option] = None
Serhiy Storchakab49eff22014-05-28 18:38:27 +03001240 return _val_or_dict(self.tk, kw, self._w, "column", column)
Guilherme Polo5f238482009-01-28 14:41:10 +00001241
1242
1243 def delete(self, *items):
1244 """Delete all specified items and all their descendants. The root
1245 item may not be deleted."""
1246 self.tk.call(self._w, "delete", items)
1247
1248
1249 def detach(self, *items):
1250 """Unlinks all of the specified items from the tree.
1251
1252 The items and all of their descendants are still present, and may
1253 be reinserted at another point in the tree, but will not be
1254 displayed. The root item may not be detached."""
1255 self.tk.call(self._w, "detach", items)
1256
1257
1258 def exists(self, item):
Georg Brandlb6046302012-04-04 20:17:06 +02001259 """Returns True if the specified item is present in the tree,
Guilherme Polo5f238482009-01-28 14:41:10 +00001260 False otherwise."""
Serhiy Storchaka9a6e2012015-04-04 12:43:01 +03001261 return self.tk.getboolean(self.tk.call(self._w, "exists", item))
Guilherme Polo5f238482009-01-28 14:41:10 +00001262
1263
1264 def focus(self, item=None):
1265 """If item is specified, sets the focus item to item. Otherwise,
1266 returns the current focus item, or '' if there is none."""
1267 return self.tk.call(self._w, "focus", item)
1268
1269
1270 def heading(self, column, option=None, **kw):
1271 """Query or modify the heading options for the specified column.
1272
1273 If kw is not given, returns a dict of the heading option values. If
1274 option is specified then the value for that option is returned.
1275 Otherwise, sets the options to the corresponding values.
1276
1277 Valid options/values are:
1278 text: text
1279 The text to display in the column heading
1280 image: image_name
1281 Specifies an image to display to the right of the column
1282 heading
1283 anchor: anchor
1284 Specifies how the heading text should be aligned. One of
1285 the standard Tk anchor values
1286 command: callback
1287 A callback to be invoked when the heading label is
1288 pressed.
1289
1290 To configure the tree column heading, call this with column = "#0" """
1291 cmd = kw.get('command')
1292 if cmd and not isinstance(cmd, str):
1293 # callback not registered yet, do it now
1294 kw['command'] = self.master.register(cmd, self._substitute)
1295
1296 if option is not None:
1297 kw[option] = None
1298
Serhiy Storchakab49eff22014-05-28 18:38:27 +03001299 return _val_or_dict(self.tk, kw, self._w, 'heading', column)
Guilherme Polo5f238482009-01-28 14:41:10 +00001300
1301
1302 def identify(self, component, x, y):
1303 """Returns a description of the specified component under the
1304 point given by x and y, or the empty string if no such component
1305 is present at that position."""
1306 return self.tk.call(self._w, "identify", component, x, y)
1307
1308
1309 def identify_row(self, y):
1310 """Returns the item ID of the item at position y."""
1311 return self.identify("row", 0, y)
1312
1313
1314 def identify_column(self, x):
1315 """Returns the data column identifier of the cell at position x.
1316
1317 The tree column has ID #0."""
1318 return self.identify("column", x, 0)
1319
1320
1321 def identify_region(self, x, y):
1322 """Returns one of:
1323
1324 heading: Tree heading area.
1325 separator: Space between two columns headings;
1326 tree: The tree area.
1327 cell: A data cell.
1328
1329 * Availability: Tk 8.6"""
1330 return self.identify("region", x, y)
1331
1332
1333 def identify_element(self, x, y):
1334 """Returns the element at position x, y.
1335
1336 * Availability: Tk 8.6"""
1337 return self.identify("element", x, y)
1338
1339
1340 def index(self, item):
1341 """Returns the integer index of item within its parent's list
1342 of children."""
Serhiy Storchakaa21acb52014-01-07 19:27:42 +02001343 return self.tk.getint(self.tk.call(self._w, "index", item))
Guilherme Polo5f238482009-01-28 14:41:10 +00001344
1345
1346 def insert(self, parent, index, iid=None, **kw):
1347 """Creates a new item and return the item identifier of the newly
1348 created item.
1349
1350 parent is the item ID of the parent item, or the empty string
1351 to create a new top-level item. index is an integer, or the value
1352 end, specifying where in the list of parent's children to insert
1353 the new item. If index is less than or equal to zero, the new node
1354 is inserted at the beginning, if index is greater than or equal to
1355 the current number of children, it is inserted at the end. If iid
1356 is specified, it is used as the item identifier, iid must not
1357 already exist in the tree. Otherwise, a new unique identifier
1358 is generated."""
1359 opts = _format_optdict(kw)
Garvit Khatri3ab44c02018-03-26 12:32:05 +05301360 if iid is not None:
Guilherme Polo5f238482009-01-28 14:41:10 +00001361 res = self.tk.call(self._w, "insert", parent, index,
1362 "-id", iid, *opts)
1363 else:
1364 res = self.tk.call(self._w, "insert", parent, index, *opts)
1365
1366 return res
1367
1368
1369 def item(self, item, option=None, **kw):
1370 """Query or modify the options for the specified item.
1371
1372 If no options are given, a dict with options/values for the item
1373 is returned. If option is specified then the value for that option
1374 is returned. Otherwise, sets the options to the corresponding
1375 values as given by kw."""
1376 if option is not None:
1377 kw[option] = None
Serhiy Storchakab49eff22014-05-28 18:38:27 +03001378 return _val_or_dict(self.tk, kw, self._w, "item", item)
Guilherme Polo5f238482009-01-28 14:41:10 +00001379
1380
1381 def move(self, item, parent, index):
1382 """Moves item to position index in parent's list of children.
1383
1384 It is illegal to move an item under one of its descendants. If
1385 index is less than or equal to zero, item is moved to the
1386 beginning, if greater than or equal to the number of children,
1387 it is moved to the end. If item was detached it is reattached."""
1388 self.tk.call(self._w, "move", item, parent, index)
1389
1390 reattach = move # A sensible method name for reattaching detached items
1391
1392
1393 def next(self, item):
1394 """Returns the identifier of item's next sibling, or '' if item
1395 is the last child of its parent."""
1396 return self.tk.call(self._w, "next", item)
1397
1398
1399 def parent(self, item):
1400 """Returns the ID of the parent of item, or '' if item is at the
1401 top level of the hierarchy."""
1402 return self.tk.call(self._w, "parent", item)
1403
1404
1405 def prev(self, item):
1406 """Returns the identifier of item's previous sibling, or '' if
1407 item is the first child of its parent."""
1408 return self.tk.call(self._w, "prev", item)
1409
1410
1411 def see(self, item):
1412 """Ensure that item is visible.
1413
1414 Sets all of item's ancestors open option to True, and scrolls
1415 the widget if necessary so that item is within the visible
1416 portion of the tree."""
1417 self.tk.call(self._w, "see", item)
1418
1419
Serhiy Storchaka97f1ca12018-02-01 18:49:21 +02001420 def selection(self):
Serhiy Storchakab84f0292016-06-20 00:05:40 +03001421 """Returns the tuple of selected items."""
Serhiy Storchaka97f1ca12018-02-01 18:49:21 +02001422 return self.tk.splitlist(self.tk.call(self._w, "selection"))
Guilherme Polo5f238482009-01-28 14:41:10 +00001423
1424
Serhiy Storchakab84f0292016-06-20 00:05:40 +03001425 def _selection(self, selop, items):
1426 if len(items) == 1 and isinstance(items[0], (tuple, list)):
1427 items = items[0]
1428
1429 self.tk.call(self._w, "selection", selop, items)
Guilherme Polo5f238482009-01-28 14:41:10 +00001430
1431
Serhiy Storchakab84f0292016-06-20 00:05:40 +03001432 def selection_set(self, *items):
1433 """The specified items becomes the new selection."""
1434 self._selection("set", items)
Guilherme Polo5f238482009-01-28 14:41:10 +00001435
1436
Serhiy Storchakab84f0292016-06-20 00:05:40 +03001437 def selection_add(self, *items):
1438 """Add all of the specified items to the selection."""
1439 self._selection("add", items)
Guilherme Polo5f238482009-01-28 14:41:10 +00001440
1441
Serhiy Storchakab84f0292016-06-20 00:05:40 +03001442 def selection_remove(self, *items):
1443 """Remove all of the specified items from the selection."""
1444 self._selection("remove", items)
1445
1446
1447 def selection_toggle(self, *items):
1448 """Toggle the selection state of each specified item."""
1449 self._selection("toggle", items)
Guilherme Polo5f238482009-01-28 14:41:10 +00001450
1451
1452 def set(self, item, column=None, value=None):
Serhiy Storchaka8f0a1d02014-09-06 22:47:58 +03001453 """Query or set the value of given item.
1454
1455 With one argument, return a dictionary of column/value pairs
1456 for the specified item. With two arguments, return the current
1457 value of the specified column. With three arguments, set the
Guilherme Polo5f238482009-01-28 14:41:10 +00001458 value of given column in given item to the specified value."""
1459 res = self.tk.call(self._w, "set", item, column, value)
1460 if column is None and value is None:
Serhiy Storchaka8f0a1d02014-09-06 22:47:58 +03001461 return _splitdict(self.tk, res,
1462 cut_minus=False, conv=_tclobj_to_py)
Guilherme Polo5f238482009-01-28 14:41:10 +00001463 else:
1464 return res
1465
1466
1467 def tag_bind(self, tagname, sequence=None, callback=None):
1468 """Bind a callback for the given event sequence to the tag tagname.
1469 When an event is delivered to an item, the callbacks for each
1470 of the item's tags option are called."""
1471 self._bind((self._w, "tag", "bind", tagname), sequence, callback, add=0)
1472
1473
1474 def tag_configure(self, tagname, option=None, **kw):
1475 """Query or modify the options for the specified tagname.
1476
1477 If kw is not given, returns a dict of the option settings for tagname.
1478 If option is specified, returns the value for that option for the
1479 specified tagname. Otherwise, sets the options to the corresponding
1480 values for the given tagname."""
1481 if option is not None:
1482 kw[option] = None
Serhiy Storchakab49eff22014-05-28 18:38:27 +03001483 return _val_or_dict(self.tk, kw, self._w, "tag", "configure",
Guilherme Polo5f238482009-01-28 14:41:10 +00001484 tagname)
1485
1486
1487 def tag_has(self, tagname, item=None):
1488 """If item is specified, returns 1 or 0 depending on whether the
1489 specified item has the given tagname. Otherwise, returns a list of
1490 all items which have the specified tag.
1491
1492 * Availability: Tk 8.6"""
Serhiy Storchaka8e92f572014-11-07 12:02:31 +02001493 if item is None:
1494 return self.tk.splitlist(
1495 self.tk.call(self._w, "tag", "has", tagname))
1496 else:
1497 return self.tk.getboolean(
Serhiy Storchakaa21acb52014-01-07 19:27:42 +02001498 self.tk.call(self._w, "tag", "has", tagname, item))
Guilherme Polo5f238482009-01-28 14:41:10 +00001499
1500
Guilherme Polo5f238482009-01-28 14:41:10 +00001501# Extensions
1502
1503class LabeledScale(Frame):
1504 """A Ttk Scale widget with a Ttk Label widget indicating its
1505 current value.
1506
1507 The Ttk Scale can be accessed through instance.scale, and Ttk Label
1508 can be accessed through instance.label"""
1509
1510 def __init__(self, master=None, variable=None, from_=0, to=10, **kw):
Serhiy Storchaka6a7b3a72016-04-17 08:32:47 +03001511 """Construct a horizontal LabeledScale with parent master, a
Guilherme Polo5f238482009-01-28 14:41:10 +00001512 variable to be associated with the Ttk Scale widget and its range.
1513 If variable is not specified, a tkinter.IntVar is created.
1514
1515 WIDGET-SPECIFIC OPTIONS
1516
1517 compound: 'top' or 'bottom'
1518 Specifies how to display the label relative to the scale.
1519 Defaults to 'top'.
1520 """
1521 self._label_top = kw.pop('compound', 'top') == 'top'
1522
1523 Frame.__init__(self, master, **kw)
1524 self._variable = variable or tkinter.IntVar(master)
1525 self._variable.set(from_)
1526 self._last_valid = from_
1527
1528 self.label = Label(self)
1529 self.scale = Scale(self, variable=self._variable, from_=from_, to=to)
1530 self.scale.bind('<<RangeChanged>>', self._adjust)
1531
1532 # position scale and label according to the compound option
1533 scale_side = 'bottom' if self._label_top else 'top'
1534 label_side = 'top' if scale_side == 'bottom' else 'bottom'
1535 self.scale.pack(side=scale_side, fill='x')
E-Paineb9ced83cf2020-12-15 19:42:55 +00001536 # Dummy required to make frame correct height
1537 dummy = Label(self)
1538 dummy.pack(side=label_side)
1539 dummy.lower()
Guilherme Polo5f238482009-01-28 14:41:10 +00001540 self.label.place(anchor='n' if label_side == 'top' else 's')
1541
1542 # update the label as scale or variable changes
1543 self.__tracecb = self._variable.trace_variable('w', self._adjust)
1544 self.bind('<Configure>', self._adjust)
1545 self.bind('<Map>', self._adjust)
1546
1547
1548 def destroy(self):
1549 """Destroy this widget and possibly its associated variable."""
1550 try:
1551 self._variable.trace_vdelete('w', self.__tracecb)
1552 except AttributeError:
Guilherme Polo5f238482009-01-28 14:41:10 +00001553 pass
1554 else:
1555 del self._variable
Victor Stinnercd7e9c12017-08-08 19:41:21 +02001556 super().destroy()
1557 self.label = None
1558 self.scale = None
Guilherme Polo5f238482009-01-28 14:41:10 +00001559
1560
1561 def _adjust(self, *args):
1562 """Adjust the label position according to the scale."""
1563 def adjust_label():
1564 self.update_idletasks() # "force" scale redraw
1565
1566 x, y = self.scale.coords()
1567 if self._label_top:
1568 y = self.scale.winfo_y() - self.label.winfo_reqheight()
1569 else:
1570 y = self.scale.winfo_reqheight() + self.label.winfo_reqheight()
1571
1572 self.label.place_configure(x=x, y=y)
1573
Serhiy Storchakaa21acb52014-01-07 19:27:42 +02001574 from_ = _to_number(self.scale['from'])
1575 to = _to_number(self.scale['to'])
Guilherme Polo5f238482009-01-28 14:41:10 +00001576 if to < from_:
1577 from_, to = to, from_
1578 newval = self._variable.get()
1579 if not from_ <= newval <= to:
1580 # value outside range, set value back to the last valid one
1581 self.value = self._last_valid
1582 return
1583
1584 self._last_valid = newval
1585 self.label['text'] = newval
1586 self.after_idle(adjust_label)
1587
Serhiy Storchakabdf6b912017-03-19 08:40:32 +02001588 @property
1589 def value(self):
Guilherme Polo5f238482009-01-28 14:41:10 +00001590 """Return current scale value."""
1591 return self._variable.get()
1592
Serhiy Storchakabdf6b912017-03-19 08:40:32 +02001593 @value.setter
1594 def value(self, val):
Guilherme Polo5f238482009-01-28 14:41:10 +00001595 """Set new scale value."""
1596 self._variable.set(val)
1597
1598
Guilherme Polo5f238482009-01-28 14:41:10 +00001599class OptionMenu(Menubutton):
1600 """Themed OptionMenu, based after tkinter's OptionMenu, which allows
1601 the user to select a value from a menu."""
1602
1603 def __init__(self, master, variable, default=None, *values, **kwargs):
1604 """Construct a themed OptionMenu widget with master as the parent,
1605 the resource textvariable set to variable, the initially selected
1606 value specified by the default parameter, the menu values given by
1607 *values and additional keywords.
1608
1609 WIDGET-SPECIFIC OPTIONS
1610
1611 style: stylename
1612 Menubutton style.
1613 direction: 'above', 'below', 'left', 'right', or 'flush'
1614 Menubutton direction.
1615 command: callback
1616 A callback that will be invoked after selecting an item.
1617 """
1618 kw = {'textvariable': variable, 'style': kwargs.pop('style', None),
1619 'direction': kwargs.pop('direction', None)}
1620 Menubutton.__init__(self, master, **kw)
1621 self['menu'] = tkinter.Menu(self, tearoff=False)
1622
1623 self._variable = variable
1624 self._callback = kwargs.pop('command', None)
1625 if kwargs:
1626 raise tkinter.TclError('unknown option -%s' % (
1627 next(iter(kwargs.keys()))))
1628
1629 self.set_menu(default, *values)
1630
1631
1632 def __getitem__(self, item):
1633 if item == 'menu':
1634 return self.nametowidget(Menubutton.__getitem__(self, item))
1635
1636 return Menubutton.__getitem__(self, item)
1637
1638
1639 def set_menu(self, default=None, *values):
1640 """Build a new menu of radiobuttons with *values and optionally
1641 a default value."""
1642 menu = self['menu']
1643 menu.delete(0, 'end')
1644 for val in values:
1645 menu.add_radiobutton(label=val,
csabellaa568e522017-07-31 05:30:09 -04001646 command=tkinter._setit(self._variable, val, self._callback),
1647 variable=self._variable)
Guilherme Polo5f238482009-01-28 14:41:10 +00001648
1649 if default:
1650 self._variable.set(default)
1651
1652
1653 def destroy(self):
1654 """Destroy this widget and its associated variable."""
Victor Stinnercd7e9c12017-08-08 19:41:21 +02001655 try:
1656 del self._variable
1657 except AttributeError:
1658 pass
1659 super().destroy()