mirror of
https://github.com/python/cpython.git
synced 2024-11-21 12:59:38 +01:00
This commit is contained in:
parent
45d648597b
commit
4dcfd02bed
@ -1391,7 +1391,8 @@ option. If you don't know the class name of a widget, use the method
|
||||
.. method:: element_create(elementname, etype, *args, **kw)
|
||||
|
||||
Create a new element in the current theme, of the given *etype* which is
|
||||
expected to be either "image" or "from".
|
||||
expected to be either "image", "from" or "vsapi".
|
||||
The latter is only available in Tk 8.6 on Windows.
|
||||
|
||||
If "image" is used, *args* should contain the default image name followed
|
||||
by statespec/value pairs (this is the imagespec), and *kw* may have the
|
||||
@ -1439,6 +1440,63 @@ option. If you don't know the class name of a widget, use the method
|
||||
style = ttk.Style(root)
|
||||
style.element_create('plain.background', 'from', 'default')
|
||||
|
||||
If "vsapi" is used as the value of *etype*, :meth:`element_create`
|
||||
will create a new element in the current theme whose visual appearance
|
||||
is drawn using the Microsoft Visual Styles API which is responsible
|
||||
for the themed styles on Windows XP and Vista.
|
||||
*args* is expected to contain the Visual Styles class and part as
|
||||
given in the Microsoft documentation followed by an optional sequence
|
||||
of tuples of ttk states and the corresponding Visual Styles API state
|
||||
value.
|
||||
*kw* may have the following options:
|
||||
|
||||
padding=padding
|
||||
Specify the element's interior padding.
|
||||
*padding* is a list of up to four integers specifying the left,
|
||||
top, right and bottom padding quantities respectively.
|
||||
If fewer than four elements are specified, bottom defaults to top,
|
||||
right defaults to left, and top defaults to left.
|
||||
In other words, a list of three numbers specify the left, vertical,
|
||||
and right padding; a list of two numbers specify the horizontal
|
||||
and the vertical padding; a single number specifies the same
|
||||
padding all the way around the widget.
|
||||
This option may not be mixed with any other options.
|
||||
|
||||
margins=padding
|
||||
Specifies the elements exterior padding.
|
||||
*padding* is a list of up to four integers specifying the left, top,
|
||||
right and bottom padding quantities respectively.
|
||||
This option may not be mixed with any other options.
|
||||
|
||||
width=width
|
||||
Specifies the width for the element.
|
||||
If this option is set then the Visual Styles API will not be queried
|
||||
for the recommended size or the part.
|
||||
If this option is set then *height* should also be set.
|
||||
The *width* and *height* options cannot be mixed with the *padding*
|
||||
or *margins* options.
|
||||
|
||||
height=height
|
||||
Specifies the height of the element.
|
||||
See the comments for *width*.
|
||||
|
||||
Example::
|
||||
|
||||
style = ttk.Style(root)
|
||||
style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [
|
||||
('pressed', '!selected', 3),
|
||||
('active', '!selected', 2),
|
||||
('pressed', 'selected', 6),
|
||||
('active', 'selected', 5),
|
||||
('selected', 4),
|
||||
('', 1)])
|
||||
style.layout('Explorer.Pin',
|
||||
[('Explorer.Pin.pin', {'sticky': 'news'})])
|
||||
pin = ttk.Checkbutton(style='Explorer.Pin')
|
||||
pin.pack(expand=True, fill='both')
|
||||
|
||||
.. versionchanged:: 3.13
|
||||
Added support of the "vsapi" element factory.
|
||||
|
||||
.. method:: element_names()
|
||||
|
||||
|
@ -301,6 +301,11 @@ tkinter
|
||||
:meth:`!tk_busy_current`, and :meth:`!tk_busy_status`.
|
||||
(Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.)
|
||||
|
||||
* Add support of the "vsapi" element type in
|
||||
the :meth:`~tkinter.ttk.Style.element_create` method of
|
||||
:class:`tkinter.ttk.Style`.
|
||||
(Contributed by Serhiy Storchaka in :gh:`68166`.)
|
||||
|
||||
traceback
|
||||
---------
|
||||
|
||||
|
@ -258,6 +258,55 @@ class StyleTest(AbstractTkTest, unittest.TestCase):
|
||||
with self.assertRaisesRegex(TclError, 'bad option'):
|
||||
style.element_create('block2', 'image', image, spam=1)
|
||||
|
||||
def test_element_create_vsapi_1(self):
|
||||
style = self.style
|
||||
if 'xpnative' not in style.theme_names():
|
||||
self.skipTest("requires 'xpnative' theme")
|
||||
style.element_create('smallclose', 'vsapi', 'WINDOW', 19, [
|
||||
('disabled', 4),
|
||||
('pressed', 3),
|
||||
('active', 2),
|
||||
('', 1)])
|
||||
style.layout('CloseButton',
|
||||
[('CloseButton.smallclose', {'sticky': 'news'})])
|
||||
b = ttk.Button(self.root, style='CloseButton')
|
||||
b.pack(expand=True, fill='both')
|
||||
self.assertEqual(b.winfo_reqwidth(), 13)
|
||||
self.assertEqual(b.winfo_reqheight(), 13)
|
||||
|
||||
def test_element_create_vsapi_2(self):
|
||||
style = self.style
|
||||
if 'xpnative' not in style.theme_names():
|
||||
self.skipTest("requires 'xpnative' theme")
|
||||
style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [
|
||||
('pressed', '!selected', 3),
|
||||
('active', '!selected', 2),
|
||||
('pressed', 'selected', 6),
|
||||
('active', 'selected', 5),
|
||||
('selected', 4),
|
||||
('', 1)])
|
||||
style.layout('Explorer.Pin',
|
||||
[('Explorer.Pin.pin', {'sticky': 'news'})])
|
||||
pin = ttk.Checkbutton(self.root, style='Explorer.Pin')
|
||||
pin.pack(expand=True, fill='both')
|
||||
self.assertEqual(pin.winfo_reqwidth(), 16)
|
||||
self.assertEqual(pin.winfo_reqheight(), 16)
|
||||
|
||||
def test_element_create_vsapi_3(self):
|
||||
style = self.style
|
||||
if 'xpnative' not in style.theme_names():
|
||||
self.skipTest("requires 'xpnative' theme")
|
||||
style.element_create('headerclose', 'vsapi', 'EXPLORERBAR', 2, [
|
||||
('pressed', 3),
|
||||
('active', 2),
|
||||
('', 1)])
|
||||
style.layout('Explorer.CloseButton',
|
||||
[('Explorer.CloseButton.headerclose', {'sticky': 'news'})])
|
||||
b = ttk.Button(self.root, style='Explorer.CloseButton')
|
||||
b.pack(expand=True, fill='both')
|
||||
self.assertEqual(b.winfo_reqwidth(), 16)
|
||||
self.assertEqual(b.winfo_reqheight(), 16)
|
||||
|
||||
def test_theme_create(self):
|
||||
style = self.style
|
||||
curr_theme = style.theme_use()
|
||||
@ -358,6 +407,39 @@ class StyleTest(AbstractTkTest, unittest.TestCase):
|
||||
|
||||
style.theme_use(curr_theme)
|
||||
|
||||
def test_theme_create_vsapi(self):
|
||||
style = self.style
|
||||
if 'xpnative' not in style.theme_names():
|
||||
self.skipTest("requires 'xpnative' theme")
|
||||
curr_theme = style.theme_use()
|
||||
new_theme = 'testtheme5'
|
||||
style.theme_create(new_theme, settings={
|
||||
'pin' : {
|
||||
'element create': ['vsapi', 'EXPLORERBAR', 3, [
|
||||
('pressed', '!selected', 3),
|
||||
('active', '!selected', 2),
|
||||
('pressed', 'selected', 6),
|
||||
('active', 'selected', 5),
|
||||
('selected', 4),
|
||||
('', 1)]],
|
||||
},
|
||||
'Explorer.Pin' : {
|
||||
'layout': [('Explorer.Pin.pin', {'sticky': 'news'})],
|
||||
},
|
||||
})
|
||||
|
||||
style.theme_use(new_theme)
|
||||
self.assertIn('pin', style.element_names())
|
||||
self.assertEqual(style.layout('Explorer.Pin'),
|
||||
[('Explorer.Pin.pin', {'sticky': 'nswe'})])
|
||||
|
||||
pin = ttk.Checkbutton(self.root, style='Explorer.Pin')
|
||||
pin.pack(expand=True, fill='both')
|
||||
self.assertEqual(pin.winfo_reqwidth(), 16)
|
||||
self.assertEqual(pin.winfo_reqheight(), 16)
|
||||
|
||||
style.theme_use(curr_theme)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
unittest.main()
|
||||
|
@ -179,7 +179,7 @@ class InternalFunctionsTest(unittest.TestCase):
|
||||
# don't format returned values as a tcl script
|
||||
# minimum acceptable for image type
|
||||
self.assertEqual(ttk._format_elemcreate('image', False, 'test'),
|
||||
("test ", ()))
|
||||
("test", ()))
|
||||
# specifying a state spec
|
||||
self.assertEqual(ttk._format_elemcreate('image', False, 'test',
|
||||
('', 'a')), ("test {} a", ()))
|
||||
@ -203,17 +203,19 @@ class InternalFunctionsTest(unittest.TestCase):
|
||||
# don't format returned values as a tcl script
|
||||
# minimum acceptable for vsapi
|
||||
self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b'),
|
||||
("a b ", ()))
|
||||
('a', 'b', ('', 1), ()))
|
||||
# now with a state spec with multiple states
|
||||
self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b',
|
||||
('a', 'b', 'c')), ("a b {a b} c", ()))
|
||||
[('a', 'b', 'c')]), ('a', 'b', ('a b', 'c'), ()))
|
||||
# state spec and option
|
||||
self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b',
|
||||
('a', 'b'), opt='x'), ("a b a b", ("-opt", "x")))
|
||||
[('a', 'b')], opt='x'), ('a', 'b', ('a', 'b'), ("-opt", "x")))
|
||||
# format returned values as a tcl script
|
||||
# state spec with a multivalue and an option
|
||||
self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b',
|
||||
('a', 'b', [1, 2]), opt='x'), ("{a b {a b} {1 2}}", "-opt x"))
|
||||
opt='x'), ("a b {{} 1}", "-opt x"))
|
||||
self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b',
|
||||
[('a', 'b', [1, 2])], opt='x'), ("a b {{a b} {1 2}}", "-opt x"))
|
||||
|
||||
# Testing type = from
|
||||
# from type expects at least a type name
|
||||
@ -222,9 +224,9 @@ class InternalFunctionsTest(unittest.TestCase):
|
||||
self.assertEqual(ttk._format_elemcreate('from', False, 'a'),
|
||||
('a', ()))
|
||||
self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'),
|
||||
('a', ('b', )))
|
||||
('a', ('b',)))
|
||||
self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'),
|
||||
('{a}', 'b'))
|
||||
('a', 'b'))
|
||||
|
||||
|
||||
def test_format_layoutlist(self):
|
||||
@ -326,6 +328,22 @@ class InternalFunctionsTest(unittest.TestCase):
|
||||
"ttk::style element create thing image {name {state1 state2} val} "
|
||||
"-opt {3 2m}")
|
||||
|
||||
vsapi = {'pin': {'element create':
|
||||
['vsapi', 'EXPLORERBAR', 3, [
|
||||
('pressed', '!selected', 3),
|
||||
('active', '!selected', 2),
|
||||
('pressed', 'selected', 6),
|
||||
('active', 'selected', 5),
|
||||
('selected', 4),
|
||||
('', 1)]]}}
|
||||
self.assertEqual(ttk._script_from_settings(vsapi),
|
||||
"ttk::style element create pin vsapi EXPLORERBAR 3 {"
|
||||
"{pressed !selected} 3 "
|
||||
"{active !selected} 2 "
|
||||
"{pressed selected} 6 "
|
||||
"{active selected} 5 "
|
||||
"selected 4 "
|
||||
"{} 1} ")
|
||||
|
||||
def test_tclobj_to_py(self):
|
||||
self.assertEqual(
|
||||
|
@ -95,40 +95,47 @@ def _format_mapdict(mapdict, script=False):
|
||||
|
||||
def _format_elemcreate(etype, script=False, *args, **kw):
|
||||
"""Formats args and kw according to the given element factory etype."""
|
||||
spec = None
|
||||
specs = ()
|
||||
opts = ()
|
||||
if etype in ("image", "vsapi"):
|
||||
if etype == "image": # define an element based on an image
|
||||
# first arg should be the default image name
|
||||
iname = args[0]
|
||||
# next args, if any, are statespec/value pairs which is almost
|
||||
# a mapdict, but we just need the value
|
||||
imagespec = _join(_mapdict_values(args[1:]))
|
||||
spec = "%s %s" % (iname, imagespec)
|
||||
|
||||
if etype == "image": # define an element based on an image
|
||||
# first arg should be the default image name
|
||||
iname = args[0]
|
||||
# next args, if any, are statespec/value pairs which is almost
|
||||
# a mapdict, but we just need the value
|
||||
imagespec = (iname, *_mapdict_values(args[1:]))
|
||||
if script:
|
||||
specs = (imagespec,)
|
||||
else:
|
||||
# define an element whose visual appearance is drawn using the
|
||||
# Microsoft Visual Styles API which is responsible for the
|
||||
# themed styles on Windows XP and Vista.
|
||||
# Availability: Tk 8.6, Windows XP and Vista.
|
||||
class_name, part_id = args[:2]
|
||||
statemap = _join(_mapdict_values(args[2:]))
|
||||
spec = "%s %s %s" % (class_name, part_id, statemap)
|
||||
specs = (_join(imagespec),)
|
||||
opts = _format_optdict(kw, script)
|
||||
|
||||
if etype == "vsapi":
|
||||
# define an element whose visual appearance is drawn using the
|
||||
# Microsoft Visual Styles API which is responsible for the
|
||||
# themed styles on Windows XP and Vista.
|
||||
# Availability: Tk 8.6, Windows XP and Vista.
|
||||
if len(args) < 3:
|
||||
class_name, part_id = args
|
||||
statemap = (((), 1),)
|
||||
else:
|
||||
class_name, part_id, statemap = args
|
||||
specs = (class_name, part_id, tuple(_mapdict_values(statemap)))
|
||||
opts = _format_optdict(kw, script)
|
||||
|
||||
elif etype == "from": # clone an element
|
||||
# it expects a themename and optionally an element to clone from,
|
||||
# otherwise it will clone {} (empty element)
|
||||
spec = args[0] # theme name
|
||||
specs = (args[0],) # theme name
|
||||
if len(args) > 1: # elementfrom specified
|
||||
opts = (_format_optvalue(args[1], script),)
|
||||
|
||||
if script:
|
||||
spec = '{%s}' % spec
|
||||
specs = _join(specs)
|
||||
opts = ' '.join(opts)
|
||||
return specs, opts
|
||||
else:
|
||||
return *specs, opts
|
||||
|
||||
return spec, opts
|
||||
|
||||
def _format_layoutlist(layout, indent=0, indent_size=2):
|
||||
"""Formats a layout list so we can pass the result to ttk::style
|
||||
@ -214,10 +221,10 @@ def _script_from_settings(settings):
|
||||
|
||||
elemargs = eopts[1:argc]
|
||||
elemkw = eopts[argc] if argc < len(eopts) and eopts[argc] else {}
|
||||
spec, opts = _format_elemcreate(etype, True, *elemargs, **elemkw)
|
||||
specs, eopts = _format_elemcreate(etype, True, *elemargs, **elemkw)
|
||||
|
||||
script.append("ttk::style element create %s %s %s %s" % (
|
||||
name, etype, spec, opts))
|
||||
name, etype, specs, eopts))
|
||||
|
||||
return '\n'.join(script)
|
||||
|
||||
@ -434,9 +441,9 @@ class Style(object):
|
||||
|
||||
def element_create(self, elementname, etype, *args, **kw):
|
||||
"""Create a new element in the current theme of given etype."""
|
||||
spec, opts = _format_elemcreate(etype, False, *args, **kw)
|
||||
*specs, opts = _format_elemcreate(etype, False, *args, **kw)
|
||||
self.tk.call(self._name, "element", "create", elementname, etype,
|
||||
spec, *opts)
|
||||
*specs, *opts)
|
||||
|
||||
|
||||
def element_names(self):
|
||||
|
@ -0,0 +1,2 @@
|
||||
Add support of the "vsapi" element type in
|
||||
:meth:`tkinter.ttk.Style.element_create`.
|
Loading…
Reference in New Issue
Block a user