pFad - Phone/Frame/Anonymizer/Declutterfier! Saves Data!


--- a PPN by Garber Painting Akron. With Image Size Reduction included!

URL: http://github.com/python/cpython/commit/4dcfd02bed0d7958703ef44baa79a4a98475be2e

241e157469407.css" /> gh-68166: Add support of "vsapi" in ttk.Style.element_create() (GH-11… · python/cpython@4dcfd02 · GitHub
Skip to content

Commit 4dcfd02

Browse files
gh-68166: Add support of "vsapi" in ttk.Style.element_create() (GH-111393)
1 parent 45d6485 commit 4dcfd02

File tree

6 files changed

+204
-32
lines changed

6 files changed

+204
-32
lines changed

Doc/library/tkinter.ttk.rst

Lines changed: 59 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1391,7 +1391,8 @@ option. If you don't know the class name of a widget, use the method
13911391
.. method:: element_create(elementname, etype, *args, **kw)
13921392

13931393
Create a new element in the current theme, of the given *etype* which is
1394-
expected to be either "image" or "from".
1394+
expected to be either "image", "from" or "vsapi".
1395+
The latter is only available in Tk 8.6 on Windows.
13951396

13961397
If "image" is used, *args* should contain the default image name followed
13971398
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
14391440
style = ttk.Style(root)
14401441
style.element_create('plain.background', 'from', 'default')
14411442

1443+
If "vsapi" is used as the value of *etype*, :meth:`element_create`
1444+
will create a new element in the current theme whose visual appearance
1445+
is drawn using the Microsoft Visual Styles API which is responsible
1446+
for the themed styles on Windows XP and Vista.
1447+
*args* is expected to contain the Visual Styles class and part as
1448+
given in the Microsoft documentation followed by an optional sequence
1449+
of tuples of ttk states and the corresponding Visual Styles API state
1450+
value.
1451+
*kw* may have the following options:
1452+
1453+
padding=padding
1454+
Specify the element's interior padding.
1455+
*padding* is a list of up to four integers specifying the left,
1456+
top, right and bottom padding quantities respectively.
1457+
If fewer than four elements are specified, bottom defaults to top,
1458+
right defaults to left, and top defaults to left.
1459+
In other words, a list of three numbers specify the left, vertical,
1460+
and right padding; a list of two numbers specify the horizontal
1461+
and the vertical padding; a single number specifies the same
1462+
padding all the way around the widget.
1463+
This option may not be mixed with any other options.
1464+
1465+
margins=padding
1466+
Specifies the elements exterior padding.
1467+
*padding* is a list of up to four integers specifying the left, top,
1468+
right and bottom padding quantities respectively.
1469+
This option may not be mixed with any other options.
1470+
1471+
width=width
1472+
Specifies the width for the element.
1473+
If this option is set then the Visual Styles API will not be queried
1474+
for the recommended size or the part.
1475+
If this option is set then *height* should also be set.
1476+
The *width* and *height* options cannot be mixed with the *padding*
1477+
or *margins* options.
1478+
1479+
height=height
1480+
Specifies the height of the element.
1481+
See the comments for *width*.
1482+
1483+
Example::
1484+
1485+
style = ttk.Style(root)
1486+
style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [
1487+
('pressed', '!selected', 3),
1488+
('active', '!selected', 2),
1489+
('pressed', 'selected', 6),
1490+
('active', 'selected', 5),
1491+
('selected', 4),
1492+
('', 1)])
1493+
style.layout('Explorer.Pin',
1494+
[('Explorer.Pin.pin', {'sticky': 'news'})])
1495+
pin = ttk.Checkbutton(style='Explorer.Pin')
1496+
pin.pack(expand=True, fill='both')
1497+
1498+
.. versionchanged:: 3.13
1499+
Added support of the "vsapi" element factory.
14421500

14431501
.. method:: element_names()
14441502

Doc/whatsnew/3.13.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -301,6 +301,11 @@ tkinter
301301
:meth:`!tk_busy_current`, and :meth:`!tk_busy_status`.
302302
(Contributed by Miguel, klappnase and Serhiy Storchaka in :gh:`72684`.)
303303

304+
* Add support of the "vsapi" element type in
305+
the :meth:`~tkinter.ttk.Style.element_create` method of
306+
:class:`tkinter.ttk.Style`.
307+
(Contributed by Serhiy Storchaka in :gh:`68166`.)
308+
304309
traceback
305310
---------
306311

Lib/test/test_ttk/test_style.py

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -258,6 +258,55 @@ def test_element_create_image_errors(self):
258258
with self.assertRaisesRegex(TclError, 'bad option'):
259259
style.element_create('block2', 'image', image, spam=1)
260260

261+
def test_element_create_vsapi_1(self):
262+
style = self.style
263+
if 'xpnative' not in style.theme_names():
264+
self.skipTest("requires 'xpnative' theme")
265+
style.element_create('smallclose', 'vsapi', 'WINDOW', 19, [
266+
('disabled', 4),
267+
('pressed', 3),
268+
('active', 2),
269+
('', 1)])
270+
style.layout('CloseButton',
271+
[('CloseButton.smallclose', {'sticky': 'news'})])
272+
b = ttk.Button(self.root, style='CloseButton')
273+
b.pack(expand=True, fill='both')
274+
self.assertEqual(b.winfo_reqwidth(), 13)
275+
self.assertEqual(b.winfo_reqheight(), 13)
276+
277+
def test_element_create_vsapi_2(self):
278+
style = self.style
279+
if 'xpnative' not in style.theme_names():
280+
self.skipTest("requires 'xpnative' theme")
281+
style.element_create('pin', 'vsapi', 'EXPLORERBAR', 3, [
282+
('pressed', '!selected', 3),
283+
('active', '!selected', 2),
284+
('pressed', 'selected', 6),
285+
('active', 'selected', 5),
286+
('selected', 4),
287+
('', 1)])
288+
style.layout('Explorer.Pin',
289+
[('Explorer.Pin.pin', {'sticky': 'news'})])
290+
pin = ttk.Checkbutton(self.root, style='Explorer.Pin')
291+
pin.pack(expand=True, fill='both')
292+
self.assertEqual(pin.winfo_reqwidth(), 16)
293+
self.assertEqual(pin.winfo_reqheight(), 16)
294+
295+
def test_element_create_vsapi_3(self):
296+
style = self.style
297+
if 'xpnative' not in style.theme_names():
298+
self.skipTest("requires 'xpnative' theme")
299+
style.element_create('headerclose', 'vsapi', 'EXPLORERBAR', 2, [
300+
('pressed', 3),
301+
('active', 2),
302+
('', 1)])
303+
style.layout('Explorer.CloseButton',
304+
[('Explorer.CloseButton.headerclose', {'sticky': 'news'})])
305+
b = ttk.Button(self.root, style='Explorer.CloseButton')
306+
b.pack(expand=True, fill='both')
307+
self.assertEqual(b.winfo_reqwidth(), 16)
308+
self.assertEqual(b.winfo_reqheight(), 16)
309+
261310
def test_theme_create(self):
262311
style = self.style
263312
curr_theme = style.theme_use()
@@ -358,6 +407,39 @@ def test_theme_create_image(self):
358407

359408
style.theme_use(curr_theme)
360409

410+
def test_theme_create_vsapi(self):
411+
style = self.style
412+
if 'xpnative' not in style.theme_names():
413+
self.skipTest("requires 'xpnative' theme")
414+
curr_theme = style.theme_use()
415+
new_theme = 'testtheme5'
416+
style.theme_create(new_theme, settings={
417+
'pin' : {
418+
'element create': ['vsapi', 'EXPLORERBAR', 3, [
419+
('pressed', '!selected', 3),
420+
('active', '!selected', 2),
421+
('pressed', 'selected', 6),
422+
('active', 'selected', 5),
423+
('selected', 4),
424+
('', 1)]],
425+
},
426+
'Explorer.Pin' : {
427+
'layout': [('Explorer.Pin.pin', {'sticky': 'news'})],
428+
},
429+
})
430+
431+
style.theme_use(new_theme)
432+
self.assertIn('pin', style.element_names())
433+
self.assertEqual(style.layout('Explorer.Pin'),
434+
[('Explorer.Pin.pin', {'sticky': 'nswe'})])
435+
436+
pin = ttk.Checkbutton(self.root, style='Explorer.Pin')
437+
pin.pack(expand=True, fill='both')
438+
self.assertEqual(pin.winfo_reqwidth(), 16)
439+
self.assertEqual(pin.winfo_reqheight(), 16)
440+
441+
style.theme_use(curr_theme)
442+
361443

362444
if __name__ == "__main__":
363445
unittest.main()

Lib/test/test_ttk_textonly.py

Lines changed: 25 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ def test_format_elemcreate(self):
179179
# don't format returned values as a tcl script
180180
# minimum acceptable for image type
181181
self.assertEqual(ttk._format_elemcreate('image', False, 'test'),
182-
("test ", ()))
182+
("test", ()))
183183
# specifying a state spec
184184
self.assertEqual(ttk._format_elemcreate('image', False, 'test',
185185
('', 'a')), ("test {} a", ()))
@@ -203,17 +203,19 @@ def test_format_elemcreate(self):
203203
# don't format returned values as a tcl script
204204
# minimum acceptable for vsapi
205205
self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b'),
206-
("a b ", ()))
206+
('a', 'b', ('', 1), ()))
207207
# now with a state spec with multiple states
208208
self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b',
209-
('a', 'b', 'c')), ("a b {a b} c", ()))
209+
[('a', 'b', 'c')]), ('a', 'b', ('a b', 'c'), ()))
210210
# state spec and option
211211
self.assertEqual(ttk._format_elemcreate('vsapi', False, 'a', 'b',
212-
('a', 'b'), opt='x'), ("a b a b", ("-opt", "x")))
212+
[('a', 'b')], opt='x'), ('a', 'b', ('a', 'b'), ("-opt", "x")))
213213
# format returned values as a tcl script
214214
# state spec with a multivalue and an option
215215
self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b',
216-
('a', 'b', [1, 2]), opt='x'), ("{a b {a b} {1 2}}", "-opt x"))
216+
opt='x'), ("a b {{} 1}", "-opt x"))
217+
self.assertEqual(ttk._format_elemcreate('vsapi', True, 'a', 'b',
218+
[('a', 'b', [1, 2])], opt='x'), ("a b {{a b} {1 2}}", "-opt x"))
217219

218220
# Testing type = from
219221
# from type expects at least a type name
@@ -222,9 +224,9 @@ def test_format_elemcreate(self):
222224
self.assertEqual(ttk._format_elemcreate('from', False, 'a'),
223225
('a', ()))
224226
self.assertEqual(ttk._format_elemcreate('from', False, 'a', 'b'),
225-
('a', ('b', )))
227+
('a', ('b',)))
226228
self.assertEqual(ttk._format_elemcreate('from', True, 'a', 'b'),
227-
('{a}', 'b'))
229+
('a', 'b'))
228230

229231

230232
def test_format_layoutlist(self):
@@ -326,6 +328,22 @@ def test_script_from_settings(self):
326328
"ttk::style element create thing image {name {state1 state2} val} "
327329
"-opt {3 2m}")
328330

331+
vsapi = {'pin': {'element create':
332+
['vsapi', 'EXPLORERBAR', 3, [
333+
('pressed', '!selected', 3),
334+
('active', '!selected', 2),
335+
('pressed', 'selected', 6),
336+
('active', 'selected', 5),
337+
('selected', 4),
338+
('', 1)]]}}
339+
self.assertEqual(ttk._script_from_settings(vsapi),
340+
"ttk::style element create pin vsapi EXPLORERBAR 3 {"
341+
"{pressed !selected} 3 "
342+
"{active !selected} 2 "
343+
"{pressed selected} 6 "
344+
"{active selected} 5 "
345+
"selected 4 "
346+
"{} 1} ")
329347

330348
def test_tclobj_to_py(self):
331349
self.assertEqual(

Lib/tkinter/ttk.py

Lines changed: 31 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -95,40 +95,47 @@ def _format_mapdict(mapdict, script=False):
9595

9696
def _format_elemcreate(etype, script=False, *args, **kw):
9797
"""Formats args and kw according to the given element factory etype."""
98-
spec = None
98+
specs = ()
9999
opts = ()
100-
if etype in ("image", "vsapi"):
101-
if etype == "image": # define an element based on an image
102-
# first arg should be the default image name
103-
iname = args[0]
104-
# next args, if any, are statespec/value pairs which is almost
105-
# a mapdict, but we just need the value
106-
imagespec = _join(_mapdict_values(args[1:]))
107-
spec = "%s %s" % (iname, imagespec)
108-
100+
if etype == "image": # define an element based on an image
101+
# first arg should be the default image name
102+
iname = args[0]
103+
# next args, if any, are statespec/value pairs which is almost
104+
# a mapdict, but we just need the value
105+
imagespec = (iname, *_mapdict_values(args[1:]))
106+
if script:
107+
specs = (imagespec,)
109108
else:
110-
# define an element whose visual appearance is drawn using the
111-
# Microsoft Visual Styles API which is responsible for the
112-
# themed styles on Windows XP and Vista.
113-
# Availability: Tk 8.6, Windows XP and Vista.
114-
class_name, part_id = args[:2]
115-
statemap = _join(_mapdict_values(args[2:]))
116-
spec = "%s %s %s" % (class_name, part_id, statemap)
109+
specs = (_join(imagespec),)
110+
opts = _format_optdict(kw, script)
117111

112+
if etype == "vsapi":
113+
# define an element whose visual appearance is drawn using the
114+
# Microsoft Visual Styles API which is responsible for the
115+
# themed styles on Windows XP and Vista.
116+
# Availability: Tk 8.6, Windows XP and Vista.
117+
if len(args) < 3:
118+
class_name, part_id = args
119+
statemap = (((), 1),)
120+
else:
121+
class_name, part_id, statemap = args
122+
specs = (class_name, part_id, tuple(_mapdict_values(statemap)))
118123
opts = _format_optdict(kw, script)
119124

120125
elif etype == "from": # clone an element
121126
# it expects a themename and optionally an element to clone from,
122127
# otherwise it will clone {} (empty element)
123-
spec = args[0] # theme name
128+
specs = (args[0],) # theme name
124129
if len(args) > 1: # elementfrom specified
125130
opts = (_format_optvalue(args[1], script),)
126131

127132
if script:
128-
spec = '{%s}' % spec
133+
specs = _join(specs)
129134
opts = ' '.join(opts)
135+
return specs, opts
136+
else:
137+
return *specs, opts
130138

131-
return spec, opts
132139

133140
def _format_layoutlist(layout, indent=0, indent_size=2):
134141
"""Formats a layout list so we can pass the result to ttk::style
@@ -214,10 +221,10 @@ def _script_from_settings(settings):
214221

215222
elemargs = eopts[1:argc]
216223
elemkw = eopts[argc] if argc < len(eopts) and eopts[argc] else {}
217-
spec, opts = _format_elemcreate(etype, True, *elemargs, **elemkw)
224+
specs, eopts = _format_elemcreate(etype, True, *elemargs, **elemkw)
218225

219226
script.append("ttk::style element create %s %s %s %s" % (
220-
name, etype, spec, opts))
227+
name, etype, specs, eopts))
221228

222229
return '\n'.join(script)
223230

@@ -434,9 +441,9 @@ def layout(self, style, layoutspec=None):
434441

435442
def element_create(self, elementname, etype, *args, **kw):
436443
"""Create a new element in the current theme of given etype."""
437-
spec, opts = _format_elemcreate(etype, False, *args, **kw)
444+
*specs, opts = _format_elemcreate(etype, False, *args, **kw)
438445
self.tk.call(self._name, "element", "create", elementname, etype,
439-
spec, *opts)
446+
*specs, *opts)
440447

441448

442449
def element_names(self):
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Add support of the "vsapi" element type in
2+
:meth:`tkinter.ttk.Style.element_create`.

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad © 2024 Your Company Name. All rights reserved.





Check this box to remove all script contents from the fetched content.



Check this box to remove all images from the fetched content.


Check this box to remove all CSS styles from the fetched content.


Check this box to keep images inefficiently compressed and original size.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy