codekingpro/portable-devtools
114k
1"""Wrapper functions for Tcl/Tk.2 3Tkinter provides classes which allow the display, positioning and4control of widgets. Toplevel widgets are Tk and Toplevel. Other5widgets are Frame, Label, Entry, Text, Canvas, Button, Radiobutton,6Checkbutton, Scale, Listbox, Scrollbar, OptionMenu, Spinbox7LabelFrame and PanedWindow.8 9Properties of the widgets are specified with keyword arguments.10Keyword arguments have the same name as the corresponding options11under Tk.12 13Widgets are positioned with one of the geometry managers Place, Pack14or Grid. These managers can be called with methods place, pack, grid15available in every Widget.16 17Actions are bound to events by options (e.g. the command18keyword argument) or with the bind() method.19 20Example (Hello, World):21import tkinter22from tkinter.constants import *23tk = tkinter.Tk()24frame = tkinter.Frame(tk, relief=RIDGE, borderwidth=2)25frame.pack(fill=BOTH, expand=1)26label = tkinter.Label(frame, text="Hello, World")27label.pack(fill=X, expand=1)28button = tkinter.Button(frame, text="Exit", command=tk.destroy)29button.pack(side=BOTTOM)30tk.mainloop()31"""32 33import collections34import enum35import sys36import types37 38import _tkinter # If this fails your Python may not be configured for Tk39TclError = _tkinter.TclError40from tkinter.constants import *41import re42 43wantobjects = 144_debug = False # set to True to print executed Tcl/Tk commands45 46TkVersion = float(_tkinter.TK_VERSION)47TclVersion = float(_tkinter.TCL_VERSION)48 49READABLE = _tkinter.READABLE50WRITABLE = _tkinter.WRITABLE51EXCEPTION = _tkinter.EXCEPTION52 53 54_magic_re = re.compile(r'([\\{}])')55_space_re = re.compile(r'([\s])', re.ASCII)56 57 58def _join(value):59 """Internal function."""60 return ' '.join(map(_stringify, value))61 62 63def _stringify(value):64 """Internal function."""65 if isinstance(value, (list, tuple)):66 if len(value) == 1:67 value = _stringify(value[0])68 if _magic_re.search(value):69 value = '{%s}' % value70 else:71 value = '{%s}' % _join(value)72 else:73 if isinstance(value, bytes):74 value = str(value, 'latin1')75 else:76 value = str(value)77 if not value:78 value = '{}'79 elif _magic_re.search(value):80 # add '\' before special characters and spaces81 value = _magic_re.sub(r'\\\1', value)82 value = value.replace('\n', r'\n')83 value = _space_re.sub(r'\\\1', value)84 if value[0] == '"':85 value = '\\' + value86 elif value[0] == '"' or _space_re.search(value):87 value = '{%s}' % value88 return value89 90 91def _flatten(seq):92 """Internal function."""93 res = ()94 for item in seq:95 if isinstance(item, (tuple, list)):96 res = res + _flatten(item)97 elif item is not None:98 res = res + (item,)99 return res100 101 102try: _flatten = _tkinter._flatten103except AttributeError: pass104 105 106def _cnfmerge(cnfs):107 """Internal function."""108 if isinstance(cnfs, dict):109 return cnfs110 elif isinstance(cnfs, (type(None), str)):111 return cnfs112 else:113 cnf = {}114 for c in _flatten(cnfs):115 try:116 cnf.update(c)117 except (AttributeError, TypeError) as msg:118 print("_cnfmerge: fallback due to:", msg)119 for k, v in c.items():120 cnf[k] = v121 return cnf122 123 124try: _cnfmerge = _tkinter._cnfmerge125except AttributeError: pass126 127 128def _splitdict(tk, v, cut_minus=True, conv=None):129 """Return a properly formatted dict built from Tcl list pairs.130 131 If cut_minus is True, the supposed '-' prefix will be removed from132 keys. If conv is specified, it is used to convert values.133 134 Tcl list is expected to contain an even number of elements.135 """136 t = tk.splitlist(v)137 if len(t) % 2:138 raise RuntimeError('Tcl list representing a dict is expected '139 'to contain an even number of elements')140 it = iter(t)141 dict = {}142 for key, value in zip(it, it):143 key = str(key)144 if cut_minus and key[0] == '-':145 key = key[1:]146 if conv:147 value = conv(value)148 dict[key] = value149 return dict150 151class _VersionInfoType(collections.namedtuple('_VersionInfoType',152 ('major', 'minor', 'micro', 'releaselevel', 'serial'))):153 def __str__(self):154 if self.releaselevel == 'final':155 return f'{self.major}.{self.minor}.{self.micro}'156 else:157 return f'{self.major}.{self.minor}{self.releaselevel[0]}{self.serial}'158 159def _parse_version(version):160 import re161 m = re.fullmatch(r'(\d+)\.(\d+)([ab.])(\d+)', version)162 major, minor, releaselevel, serial = m.groups()163 major, minor, serial = int(major), int(minor), int(serial)164 if releaselevel == '.':165 micro = serial166 serial = 0167 releaselevel = 'final'168 else:169 micro = 0170 releaselevel = {'a': 'alpha', 'b': 'beta'}[releaselevel]171 return _VersionInfoType(major, minor, micro, releaselevel, serial)172 173 174@enum._simple_enum(enum.StrEnum)175class EventType:176 KeyPress = '2'177 Key = KeyPress178 KeyRelease = '3'179 ButtonPress = '4'180 Button = ButtonPress181 ButtonRelease = '5'182 Motion = '6'183 Enter = '7'184 Leave = '8'185 FocusIn = '9'186 FocusOut = '10'187 Keymap = '11' # undocumented188 Expose = '12'189 GraphicsExpose = '13' # undocumented190 NoExpose = '14' # undocumented191 Visibility = '15'192 Create = '16'193 Destroy = '17'194 Unmap = '18'195 Map = '19'196 MapRequest = '20'197 Reparent = '21'198 Configure = '22'199 ConfigureRequest = '23'200 Gravity = '24'201 ResizeRequest = '25'202 Circulate = '26'203 CirculateRequest = '27'204 Property = '28'205 SelectionClear = '29' # undocumented206 SelectionRequest = '30' # undocumented207 Selection = '31' # undocumented208 Colormap = '32'209 ClientMessage = '33' # undocumented210 Mapping = '34' # undocumented211 VirtualEvent = '35' # undocumented212 Activate = '36'213 Deactivate = '37'214 MouseWheel = '38'215 216 217class Event:218 """Container for the properties of an event.219 220 Instances of this type are generated if one of the following events occurs:221 222 KeyPress, KeyRelease - for keyboard events223 ButtonPress, ButtonRelease, Motion, Enter, Leave, MouseWheel - for mouse events224 Visibility, Unmap, Map, Expose, FocusIn, FocusOut, Circulate,225 Colormap, Gravity, Reparent, Property, Destroy, Activate,226 Deactivate - for window events.227 228 If a callback function for one of these events is registered229 using bind, bind_all, bind_class, or tag_bind, the callback is230 called with an Event as first argument. It will have the231 following attributes (in braces are the event types for which232 the attribute is valid):233 234 serial - serial number of event235 num - mouse button pressed (ButtonPress, ButtonRelease)236 focus - whether the window has the focus (Enter, Leave)237 height - height of the exposed window (Configure, Expose)238 width - width of the exposed window (Configure, Expose)239 keycode - keycode of the pressed key (KeyPress, KeyRelease)240 state - state of the event as a number (ButtonPress, ButtonRelease,241 Enter, KeyPress, KeyRelease,242 Leave, Motion)243 state - state as a string (Visibility)244 time - when the event occurred245 x - x-position of the mouse246 y - y-position of the mouse247 x_root - x-position of the mouse on the screen248 (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion)249 y_root - y-position of the mouse on the screen250 (ButtonPress, ButtonRelease, KeyPress, KeyRelease, Motion)251 char - pressed character (KeyPress, KeyRelease)252 send_event - see X/Windows documentation253 keysym - keysym of the event as a string (KeyPress, KeyRelease)254 keysym_num - keysym of the event as a number (KeyPress, KeyRelease)255 type - type of the event as a number256 widget - widget in which the event occurred257 delta - delta of wheel movement (MouseWheel)258 """259 260 def __repr__(self):261 attrs = {k: v for k, v in self.__dict__.items() if v != '??'}262 if not self.char:263 del attrs['char']264 elif self.char != '??':265 attrs['char'] = repr(self.char)266 if not getattr(self, 'send_event', True):267 del attrs['send_event']268 if self.state == 0:269 del attrs['state']270 elif isinstance(self.state, int):271 state = self.state272 mods = ('Shift', 'Lock', 'Control',273 'Mod1', 'Mod2', 'Mod3', 'Mod4', 'Mod5',274 'Button1', 'Button2', 'Button3', 'Button4', 'Button5')275 s = []276 for i, n in enumerate(mods):277 if state & (1 << i):278 s.append(n)279 state = state & ~((1<< len(mods)) - 1)280 if state or not s:281 s.append(hex(state))282 attrs['state'] = '|'.join(s)283 if self.delta == 0:284 del attrs['delta']285 # widget usually is known286 # serial and time are not very interesting287 # keysym_num duplicates keysym288 # x_root and y_root mostly duplicate x and y289 keys = ('send_event',290 'state', 'keysym', 'keycode', 'char',291 'num', 'delta', 'focus',292 'x', 'y', 'width', 'height')293 return '<%s event%s>' % (294 getattr(self.type, 'name', self.type),295 ''.join(' %s=%s' % (k, attrs[k]) for k in keys if k in attrs)296 )297 298 __class_getitem__ = classmethod(types.GenericAlias)299 300 301_support_default_root = True302_default_root = None303 304 305def NoDefaultRoot():306 """Inhibit setting of default root window.307 308 Call this function to inhibit that the first instance of309 Tk is used for windows without an explicit parent window.310 """311 global _support_default_root, _default_root312 _support_default_root = False313 # Delete, so any use of _default_root will immediately raise an exception.314 # Rebind before deletion, so repeated calls will not fail.315 _default_root = None316 del _default_root317 318 319def _get_default_root(what=None):320 if not _support_default_root:321 raise RuntimeError("No master specified and tkinter is "322 "configured to not support default root")323 if _default_root is None:324 if what:325 raise RuntimeError(f"Too early to {what}: no default root window")326 root = Tk()327 assert _default_root is root328 return _default_root329 330 331def _get_temp_root():332 global _support_default_root333 if not _support_default_root:334 raise RuntimeError("No master specified and tkinter is "335 "configured to not support default root")336 root = _default_root337 if root is None:338 assert _support_default_root339 _support_default_root = False340 root = Tk()341 _support_default_root = True342 assert _default_root is None343 root.withdraw()344 root._temporary = True345 return root346 347 348def _destroy_temp_root(master):349 if getattr(master, '_temporary', False):350 try:351 master.destroy()352 except TclError:353 pass354 355 356def _tkerror(err):357 """Internal function."""358 pass359 360 361def _exit(code=0):362 """Internal function. Calling it will raise the exception SystemExit."""363 try:364 code = int(code)365 except ValueError:366 pass367 raise SystemExit(code)368 369 370_varnum = 0371 372 373class Variable:374 """Class to define value holders for e.g. buttons.375 376 Subclasses StringVar, IntVar, DoubleVar, BooleanVar are specializations377 that constrain the type of the value returned from get()."""378 _default = ""379 _tk = None380 _tclCommands = None381 382 def __init__(self, master=None, value=None, name=None):383 """Construct a variable384 385 MASTER can be given as master widget.386 VALUE is an optional value (defaults to "")387 NAME is an optional Tcl name (defaults to PY_VARnum).388 389 If NAME matches an existing variable and VALUE is omitted390 then the existing value is retained.391 """392 # check for type of NAME parameter to override weird error message393 # raised from Modules/_tkinter.c:SetVar like:394 # TypeError: setvar() takes exactly 3 arguments (2 given)395 if name is not None and not isinstance(name, str):396 raise TypeError("name must be a string")397 global _varnum398 if master is None:399 master = _get_default_root('create variable')400 self._root = master._root()401 self._tk = master.tk402 if name:403 self._name = name404 else:405 self._name = 'PY_VAR' + repr(_varnum)406 _varnum += 1407 if value is not None:408 self.initialize(value)409 elif not self._tk.getboolean(self._tk.call("info", "exists", self._name)):410 self.initialize(self._default)411 412 def __del__(self):413 """Unset the variable in Tcl."""414 if self._tk is None:415 return416 if self._tk.getboolean(self._tk.call("info", "exists", self._name)):417 self._tk.globalunsetvar(self._name)418 if self._tclCommands is not None:419 for name in self._tclCommands:420 self._tk.deletecommand(name)421 self._tclCommands = None422 423 def __str__(self):424 """Return the name of the variable in Tcl."""425 return self._name426 427 def set(self, value):428 """Set the variable to VALUE."""429 return self._tk.globalsetvar(self._name, value)430 431 initialize = set432 433 def get(self):434 """Return value of variable."""435 return self._tk.globalgetvar(self._name)436 437 def _register(self, callback):438 f = CallWrapper(callback, None, self._root).__call__439 cbname = repr(id(f))440 try:441 callback = callback.__func__442 except AttributeError:443 pass444 try:445 cbname = cbname + callback.__name__446 except AttributeError:447 pass448 self._tk.createcommand(cbname, f)449 if self._tclCommands is None:450 self._tclCommands = []451 self._tclCommands.append(cbname)452 return cbname453 454 def trace_add(self, mode, callback):455 """Define a trace callback for the variable.456 457 Mode is one of "read", "write", "unset", or a list or tuple of458 such strings.459 Callback must be a function which is called when the variable is460 read, written or unset.461 462 Return the name of the callback.463 """464 cbname = self._register(callback)465 self._tk.call('trace', 'add', 'variable',466 self._name, mode, (cbname,))467 return cbname468 469 def trace_remove(self, mode, cbname):470 """Delete the trace callback for a variable.471 472 Mode is one of "read", "write", "unset" or a list or tuple of473 such strings. Must be same as were specified in trace_add().474 cbname is the name of the callback returned from trace_add().475 """476 self._tk.call('trace', 'remove', 'variable',477 self._name, mode, cbname)478 for m, ca in self.trace_info():479 if self._tk.splitlist(ca)[0] == cbname:480 break481 else:482 self._tk.deletecommand(cbname)483 try:484 self._tclCommands.remove(cbname)485 except ValueError:486 pass487 488 def trace_info(self):489 """Return all trace callback information."""490 splitlist = self._tk.splitlist491 return [(splitlist(k), v) for k, v in map(splitlist,492 splitlist(self._tk.call('trace', 'info', 'variable', self._name)))]493 494 def trace_variable(self, mode, callback):495 """Define a trace callback for the variable.496 497 MODE is one of "r", "w", "u" for read, write, undefine.498 CALLBACK must be a function which is called when499 the variable is read, written or undefined.500 501 Return the name of the callback.502 503 This deprecated method wraps a deprecated Tcl method removed504 in Tcl 9.0. Use trace_add() instead.505 """506 import warnings507 warnings.warn(508 "trace_variable() is deprecated and not supported with Tcl 9; "509 "use trace_add() instead.",510 DeprecationWarning, stacklevel=2)511 cbname = self._register(callback)512 self._tk.call("trace", "variable", self._name, mode, cbname)513 return cbname514 515 trace = trace_variable516 517 def trace_vdelete(self, mode, cbname):518 """Delete the trace callback for a variable.519 520 MODE is one of "r", "w", "u" for read, write, undefine.521 CBNAME is the name of the callback returned from trace_variable or trace.522 523 This deprecated method wraps a deprecated Tcl method removed524 in Tcl 9.0. Use trace_remove() instead.525 """526 import warnings527 warnings.warn(528 "trace_vdelete() is deprecated and not supported with Tcl 9; "529 "use trace_remove() instead.",530 DeprecationWarning, stacklevel=2)531 self._tk.call("trace", "vdelete", self._name, mode, cbname)532 cbname = self._tk.splitlist(cbname)[0]533 for m, ca in self.trace_info():534 if self._tk.splitlist(ca)[0] == cbname:535 break536 else:537 self._tk.deletecommand(cbname)538 try:539 self._tclCommands.remove(cbname)540 except ValueError:541 pass542 543 def trace_vinfo(self):544 """Return all trace callback information.545 546 This deprecated method wraps a deprecated Tcl method removed547 in Tcl 9.0. Use trace_info() instead.548 """549 import warnings550 warnings.warn(551 "trace_vinfo() is deprecated and not supported with Tcl 9; "552 "use trace_info() instead.",553 DeprecationWarning, stacklevel=2)554 return [self._tk.splitlist(x) for x in self._tk.splitlist(555 self._tk.call("trace", "vinfo", self._name))]556 557 def __eq__(self, other):558 if not isinstance(other, Variable):559 return NotImplemented560 return (self._name == other._name561 and self.__class__.__name__ == other.__class__.__name__562 and self._tk == other._tk)563 564 565class StringVar(Variable):566 """Value holder for strings variables."""567 _default = ""568 569 def __init__(self, master=None, value=None, name=None):570 """Construct a string variable.571 572 MASTER can be given as master widget.573 VALUE is an optional value (defaults to "")574 NAME is an optional Tcl name (defaults to PY_VARnum).575 576 If NAME matches an existing variable and VALUE is omitted577 then the existing value is retained.578 """579 Variable.__init__(self, master, value, name)580 581 def get(self):582 """Return value of variable as string."""583 value = self._tk.globalgetvar(self._name)584 if isinstance(value, str):585 return value586 return str(value)587 588 589class IntVar(Variable):590 """Value holder for integer variables."""591 _default = 0592 593 def __init__(self, master=None, value=None, name=None):594 """Construct an integer variable.595 596 MASTER can be given as master widget.597 VALUE is an optional value (defaults to 0)598 NAME is an optional Tcl name (defaults to PY_VARnum).599 600 If NAME matches an existing variable and VALUE is omitted601 then the existing value is retained.602 """603 Variable.__init__(self, master, value, name)604 605 def get(self):606 """Return the value of the variable as an integer."""607 value = self._tk.globalgetvar(self._name)608 try:609 return self._tk.getint(value)610 except (TypeError, TclError):611 return int(self._tk.getdouble(value))612 613 614class DoubleVar(Variable):615 """Value holder for float variables."""616 _default = 0.0617 618 def __init__(self, master=None, value=None, name=None):619 """Construct a float variable.620 621 MASTER can be given as master widget.622 VALUE is an optional value (defaults to 0.0)623 NAME is an optional Tcl name (defaults to PY_VARnum).624 625 If NAME matches an existing variable and VALUE is omitted626 then the existing value is retained.627 """628 Variable.__init__(self, master, value, name)629 630 def get(self):631 """Return the value of the variable as a float."""632 return self._tk.getdouble(self._tk.globalgetvar(self._name))633 634 635class BooleanVar(Variable):636 """Value holder for boolean variables."""637 _default = False638 639 def __init__(self, master=None, value=None, name=None):640 """Construct a boolean variable.641 642 MASTER can be given as master widget.643 VALUE is an optional value (defaults to False)644 NAME is an optional Tcl name (defaults to PY_VARnum).645 646 If NAME matches an existing variable and VALUE is omitted647 then the existing value is retained.648 """649 Variable.__init__(self, master, value, name)650 651 def set(self, value):652 """Set the variable to VALUE."""653 return self._tk.globalsetvar(self._name, self._tk.getboolean(value))654 655 initialize = set656 657 def get(self):658 """Return the value of the variable as a bool."""659 try:660 return self._tk.getboolean(self._tk.globalgetvar(self._name))661 except TclError:662 raise ValueError("invalid literal for getboolean()")663 664 665def mainloop(n=0):666 """Run the main loop of Tcl."""667 _get_default_root('run the main loop').tk.mainloop(n)668 669 670getint = int671 672getdouble = float673 674 675def getboolean(s):676 """Convert Tcl object to True or False."""677 try:678 return _get_default_root('use getboolean()').tk.getboolean(s)679 except TclError:680 raise ValueError("invalid literal for getboolean()")681 682 683# Methods defined on both toplevel and interior widgets684 685class Misc:686 """Internal class.687 688 Base class which defines methods common for interior widgets."""689 690 # used for generating child widget names691 _last_child_ids = None692 693 # XXX font command?694 _tclCommands = None695 696 def destroy(self):697 """Internal function.698 699 Delete all Tcl commands created for700 this widget in the Tcl interpreter."""701 if self._tclCommands is not None:702 for name in self._tclCommands:703 self.tk.deletecommand(name)704 self._tclCommands = None705 706 def deletecommand(self, name):707 """Internal function.708 709 Delete the Tcl command provided in NAME."""710 self.tk.deletecommand(name)711 try:712 self._tclCommands.remove(name)713 except ValueError:714 pass715 716 def tk_strictMotif(self, boolean=None):717 """Set Tcl internal variable, whether the look and feel718 should adhere to Motif.719 720 A parameter of 1 means adhere to Motif (e.g. no color721 change if mouse passes over slider).722 Returns the set value."""723 return self.tk.getboolean(self.tk.call(724 'set', 'tk_strictMotif', boolean))725 726 def tk_bisque(self):727 """Change the color scheme to light brown as used in Tk 3.6 and before."""728 self.tk.call('tk_bisque')729 730 def tk_setPalette(self, *args, **kw):731 """Set a new color scheme for all widget elements.732 733 A single color as argument will cause that all colors of Tk734 widget elements are derived from this.735 Alternatively several keyword parameters and its associated736 colors can be given. The following keywords are valid:737 activeBackground, foreground, selectColor,738 activeForeground, highlightBackground, selectBackground,739 background, highlightColor, selectForeground,740 disabledForeground, insertBackground, troughColor."""741 self.tk.call(('tk_setPalette',)742 + _flatten(args) + _flatten(list(kw.items())))743 744 def wait_variable(self, name='PY_VAR'):745 """Wait until the variable is modified.746 747 A parameter of type IntVar, StringVar, DoubleVar or748 BooleanVar must be given."""749 self.tk.call('tkwait', 'variable', name)750 waitvar = wait_variable # XXX b/w compat751 752 def wait_window(self, window=None):753 """Wait until a WIDGET is destroyed.754 755 If no parameter is given self is used."""756 if window is None:757 window = self758 self.tk.call('tkwait', 'window', window._w)759 760 def wait_visibility(self, window=None):761 """Wait until the visibility of a WIDGET changes762 (e.g. it appears).763 764 If no parameter is given self is used."""765 if window is None:766 window = self767 self.tk.call('tkwait', 'visibility', window._w)768 769 def setvar(self, name='PY_VAR', value='1'):770 """Set Tcl variable NAME to VALUE."""771 self.tk.setvar(name, value)772 773 def getvar(self, name='PY_VAR'):774 """Return value of Tcl variable NAME."""775 return self.tk.getvar(name)776 777 def getint(self, s):778 try:779 return self.tk.getint(s)780 except TclError as exc:781 raise ValueError(str(exc))782 783 def getdouble(self, s):784 try:785 return self.tk.getdouble(s)786 except TclError as exc:787 raise ValueError(str(exc))788 789 def getboolean(self, s):790 """Return a boolean value for Tcl boolean values true and false given as parameter."""791 try:792 return self.tk.getboolean(s)793 except TclError:794 raise ValueError("invalid literal for getboolean()")795 796 def focus_set(self):797 """Direct input focus to this widget.798 799 If the application currently does not have the focus800 this widget will get the focus if the application gets801 the focus through the window manager."""802 self.tk.call('focus', self._w)803 focus = focus_set # XXX b/w compat?804 805 def focus_force(self):806 """Direct input focus to this widget even if the807 application does not have the focus. Use with808 caution!"""809 self.tk.call('focus', '-force', self._w)810 811 def focus_get(self):812 """Return the widget which has currently the focus in the813 application.814 815 Use focus_displayof to allow working with several816 displays. Return None if application does not have817 the focus."""818 name = self.tk.call('focus')819 if name == 'none' or not name: return None820 return self._nametowidget(name)821 822 def focus_displayof(self):823 """Return the widget which has currently the focus on the824 display where this widget is located.825 826 Return None if the application does not have the focus."""827 name = self.tk.call('focus', '-displayof', self._w)828 if name == 'none' or not name: return None829 return self._nametowidget(name)830 831 def focus_lastfor(self):832 """Return the widget which would have the focus if top level833 for this widget gets the focus from the window manager."""834 name = self.tk.call('focus', '-lastfor', self._w)835 if name == 'none' or not name: return None836 return self._nametowidget(name)837 838 def tk_focusFollowsMouse(self):839 """The widget under mouse will get automatically focus. Can not840 be disabled easily."""841 self.tk.call('tk_focusFollowsMouse')842 843 def tk_focusNext(self):844 """Return the next widget in the focus order which follows845 widget which has currently the focus.846 847 The focus order first goes to the next child, then to848 the children of the child recursively and then to the849 next sibling which is higher in the stacking order. A850 widget is omitted if it has the takefocus option set851 to 0."""852 name = self.tk.call('tk_focusNext', self._w)853 if not name: return None854 return self._nametowidget(name)855 856 def tk_focusPrev(self):857 """Return previous widget in the focus order. See tk_focusNext for details."""858 name = self.tk.call('tk_focusPrev', self._w)859 if not name: return None860 return self._nametowidget(name)861 862 def after(self, ms, func=None, *args, **kw):863 """Call function once after given time.864 865 MS specifies the time in milliseconds. FUNC gives the866 function which shall be called. Additional parameters867 are given as parameters to the function call. Return868 identifier to cancel scheduling with after_cancel."""869 if func is None:870 # I'd rather use time.sleep(ms*0.001)871 self.tk.call('after', ms)872 return None873 else:874 def callit():875 try:876 func(*args, **kw)877 finally:878 try:879 self.deletecommand(name)880 except TclError:881 pass882 try:883 callit.__name__ = func.__name__884 except AttributeError:885 # Required for callable classes (bpo-44404)886 callit.__name__ = type(func).__name__887 name = self._register(callit)888 return self.tk.call('after', ms, name)889 890 def after_idle(self, func, *args, **kw):891 """Call FUNC once if the Tcl main loop has no event to892 process.893 894 Return an identifier to cancel the scheduling with895 after_cancel."""896 return self.after('idle', func, *args, **kw)897 898 def after_cancel(self, id):899 """Cancel scheduling of function identified with ID.900 901 Identifier returned by after or after_idle must be902 given as first parameter.903 """904 if not id:905 raise ValueError('id must be a valid identifier returned from '906 'after or after_idle')907 try:908 data = self.tk.call('after', 'info', id)909 script = self.tk.splitlist(data)[0]910 self.deletecommand(script)911 except TclError:912 pass913 self.tk.call('after', 'cancel', id)914 915 def after_info(self, id=None):916 """Return information about existing event handlers.917 918 With no argument, return a tuple of the identifiers for all existing919 event handlers created by the after and after_idle commands for this920 interpreter. If id is supplied, it specifies an existing handler; id921 must have been the return value from some previous call to after or922 after_idle and it must not have triggered yet or been canceled. If the923 id doesn't exist, a TclError is raised. Otherwise, the return value is924 a tuple containing (script, type) where script is a reference to the925 function to be called by the event handler and type is either 'idle'926 or 'timer' to indicate what kind of event handler it is.927 """928 return self.tk.splitlist(self.tk.call('after', 'info', id))929 930 def bell(self, displayof=0):931 """Ring a display's bell."""932 self.tk.call(('bell',) + self._displayof(displayof))933 934 def tk_busy_cget(self, option):935 """Return the value of busy configuration option.936 937 The widget must have been previously made busy by938 tk_busy_hold(). Option may have any of the values accepted by939 tk_busy_hold().940 """941 return self.tk.call('tk', 'busy', 'cget', self._w, '-'+option)942 busy_cget = tk_busy_cget943 944 def tk_busy_configure(self, cnf=None, **kw):945 """Query or modify the busy configuration options.946 947 The widget must have been previously made busy by948 tk_busy_hold(). Options may have any of the values accepted by949 tk_busy_hold().950 951 Please note that the option database is referenced by the widget952 name or class. For example, if a Frame widget with name "frame"953 is to be made busy, the busy cursor can be specified for it by954 either call:955 956 w.option_add('*frame.busyCursor', 'gumby')957 w.option_add('*Frame.BusyCursor', 'gumby')958 """959 if kw:960 cnf = _cnfmerge((cnf, kw))961 elif cnf:962 cnf = _cnfmerge(cnf)963 if cnf is None:964 return self._getconfigure(965 'tk', 'busy', 'configure', self._w)966 if isinstance(cnf, str):967 return self._getconfigure1(968 'tk', 'busy', 'configure', self._w, '-'+cnf)969 self.tk.call('tk', 'busy', 'configure', self._w, *self._options(cnf))970 busy_config = busy_configure = tk_busy_config = tk_busy_configure971 972 def tk_busy_current(self, pattern=None):973 """Return a list of widgets that are currently busy.974 975 If a pattern is given, only busy widgets whose path names match976 a pattern are returned.977 """978 return [self._nametowidget(x) for x in979 self.tk.splitlist(self.tk.call(980 'tk', 'busy', 'current', pattern))]981 busy_current = tk_busy_current982 983 def tk_busy_forget(self):984 """Make this widget no longer busy.985 986 User events will again be received by the widget.987 """988 self.tk.call('tk', 'busy', 'forget', self._w)989 busy_forget = tk_busy_forget990 991 def tk_busy_hold(self, **kw):992 """Make this widget appear busy.993 994 The specified widget and its descendants will be blocked from995 user interactions. Normally update() should be called996 immediately afterward to insure that the hold operation is in997 effect before the application starts its processing.998 999 The only supported configuration option is:1000 1001 cursor: the cursor to be displayed when the widget is made1002 busy.1003 """1004 self.tk.call('tk', 'busy', 'hold', self._w, *self._options(kw))1005 busy = busy_hold = tk_busy = tk_busy_hold1006 1007 def tk_busy_status(self):1008 """Return True if the widget is busy, False otherwise."""1009 return self.tk.getboolean(self.tk.call(1010 'tk', 'busy', 'status', self._w))1011 busy_status = tk_busy_status1012 1013 # Clipboard handling:1014 def clipboard_get(self, **kw):1015 """Retrieve data from the clipboard on window's display.1016 1017 The window keyword defaults to the root window of the Tkinter1018 application.1019 1020 The type keyword specifies the form in which the data is1021 to be returned and should be an atom name such as STRING1022 or FILE_NAME. Type defaults to STRING, except on X11, where the default1023 is to try UTF8_STRING and fall back to STRING.1024 1025 This command is equivalent to:1026 1027 selection_get(CLIPBOARD)1028 """1029 if 'type' not in kw and self._windowingsystem == 'x11':1030 try:1031 kw['type'] = 'UTF8_STRING'1032 return self.tk.call(('clipboard', 'get') + self._options(kw))1033 except TclError:1034 del kw['type']1035 return self.tk.call(('clipboard', 'get') + self._options(kw))1036 1037 def clipboard_clear(self, **kw):1038 """Clear the data in the Tk clipboard.1039 1040 A widget specified for the optional displayof keyword1041 argument specifies the target display."""1042 if 'displayof' not in kw: kw['displayof'] = self._w1043 self.tk.call(('clipboard', 'clear') + self._options(kw))1044 1045 def clipboard_append(self, string, **kw):1046 """Append STRING to the Tk clipboard.1047 1048 A widget specified at the optional displayof keyword1049 argument specifies the target display. The clipboard1050 can be retrieved with selection_get."""1051 if 'displayof' not in kw: kw['displayof'] = self._w1052 self.tk.call(('clipboard', 'append') + self._options(kw)1053 + ('--', string))1054 # XXX grab current w/o window argument1055 1056 def grab_current(self):1057 """Return widget which has currently the grab in this application1058 or None."""1059 name = self.tk.call('grab', 'current', self._w)1060 if not name: return None1061 return self._nametowidget(name)1062 1063 def grab_release(self):1064 """Release grab for this widget if currently set."""1065 self.tk.call('grab', 'release', self._w)1066 1067 def grab_set(self):1068 """Set grab for this widget.1069 1070 A grab directs all events to this and descendant1071 widgets in the application."""1072 self.tk.call('grab', 'set', self._w)1073 1074 def grab_set_global(self):1075 """Set global grab for this widget.1076 1077 A global grab directs all events to this and1078 descendant widgets on the display. Use with caution -1079 other applications do not get events anymore."""1080 self.tk.call('grab', 'set', '-global', self._w)1081 1082 def grab_status(self):1083 """Return None, "local" or "global" if this widget has1084 no, a local or a global grab."""1085 status = self.tk.call('grab', 'status', self._w)1086 if status == 'none': status = None1087 return status1088 1089 def option_add(self, pattern, value, priority = None):1090 """Set a VALUE (second parameter) for an option1091 PATTERN (first parameter).1092 1093 An optional third parameter gives the numeric priority1094 (defaults to 80)."""1095 self.tk.call('option', 'add', pattern, value, priority)1096 1097 def option_clear(self):1098 """Clear the option database.1099 1100 It will be reloaded if option_add is called."""1101 self.tk.call('option', 'clear')1102 1103 def option_get(self, name, className):1104 """Return the value for an option NAME for this widget1105 with CLASSNAME.1106 1107 Values with higher priority override lower values."""1108 return self.tk.call('option', 'get', self._w, name, className)1109 1110 def option_readfile(self, fileName, priority = None):1111 """Read file FILENAME into the option database.1112 1113 An optional second parameter gives the numeric1114 priority."""1115 self.tk.call('option', 'readfile', fileName, priority)1116 1117 def selection_clear(self, **kw):1118 """Clear the current X selection."""1119 if 'displayof' not in kw: kw['displayof'] = self._w1120 self.tk.call(('selection', 'clear') + self._options(kw))1121 1122 def selection_get(self, **kw):1123 """Return the contents of the current X selection.1124 1125 A keyword parameter selection specifies the name of1126 the selection and defaults to PRIMARY. A keyword1127 parameter displayof specifies a widget on the display1128 to use. A keyword parameter type specifies the form of data to be1129 fetched, defaulting to STRING except on X11, where UTF8_STRING is tried1130 before STRING."""1131 if 'displayof' not in kw: kw['displayof'] = self._w1132 if 'type' not in kw and self._windowingsystem == 'x11':1133 try:1134 kw['type'] = 'UTF8_STRING'1135 return self.tk.call(('selection', 'get') + self._options(kw))1136 except TclError:1137 del kw['type']1138 return self.tk.call(('selection', 'get') + self._options(kw))1139 1140 def selection_handle(self, command, **kw):1141 """Specify a function COMMAND to call if the X1142 selection owned by this widget is queried by another1143 application.1144 1145 This function must return the contents of the1146 selection. The function will be called with the1147 arguments OFFSET and LENGTH which allows the chunking1148 of very long selections. The following keyword1149 parameters can be provided:1150 selection - name of the selection (default PRIMARY),1151 type - type of the selection (e.g. STRING, FILE_NAME)."""1152 name = self._register(command)1153 self.tk.call(('selection', 'handle') + self._options(kw)1154 + (self._w, name))1155 1156 def selection_own(self, **kw):1157 """Become owner of X selection.1158 1159 A keyword parameter selection specifies the name of1160 the selection (default PRIMARY)."""1161 self.tk.call(('selection', 'own') +1162 self._options(kw) + (self._w,))1163 1164 def selection_own_get(self, **kw):1165 """Return owner of X selection.1166 1167 The following keyword parameter can1168 be provided:1169 selection - name of the selection (default PRIMARY),1170 type - type of the selection (e.g. STRING, FILE_NAME)."""1171 if 'displayof' not in kw: kw['displayof'] = self._w1172 name = self.tk.call(('selection', 'own') + self._options(kw))1173 if not name: return None1174 return self._nametowidget(name)1175 1176 def send(self, interp, cmd, *args):1177 """Send Tcl command CMD to different interpreter INTERP to be executed."""1178 return self.tk.call(('send', interp, cmd) + args)1179 1180 def lower(self, belowThis=None):1181 """Lower this widget in the stacking order."""1182 self.tk.call('lower', self._w, belowThis)1183 1184 def tkraise(self, aboveThis=None):1185 """Raise this widget in the stacking order."""1186 self.tk.call('raise', self._w, aboveThis)1187 1188 lift = tkraise1189 1190 def info_patchlevel(self):1191 """Returns the exact version of the Tcl library."""1192 patchlevel = self.tk.call('info', 'patchlevel')1193 return _parse_version(patchlevel)1194 1195 def winfo_atom(self, name, displayof=0):1196 """Return integer which represents atom NAME."""1197 args = ('winfo', 'atom') + self._displayof(displayof) + (name,)1198 return self.tk.getint(self.tk.call(args))1199 1200 def winfo_atomname(self, id, displayof=0):