|
|
|
@ -26,8 +26,7 @@ __all__ = ["Button", "Checkbutton", "Combobox", "Entry", "Frame", "Label", |
|
|
|
"tclobjs_to_py", "setup_master"] |
|
|
|
|
|
|
|
import tkinter |
|
|
|
|
|
|
|
_flatten = tkinter._flatten |
|
|
|
from tkinter import _flatten, _join, _stringify |
|
|
|
|
|
|
|
# Verify if Tk is new enough to not need the Tile package |
|
|
|
_REQUIRE_TILE = True if tkinter.TkVersion < 8.5 else False |
|
|
|
@ -47,40 +46,55 @@ def _load_tile(master): |
|
|
|
master.tk.eval('package require tile') # TclError may be raised here |
|
|
|
master._tile_loaded = True |
|
|
|
|
|
|
|
def _format_optvalue(value, script=False): |
|
|
|
"""Internal function.""" |
|
|
|
if script: |
|
|
|
# if caller passes a Tcl script to tk.call, all the values need to |
|
|
|
# be grouped into words (arguments to a command in Tcl dialect) |
|
|
|
value = _stringify(value) |
|
|
|
elif isinstance(value, (list, tuple)): |
|
|
|
value = _join(value) |
|
|
|
return value |
|
|
|
|
|
|
|
def _format_optdict(optdict, script=False, ignore=None): |
|
|
|
"""Formats optdict to a tuple to pass it to tk.call. |
|
|
|
|
|
|
|
E.g. (script=False): |
|
|
|
{'foreground': 'blue', 'padding': [1, 2, 3, 4]} returns: |
|
|
|
('-foreground', 'blue', '-padding', '1 2 3 4')""" |
|
|
|
format = "%s" if not script else "{%s}" |
|
|
|
|
|
|
|
opts = [] |
|
|
|
for opt, value in optdict.items(): |
|
|
|
if ignore and opt in ignore: |
|
|
|
continue |
|
|
|
|
|
|
|
if isinstance(value, (list, tuple)): |
|
|
|
v = [] |
|
|
|
for val in value: |
|
|
|
if isinstance(val, str): |
|
|
|
v.append(str(val) if val else '{}') |
|
|
|
else: |
|
|
|
v.append(str(val)) |
|
|
|
|
|
|
|
# format v according to the script option, but also check for |
|
|
|
# space in any value in v in order to group them correctly |
|
|
|
value = format % ' '.join( |
|
|
|
('{%s}' if ' ' in val else '%s') % val for val in v) |
|
|
|
if not ignore or opt not in ignore: |
|
|
|
opts.append("-%s" % opt) |
|
|
|
if value is not None: |
|
|
|
opts.append(_format_optvalue(value, script)) |
|
|
|
|
|
|
|
if script and value == '': |
|
|
|
value = '{}' # empty string in Python is equivalent to {} in Tcl |
|
|
|
|
|
|
|
opts.append(("-%s" % opt, value)) |
|
|
|
|
|
|
|
# Remember: _flatten skips over None |
|
|
|
return _flatten(opts) |
|
|
|
|
|
|
|
def _mapdict_values(items): |
|
|
|
# each value in mapdict is expected to be a sequence, where each item |
|
|
|
# is another sequence containing a state (or several) and a value |
|
|
|
# E.g. (script=False): |
|
|
|
# [('active', 'selected', 'grey'), ('focus', [1, 2, 3, 4])] |
|
|
|
# returns: |
|
|
|
# ['active selected', 'grey', 'focus', [1, 2, 3, 4]] |
|
|
|
opt_val = [] |
|
|
|
for *state, val in items: |
|
|
|
# hacks for bakward compatibility |
|
|
|
state[0] # raise IndexError if empty |
|
|
|
if len(state) == 1: |
|
|
|
# if it is empty (something that evaluates to False), then |
|
|
|
# format it to Tcl code to denote the "normal" state |
|
|
|
state = state[0] or '' |
|
|
|
else: |
|
|
|
# group multiple states |
|
|
|
state = ' '.join(state) # raise TypeError if not str |
|
|
|
opt_val.append(state) |
|
|
|
if val is not None: |
|
|
|
opt_val.append(val) |
|
|
|
return opt_val |
|
|
|
|
|
|
|
def _format_mapdict(mapdict, script=False): |
|
|
|
"""Formats mapdict to pass it to tk.call. |
|
|
|
|
|
|
|
@ -90,32 +104,11 @@ def _format_mapdict(mapdict, script=False): |
|
|
|
returns: |
|
|
|
|
|
|
|
('-expand', '{active selected} grey focus {1, 2, 3, 4}')""" |
|
|
|
# if caller passes a Tcl script to tk.call, all the values need to |
|
|
|
# be grouped into words (arguments to a command in Tcl dialect) |
|
|
|
format = "%s" if not script else "{%s}" |
|
|
|
|
|
|
|
opts = [] |
|
|
|
for opt, value in mapdict.items(): |
|
|
|
|
|
|
|
opt_val = [] |
|
|
|
# each value in mapdict is expected to be a sequence, where each item |
|
|
|
# is another sequence containing a state (or several) and a value |
|
|
|
for statespec in value: |
|
|
|
state, val = statespec[:-1], statespec[-1] |
|
|
|
|
|
|
|
if len(state) > 1: # group multiple states |
|
|
|
state = "{%s}" % ' '.join(state) |
|
|
|
else: # single state |
|
|
|
# if it is empty (something that evaluates to False), then |
|
|
|
# format it to Tcl code to denote the "normal" state |
|
|
|
state = state[0] or '{}' |
|
|
|
|
|
|
|
if isinstance(val, (list, tuple)): # val needs to be grouped |
|
|
|
val = "{%s}" % ' '.join(map(str, val)) |
|
|
|
|
|
|
|
opt_val.append("%s %s" % (state, val)) |
|
|
|
|
|
|
|
opts.append(("-%s" % opt, format % ' '.join(opt_val))) |
|
|
|
opts.extend(("-%s" % opt, |
|
|
|
_format_optvalue(_mapdict_values(value), script))) |
|
|
|
|
|
|
|
return _flatten(opts) |
|
|
|
|
|
|
|
@ -129,7 +122,7 @@ def _format_elemcreate(etype, script=False, *args, **kw): |
|
|
|
iname = args[0] |
|
|
|
# next args, if any, are statespec/value pairs which is almost |
|
|
|
# a mapdict, but we just need the value |
|
|
|
imagespec = _format_mapdict({None: args[1:]})[1] |
|
|
|
imagespec = _join(_mapdict_values(args[1:])) |
|
|
|
spec = "%s %s" % (iname, imagespec) |
|
|
|
|
|
|
|
else: |
|
|
|
@ -138,7 +131,7 @@ def _format_elemcreate(etype, script=False, *args, **kw): |
|
|
|
# themed styles on Windows XP and Vista. |
|
|
|
# Availability: Tk 8.6, Windows XP and Vista. |
|
|
|
class_name, part_id = args[:2] |
|
|
|
statemap = _format_mapdict({None: args[2:]})[1] |
|
|
|
statemap = _join(_mapdict_values(args[2:])) |
|
|
|
spec = "%s %s %s" % (class_name, part_id, statemap) |
|
|
|
|
|
|
|
opts = _format_optdict(kw, script) |
|
|
|
@ -148,11 +141,11 @@ def _format_elemcreate(etype, script=False, *args, **kw): |
|
|
|
# otherwise it will clone {} (empty element) |
|
|
|
spec = args[0] # theme name |
|
|
|
if len(args) > 1: # elementfrom specified |
|
|
|
opts = (args[1], ) |
|
|
|
opts = (_format_optvalue(args[1], script),) |
|
|
|
|
|
|
|
if script: |
|
|
|
spec = '{%s}' % spec |
|
|
|
opts = ' '.join(map(str, opts)) |
|
|
|
opts = ' '.join(opts) |
|
|
|
|
|
|
|
return spec, opts |
|
|
|
|
|
|
|
@ -189,7 +182,7 @@ def _format_layoutlist(layout, indent=0, indent_size=2): |
|
|
|
for layout_elem in layout: |
|
|
|
elem, opts = layout_elem |
|
|
|
opts = opts or {} |
|
|
|
fopts = ' '.join(map(str, _format_optdict(opts, True, "children"))) |
|
|
|
fopts = ' '.join(_format_optdict(opts, True, ("children",))) |
|
|
|
head = "%s%s%s" % (' ' * indent, elem, (" %s" % fopts) if fopts else '') |
|
|
|
|
|
|
|
if "children" in opts: |
|
|
|
@ -215,11 +208,11 @@ def _script_from_settings(settings): |
|
|
|
for name, opts in settings.items(): |
|
|
|
# will format specific keys according to Tcl code |
|
|
|
if opts.get('configure'): # format 'configure' |
|
|
|
s = ' '.join(map(str, _format_optdict(opts['configure'], True))) |
|
|
|
s = ' '.join(_format_optdict(opts['configure'], True)) |
|
|
|
script.append("ttk::style configure %s %s;" % (name, s)) |
|
|
|
|
|
|
|
if opts.get('map'): # format 'map' |
|
|
|
s = ' '.join(map(str, _format_mapdict(opts['map'], True))) |
|
|
|
s = ' '.join(_format_mapdict(opts['map'], True)) |
|
|
|
script.append("ttk::style map %s %s;" % (name, s)) |
|
|
|
|
|
|
|
if 'layout' in opts: # format 'layout' which may be empty |
|
|
|
@ -706,30 +699,9 @@ class Combobox(Entry): |
|
|
|
exportselection, justify, height, postcommand, state, |
|
|
|
textvariable, values, width |
|
|
|
""" |
|
|
|
# The "values" option may need special formatting, so leave to |
|
|
|
# _format_optdict the responsibility to format it |
|
|
|
if "values" in kw: |
|
|
|
kw["values"] = _format_optdict({'v': kw["values"]})[1] |
|
|
|
|
|
|
|
Entry.__init__(self, master, "ttk::combobox", **kw) |
|
|
|
|
|
|
|
|
|
|
|
def __setitem__(self, item, value): |
|
|
|
if item == "values": |
|
|
|
value = _format_optdict({item: value})[1] |
|
|
|
|
|
|
|
Entry.__setitem__(self, item, value) |
|
|
|
|
|
|
|
|
|
|
|
def configure(self, cnf=None, **kw): |
|
|
|
"""Custom Combobox configure, created to properly format the values |
|
|
|
option.""" |
|
|
|
if "values" in kw: |
|
|
|
kw["values"] = _format_optdict({'v': kw["values"]})[1] |
|
|
|
|
|
|
|
return Entry.configure(self, cnf, **kw) |
|
|
|
|
|
|
|
|
|
|
|
def current(self, newindex=None): |
|
|
|
"""If newindex is supplied, sets the combobox value to the |
|
|
|
element at position newindex in the list of values. Otherwise, |
|
|
|
|