Source code for teek._widgets.base
import collections.abc
import contextlib
import functools
import keyword
import operator
import re
import teek
from teek._tcl_calls import counts, from_tcl, make_thread_safe
from teek._structures import ConfigDict, CgetConfigureConfigDict, after_quit
_widgets = {}
_class_bindings = {}
after_quit.connect(_widgets.clear)
after_quit.connect(_class_bindings.clear)
# like what you would expect to get for combining @classmethod and @property,
# but doesn't do any magic with assigning, only getting
class _ClassProperty:
def __init__(self, getter):
assert isinstance(getter.__name__, str)
self._getter = getter
def __get__(self, instance_or_none, claas):
if instance_or_none is None:
return self._getter(claas)
attribute = self._getter.__name__
classname = claas.__name__
raise AttributeError(
"the %s attribute must be used like %s.%s, "
"not like some_%s_instance.%s"
% (attribute, classname, attribute,
classname.lower(), attribute))
class StateSet(collections.abc.MutableSet):
def __init__(self, widget):
self._widget = widget
def __repr__(self):
# yes, this uses [] even though it behaves like a set, that's the best
# thing i thought of
return '<state set: %r>' % (list(self),)
def __iter__(self):
return iter(self._widget._call([str], self._widget, 'state'))
def __len__(self):
return len(self._widget._call([str], self._widget, 'state'))
def __contains__(self, state):
return self._widget._call(bool, self._widget, 'instate', state)
def add(self, state):
self._widget._call(None, self._widget, 'state', state)
def discard(self, state):
self._widget._call(None, self._widget, 'state', '!' + state)
class GridRowOrColumnConfig(ConfigDict):
def __init__(self, configure_method):
super().__init__()
self._types.update({
'minsize': teek.ScreenDistance,
'weight': float,
'uniform': str,
'pad': teek.ScreenDistance,
})
self._configure = configure_method
def _set(self, option, value):
self._configure(None, '-' + option, value)
def _get(self, option):
return self._configure(self._types.get(option, str), '-' + option)
def _list_options(self):
return (key.lstrip('-') for key in self._configure({}).keys())
class GridRowOrColumn:
def __init__(self, widget, row_or_column, number):
super().__init__()
self._widget = widget
self._row_or_column = row_or_column
self._number = number
self.config = GridRowOrColumnConfig(self._configure)
def __repr__(self):
return (
"<grid %s %d: has a config attribute and a get_slaves() method>"
% (self._row_or_column, self._number))
def __eq__(self, other):
if not isinstance(other, GridRowOrColumn):
return NotImplemented
return (self._widget == other._widget and
self._row_or_column == other._row_or_column and
self._number == other._number)
def __hash__(self):
return hash((self._widget, self._row_or_column, self._number))
def _configure(self, returntype, *args):
return self._widget._call(
returntype, 'grid', self._row_or_column + 'configure',
self._widget, self._number, *args)
def get_slaves(self):
return self._widget._call(
[Widget], 'grid', 'slaves', self._widget,
'-' + self._row_or_column, self._number)
# make things more tkinter-user-friendly
def _tkinter_hint(good, bad):
def dont_use_this(self, *args, **kwargs):
raise TypeError("use %s, not %s" % (good, bad))
return dont_use_this
[docs]class Widget:
"""This is a base class for all widgets.
All widgets inherit from this class, and they have all the attributes
and methods documented here.
Don't create instances of ``Widget`` yourself like ``Widget(...)``; use one
of the classes documented below instead. However, you can use ``Widget``
with :func:`isinstance`; e.g. ``isinstance(thingy, teek.Widget)`` returns
``True`` if ``thingy`` is a teek widget.
.. attribute:: config
A dict-like object that represents the widget's options.
>>> window = teek.Window()
>>> label = teek.Label(window, text='Hello World')
>>> label.config
<a config object, behaves like a dict>
>>> label.config['text']
'Hello World'
>>> label.config['text'] = 'New Text'
>>> label.config['text']
'New Text'
>>> label.config.update({'text': 'Even newer text'})
>>> label.config['text']
'Even newer text'
>>> import pprint
>>> pprint.pprint(dict(label.config)) # prints everything nicely \
# doctest: +ELLIPSIS
{...,
'text': 'Even newer text',
...}
.. attribute:: state
Represents the Ttk state of the widget. The state object behaves like a
:class:`set` of strings. For example, ``widget.state.add('disabled')``
makes a widget look like it's grayed out, and
``widget.state.remove('disabled')`` undoes that. See ``STATES`` in
:man:`ttk_intro(3tk)` for more details about states.
.. note::
Only Ttk widgets have states, and this attribute is set to None for
non-Ttk widgets. If you don't know what Ttk is, you should read
about it in :ref:`the teek tutorial <tcl-tk-tkinter-teek>`.
Most teek widgets are ttk widgets, but some aren't, and that's
mentioned in the documentation of those widgets.
.. attribute:: tk_class_name
Tk's class name of the widget class, as a string.
This is a class attribute, but it can be accessed from instances as
well:
>>> text = teek.Text(teek.Window())
>>> text.tk_class_name
'Text'
>>> teek.Text.tk_class_name
'Text'
Note that Tk's class names are sometimes different from the names of
Python classes, and this attribute can also be None in some special
cases.
>>> teek.Label.tk_class_name
'TLabel'
>>> class AsdLabel(teek.Label):
... pass
...
>>> AsdLabel.tk_class_name
'TLabel'
>>> print(teek.Window.tk_class_name)
None
>>> print(teek.Widget.tk_class_name)
None
.. attribute:: command_list
A list of command strings from :func:`.create_command`.
Append a command to this if you want the command to be deleted with
:func:`.delete_command` when the widget is destroyed (with e.g.
:meth:`.destroy`).
"""
_widget_name = None
tk_class_name = None
@make_thread_safe
def __init__(self, parent, **kwargs):
if type(self)._widget_name is None:
raise TypeError("cannot create instances of %s directly, "
"use one of its subclasses instead"
% type(self).__name__)
if parent is None:
parentpath = ''
else:
parentpath = parent.to_tcl()
self.parent = parent
# yes, it must be lowercase
safe_class_name = re.sub(r'\W', '_', type(self).__name__).lower()
# use some_widget.to_tcl() to access the _widget_path
self._widget_path = '%s.%s%d' % (
parentpath, safe_class_name, next(counts[safe_class_name]))
# TODO: some config options can only be given when the widget is
# created, add support for them
self._call(None, type(self)._widget_name, self.to_tcl())
_widgets[self.to_tcl()] = self
self.config = CgetConfigureConfigDict(
lambda returntype, *args: self._call(returntype, self, *args))
self._init_config() # subclasses should override this and use super
# support kwargs like from_=1, because from=1 is invalid syntax
for invalid_syntax in keyword.kwlist:
if invalid_syntax + '_' in kwargs:
kwargs[invalid_syntax] = kwargs.pop(invalid_syntax + '_')
self.config.update(kwargs)
# command strings that are deleted when the widget is destroyed
self.command_list = []
self.bindings = BindingDict( # BindingDict is defined below
lambda returntype, *args: self._call(
returntype, 'bind', self, *args
),
self.command_list)
self.bind = self.bindings._convenience_bind
if type(self)._widget_name.startswith('ttk::'):
self.state = StateSet(self)
else:
self.state = None
def _init_config(self):
# width and height aren't here because they are integers for some
# widgets and ScreenDistances for others... and sometimes the manual
# pages don't say which, so i have checked them by hand
self.config._types.update({
# ttk_widget(3tk)
'class': str,
'cursor': str,
'style': str,
# options(3tk)
'activebackground': teek.Color,
'activeborderwidth': teek.ScreenDistance,
'activeforeground': teek.Color,
'anchor': str,
'background': teek.Color,
'bg': teek.Color,
#'bitmap': ???,
'borderwidth': teek.ScreenDistance,
'bd': teek.ScreenDistance,
'cursor': str,
'compound': str,
'disabledforeground': teek.Color,
'exportselection': bool,
'font': teek.Font,
'foreground': teek.Color,
'fg': teek.Color,
'highlightbackground': teek.Color,
'highlightcolor': teek.Color,
'highlightthickness': str,
'insertbackground': teek.Color,
'insertborderwidth': teek.ScreenDistance,
'insertofftime': int,
'insertontime': int,
'insertwidth': teek.ScreenDistance,
'jump': bool,
'justify': str,
'orient': str,
'padx': teek.ScreenDistance,
'pady': teek.ScreenDistance,
'relief': str,
'repeatdelay': int,
'repeatinterval': int,
'selectbackground': teek.Color,
'selectborderwidth': teek.ScreenDistance,
'selectforeground': teek.Color,
'setgrid': bool,
'text': str,
'troughcolor': teek.Color,
'wraplength': teek.ScreenDistance,
# these options are in both man pages
'textvariable': teek.StringVar,
'underline': int,
'image': teek.Image,
# 'xscrollcommand' and 'yscrollcommand' are done below
'takefocus': str, # this one is harder to do right than you think
# other stuff that many things seem to have
'padding': teek.ScreenDistance,
'state': str,
})
for option_name in ('xscrollcommand', 'yscrollcommand'):
self.config._special[option_name] = functools.partial(
self._create_scroll_callback, option_name)
[docs] @classmethod
@make_thread_safe
def from_tcl(cls, path_string):
"""Creates a widget from a Tcl path name.
In Tcl, widgets are represented as commands, and doing something to the
widget invokes the command. Use this method if you know the Tcl command
and you would like to have a widget object instead.
This method raises :exc:`TypeError` if it's called from a different
``Widget`` subclass than what the type of the ``path_string`` widget
is:
>>> window = teek.Window()
>>> teek.Button.from_tcl(teek.Label(window).to_tcl()) \
# doctest: +ELLIPSIS
Traceback (most recent call last):
...
TypeError: '...' is a Label, not a Button
"""
if path_string == '.':
# this kind of sucks, i might make a _RootWindow class later
return None
result = _widgets[path_string]
if not isinstance(result, cls):
raise TypeError("%r is a %s, not a %s" % (
path_string, type(result).__name__, cls.__name__))
return result
[docs] def to_tcl(self):
"""Returns the widget's Tcl command name. See :meth:`from_tcl`."""
return self._widget_path
def __repr__(self):
class_name = type(self).__name__
if getattr(teek, class_name, None) is type(self):
result = 'teek.%s widget' % class_name
else:
result = '{0.__module__}.{0.__name__} widget'.format(type(self))
if not self.winfo_exists():
# _repr_parts() doesn't need to work with destroyed widgets
return '<destroyed %s>' % result
parts = self._repr_parts()
if parts:
result += ': ' + ', '.join(parts)
return '<' + result + '>'
def _repr_parts(self):
# overrided in subclasses
return []
def _create_scroll_callback(self, option_name):
result = teek.Callback()
command_string = teek.create_command(result.run, [float, float])
self.command_list.append(command_string)
self._call(None, self, 'configure', '-' + option_name, command_string)
return result
__getitem__ = _tkinter_hint("widget.config['option']", "widget['option']")
__setitem__ = _tkinter_hint("widget.config['option']", "widget['option']")
cget = _tkinter_hint("widget.config['option']", "widget.cget('option')")
configure = _tkinter_hint("widget.config['option'] = value",
"widget.configure(option=value)")
# like _tcl_calls.tcl_call, but with better error handling
@make_thread_safe
def _call(self, *args, **kwargs):
try:
return teek.tcl_call(*args, **kwargs)
except teek.TclError as err:
if not self.winfo_exists():
raise RuntimeError("the widget has been destroyed") from None
raise err
[docs] @make_thread_safe
def destroy(self):
"""Delete this widget and all child widgets.
Manual page: :man:`destroy(3tk)`
.. note::
Don't override this in a subclass. In some cases, the widget is
destroyed without a call to this method.
>>> class BrokenFunnyLabel(teek.Label):
... def destroy(self):
... print("destroying")
... super().destroy()
...
>>> BrokenFunnyLabel(teek.Window()).pack()
>>> teek.quit()
>>> # nothing was printed!
Use the ``<Destroy>`` event instead:
>>> class WorkingFunnyLabel(teek.Label):
... def __init__(self, *args, **kwargs):
... super().__init__(*args, **kwargs)
... self.bind('<Destroy>', self._destroy_callback)
... def _destroy_callback(self):
... print("destroying")
...
>>> WorkingFunnyLabel(teek.Window()).pack()
>>> teek.quit()
destroying
"""
for name in self._call([str], 'winfo', 'children', self):
# allow overriding the destroy() method if the widget was
# created by teek
if name in _widgets:
_widgets[name]._destroy_recurser()
else:
self._call(None, 'destroy', name)
# this must be BEFORE deleting command_list commands because <Destroy>
# bindings may need command_list stuff
self._call(None, 'destroy', self)
# this is here because now the widget is basically useless
del _widgets[self.to_tcl()]
for command in self.command_list:
teek.delete_command(command)
self.command_list.clear() # why not
# can be overrided when .destroy() in .destroy() would cause infinite
# recursion, see Window in windows.py
def _destroy_recurser(self):
self.destroy()
@_ClassProperty
@make_thread_safe
def class_bindings(cls):
if cls is Widget:
assert cls.tk_class_name is None
bindtag = 'all'
elif cls.tk_class_name is not None:
bindtag = cls.tk_class_name
else:
raise AttributeError(
"%s cannot be used with class_bindings and bind_class()"
% cls.__name__)
try:
return _class_bindings[bindtag]
except KeyError:
def call_bind(returntype, *args):
return teek.tcl_call(returntype, 'bind', bindtag, *args)
# all commands are deleted when the interpreter shuts down, and the
# binding dict created here should be alive until then, so it's
# fine to pass a new empty list for command list
bindings = BindingDict(call_bind, [])
_class_bindings[bindtag] = bindings
return bindings
@classmethod
@make_thread_safe
def bind_class(cls, *args, **kwargs):
return cls.class_bindings._convenience_bind(*args, **kwargs)
[docs] def winfo_children(self):
"""Returns a list of child widgets that this widget has.
Manual page: :man:`winfo(3tk)`
"""
return self._call([Widget], 'winfo', 'children', self)
[docs] def winfo_exists(self):
"""Returns False if the widget has been destroyed. See :meth:`destroy`.
Manual page: :man:`winfo(3tk)`
"""
# self._call uses this, so this must not use that
return teek.tcl_call(bool, 'winfo', 'exists', self)
def winfo_ismapped(self):
"""
Returns True if the widget is showing on the screen, or False
otherwise.
Manual page: :man:`winfo(3tk)`
"""
return self._call(bool, 'winfo', 'ismapped', self)
[docs] def winfo_toplevel(self):
"""Returns the :class:`Toplevel` widget that this widget is in.
Manual page: :man:`winfo(3tk)`
"""
return self._call(Widget, 'winfo', 'toplevel', self)
[docs] def winfo_width(self):
"""Calls ``winfo width``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'width', self)
[docs] def winfo_height(self):
"""Calls ``winfo height``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'height', self)
[docs] def winfo_reqwidth(self):
"""Calls ``winfo reqwidth``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'reqwidth', self)
[docs] def winfo_reqheight(self):
"""Calls ``winfo reqheight``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'reqheight', self)
[docs] def winfo_x(self):
"""Calls ``winfo x``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'x', self)
[docs] def winfo_y(self):
"""Calls ``winfo y``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'y', self)
[docs] def winfo_rootx(self):
"""Calls ``winfo rootx``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'rootx', self)
[docs] def winfo_rooty(self):
"""Calls ``winfo rooty``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'rooty', self)
[docs] def winfo_id(self):
"""Calls ``winfo id``. Returns an integer.
Manual page: :man:`winfo(3tk)`
"""
return self._call(int, 'winfo', 'id', self)
[docs] def focus(self, *, force=False):
"""Focuses the widget with :man:`focus(3tk)`.
If ``force=True`` is given, the ``-force`` option is used.
"""
if force:
self._call(None, 'focus', '-force', self)
else:
self._call(None, 'focus', self)
def _geometry_manager_slaves(self, geometry_manager):
return self._call([Widget], geometry_manager, 'slaves', self)
pack_slaves = functools.partialmethod(_geometry_manager_slaves, 'pack')
grid_slaves = functools.partialmethod(_geometry_manager_slaves, 'grid')
place_slaves = functools.partialmethod(_geometry_manager_slaves, 'place')
@property
def grid_rows(self):
width, height = self._call([int], 'grid', 'size', self)
return [GridRowOrColumn(self, 'row', number)
for number in range(height)]
@property
def grid_columns(self):
width, height = self._call([int], 'grid', 'size', self)
return [GridRowOrColumn(self, 'column', number)
for number in range(width)]
grid_rowconfigure = _tkinter_hint(
"widget.grid_rows[index].config['option'] = value",
"widget.grid_rowconfigure(index, option=value)")
grid_columnconfigure = _tkinter_hint(
"widget.grid_columns[index].config['option'] = value",
"widget.grid_columnconfigure(index, option=value)")
[docs] def busy_hold(self):
"""See ``tk busy hold`` in :man:`busy(3tk)`.
*New in Tk 8.6.*
"""
self._call(None, 'tk', 'busy', 'hold', self)
[docs] def busy_forget(self):
"""See ``tk busy forget`` in :man:`busy(3tk)`.
*New in Tk 8.6.*
"""
self._call(None, 'tk', 'busy', 'forget', self)
[docs] def busy_status(self):
"""See ``tk busy status`` in :man:`busy(3tk)`.
This Returns True or False.
*New in Tk 8.6.*
"""
return self._call(bool, 'tk', 'busy', 'status', self)
[docs] @contextlib.contextmanager
def busy(self):
"""A context manager that calls :func:`busy_hold` and :func:`busy_forg\
et`.
Example::
with window.busy():
# window.busy_hold() has been called, do something
...
# now window.busy_forget() has been called
"""
self.busy_hold()
try:
yield
finally:
self.busy_forget()
[docs] def event_generate(self, event, **kwargs):
"""Calls ``event generate`` documented in :man:`event(3tk)`.
As usual, options are given without dashes as keyword arguments, so Tcl
code like ``event generate $widget <SomeEvent> -data $theData`` looks
like ``widget.event_generate('<SomeEvent>', data=the_data)`` in
teek.
"""
option_args = []
for name, value in kwargs.items():
option_args.extend(['-' + name, value])
self._call(None, 'event', 'generate', self, event, *option_args)
# these are from bind(3tk), Tk 8.5 and 8.6 support all of these
#
# event(3tk) says that width and height are screen distances, but bind seems to
# convert them to ints, so they are ints here
#
# if you change this, also change docs/bind.rst
_BIND_SUBS = [
('%#', int, 'serial'),
('%a', int, 'above'),
('%b', int, 'button'),
('%c', int, 'count'),
('%d', str, '_data'),
('%f', bool, 'focus'),
('%h', int, 'height'),
('%i', int, 'i_window'),
('%k', int, 'keycode'),
('%m', str, 'mode'),
('%o', bool, 'override'),
('%p', str, 'place'),
('%s', str, 'state'),
('%t', int, 'time'),
('%w', int, 'width'),
('%x', int, 'x'),
('%y', int, 'y'),
('%A', str, 'char'),
('%B', int, 'borderwidth'),
('%D', int, 'delta'),
('%E', bool, 'sendevent'),
('%K', str, 'keysym'),
('%N', int, 'keysym_num'),
('%P', str, 'property_name'),
('%R', int, 'root'),
('%S', int, 'subwindow'),
('%T', int, 'type'),
('%W', Widget, 'widget'),
('%X', int, 'rootx'),
('%Y', int, 'rooty'),
]
class Event:
def __repr__(self):
# try to avoid making the repr too verbose
ignored_names = ['widget', 'sendevent', 'subwindow', 'time',
'i_window', 'root', 'state']
ignored_values = [None, '??', -1, 0]
pairs = []
for name, value in sorted(self.__dict__.items(),
key=operator.itemgetter(0)):
if name not in ignored_names and value not in ignored_values:
display_name = 'data' if name == '_data' else name
pairs.append('%s=%r' % (display_name, value))
return '<Event: %s>' % ', '.join(pairs)
def data(self, type_spec):
return from_tcl(type_spec, self._data)
class BindingDict(collections.abc.Mapping):
# bind(3tk) calls things like '<Button-1>' sequences, so this code is
# consistent with that
def __init__(self, bind_caller, command_list):
self._call_bind = bind_caller
self.command_list = command_list
self._callback_objects = {} # {sequence: callback}
def __repr__(self):
return '<a bindings object, behaves like a dict>'
def __iter__(self):
# loops over all existing bindings, not all possible bindings
return iter(self._call_bind([str]))
def __len__(self):
return len(self._call_bind([str]))
def _callback_runner(self, callback, *args):
assert len(args) == len(_BIND_SUBS)
event = Event()
for (character, type_, attrib), string_value in zip(_BIND_SUBS, args):
assert isinstance(string_value, str)
try:
value = from_tcl(type_, string_value)
except (ValueError, teek.TclError) as e:
if string_value == '??':
value = None
elif attrib == 'sendevent':
# this seems to be a bug in Tk, here's a minimal example:
#
# label .lab -text "click this to do the bug"
# pack .lab
# bind .lab <Leave> { puts "leave: %E" }
# bind .lab <Button-1> { tk_messageBox }
#
# for me this prints "leave: 343089580", even though
# bind(3tk) says that %E is 1 or 0
value = None
else: # pragma: no cover
raise e # if this runs, there's a bug in teek
setattr(event, attrib, value)
return callback.run(event)
def __getitem__(self, sequence):
if sequence in self._callback_objects:
return self._callback_objects[sequence]
# <1> and <Button-1> are equivalent, this handles that
for equiv_sequence, equiv_callback in self._callback_objects.items():
# this equivalence check should handle corner cases imo because the
# command names from create_command are unique
if (self._call_bind(str, sequence) ==
self._call_bind(str, equiv_sequence)): # noqa: E129
# found an equivalent binding, tcl commands are the same
self._callback_objects[sequence] = equiv_callback
return equiv_callback
callback = teek.Callback()
runner = functools.partial(self._callback_runner, callback)
command = teek.create_command(runner, [str] * len(_BIND_SUBS))
self.command_list.append(command) # avoid memory leaks
subs_string = ' '.join(subs for subs, type_, name in _BIND_SUBS)
self._call_bind(
None, sequence, '+ if { [%s %s] eq {break} } { break }' % (
command, subs_string))
self._callback_objects[sequence] = callback
return callback
# any_widget.bind is set to this
def _convenience_bind(self, sequence, func, *, event=False):
self[sequence].connect(func if event else (lambda event: func()))
# TODO: "RELATIVE PLACEMENT" in grid(3tk)
class ChildMixin:
"""Mixin class for widgets that can be added to other widgets.
Widgets like Label and Frame inherit from this, and widgets like
Toplevel and Window don't. Many widgets use this, so it shouldn't be
hard to find some usage examples for creating new widgets.
"""
def _geometry_manage(self, geometry_manager, **kwargs):
args = []
for name, value in kwargs.items():
if name == 'in_':
name = 'in'
args.append('-' + name)
args.append(value)
# special case: tkinter does nothing (lol), teek would give a
# noob-unfriendly TclError otherwise
if geometry_manager == 'place' and not args:
raise TypeError(
"cannot call widget.place() without any arguments, "
"do e.g. widget.place(relx=0, rely=0) instead")
self._call(None, geometry_manager, self.to_tcl(), *args)
def _geometry_manager_forget(self, geometry_manager):
self._call(None, geometry_manager, 'forget', self.to_tcl())
def _geometry_manager_info(self, geometry_manager):
types = {
'-in': Widget,
}
if geometry_manager == 'pack' or geometry_manager == 'grid':
types.update({
# padx and pady can be lists of 2 screen distances or just 1
# screen distance, which is fine because a Tcl screen distance
# string
# behaves like a list of 1 item
'-padx': [teek.ScreenDistance],
'-pady': [teek.ScreenDistance],
'-ipadx': teek.ScreenDistance,
'-ipady': teek.ScreenDistance,
})
if geometry_manager == 'pack':
types.update({
'-expand': bool,
})
elif geometry_manager == 'grid':
types.update({
'-column': int,
'-columnspan': int,
'-row': int,
'-rowspan': int,
'-sticky': str,
})
elif geometry_manager == 'place':
types.update({
'-anchor': str,
'-bordermode': str,
'-width': teek.ScreenDistance,
'-height': teek.ScreenDistance,
'-relheight': float,
'-relwidth': float,
'-relx': float,
'-rely': float,
'-x': teek.ScreenDistance,
'-y': teek.ScreenDistance,
})
else:
raise RuntimeError("oh no") # pragma: no cover
result = self._call(types, geometry_manager, 'info', self.to_tcl())
return {key.lstrip('-'): value for key, value in result.items()}
pack = functools.partialmethod(_geometry_manage, 'pack')
grid = functools.partialmethod(_geometry_manage, 'grid')
place = functools.partialmethod(_geometry_manage, 'place')
pack_forget = functools.partialmethod(_geometry_manager_forget, 'pack')
grid_forget = functools.partialmethod(_geometry_manager_forget, 'grid')
place_forget = functools.partialmethod(_geometry_manager_forget, 'place')
pack_info = functools.partialmethod(_geometry_manager_info, 'pack')
grid_info = functools.partialmethod(_geometry_manager_info, 'grid')
place_info = functools.partialmethod(_geometry_manager_info, 'place')