Skip to content

Commit 683a7a9

Browse files
bpo-42328: Fix tkinter.ttk.Style.map().
It accepts now the default state as returned by Style.map().
1 parent 7a27c7e commit 683a7a9

File tree

4 files changed

+108
-11
lines changed

4 files changed

+108
-11
lines changed

Lib/tkinter/test/test_ttk/test_functions.py

Lines changed: 3 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -137,6 +137,9 @@ def test_format_mapdict(self):
137137
result = ttk._format_mapdict(opts)
138138
self.assertEqual(result, ('-üñíćódè', 'á vãl'))
139139

140+
self.assertEqual(ttk._format_mapdict({'opt': [('value',)]}),
141+
('-opt', '{} value'))
142+
140143
# empty states
141144
valid = {'opt': [('', '', 'hi')]}
142145
self.assertEqual(ttk._format_mapdict(valid), ('-opt', '{ } hi'))
@@ -159,10 +162,6 @@ def test_format_mapdict(self):
159162
opts = {'a': None}
160163
self.assertRaises(TypeError, ttk._format_mapdict, opts)
161164

162-
# items in the value must have size >= 2
163-
self.assertRaises(IndexError, ttk._format_mapdict,
164-
{'a': [('invalid', )]})
165-
166165

167166
def test_format_elemcreate(self):
168167
self.assertTrue(ttk._format_elemcreate(None), (None, ()))

Lib/tkinter/test/test_ttk/test_style.py

Lines changed: 103 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,22 @@
11
import unittest
22
import tkinter
33
from tkinter import ttk
4+
from test import support
45
from test.support import requires, run_unittest
56
from tkinter.test.support import AbstractTkTest
67

78
requires('gui')
89

10+
CLASS_NAMES = [
11+
'.', 'ComboboxPopdownFrame', 'Heading',
12+
'Horizontal.TProgressbar', 'Horizontal.TScale', 'Item', 'Sash',
13+
'TButton', 'TCheckbutton', 'TCombobox', 'TEntry',
14+
'TLabelframe', 'TLabelframe.Label', 'TMenubutton',
15+
'TNotebook', 'TNotebook.Tab', 'Toolbutton', 'TProgressbar',
16+
'TRadiobutton', 'Treeview', 'TScale', 'TScrollbar', 'TSpinbox',
17+
'Vertical.TProgressbar', 'Vertical.TScale'
18+
]
19+
920
class StyleTest(AbstractTkTest, unittest.TestCase):
1021

1122
def setUp(self):
@@ -23,11 +34,48 @@ def test_configure(self):
2334

2435
def test_map(self):
2536
style = self.style
26-
style.map('TButton', background=[('active', 'background', 'blue')])
27-
self.assertEqual(style.map('TButton', 'background'),
28-
[('active', 'background', 'blue')] if self.wantobjects else
29-
[('active background', 'blue')])
30-
self.assertIsInstance(style.map('TButton'), dict)
37+
38+
# Single state
39+
for states in ['active'], [('active',)]:
40+
style.map('TButton', background=[('active', 'white')])
41+
self.assertEqual(style.map('TButton', 'background'), [('active', 'white')])
42+
m = style.map('TButton')
43+
self.assertIsInstance(m, dict)
44+
if self.wantobjects:
45+
self.assertEqual(m['background'], [('active', 'white')])
46+
else:
47+
self.assertEqual(m['background'], 'active white')
48+
49+
# Multiple states
50+
for states in ['pressed', '!disabled'], [('pressed', '!disabled')], ['pressed !disabled']:
51+
style.map('TButton', background=[(*states, 'black')])
52+
if self.wantobjects:
53+
self.assertEqual(style.map('TButton', 'background'),
54+
[('pressed', '!disabled', 'black')])
55+
else:
56+
self.assertEqual(style.map('TButton', 'background'),
57+
[('pressed !disabled', 'black')])
58+
m = style.map('TButton')
59+
self.assertIsInstance(m, dict)
60+
if self.wantobjects:
61+
self.assertEqual(m['background'],
62+
[('pressed', '!disabled', 'black')])
63+
else:
64+
self.assertEqual(m['background'], '{pressed !disabled} black')
65+
66+
# Default state
67+
for states in [], ['']:
68+
style.map('TButton', background=[(*states, 'grey')])
69+
if self.wantobjects:
70+
self.assertEqual(style.map('TButton', 'background'), [('grey',)])
71+
else:
72+
self.assertEqual(style.map('TButton', 'background'), [('', 'grey')])
73+
m = style.map('TButton')
74+
self.assertIsInstance(m, dict)
75+
if self.wantobjects:
76+
self.assertEqual(m['background'], [('grey',)])
77+
else:
78+
self.assertEqual(m['background'], '{} grey')
3179

3280

3381
def test_lookup(self):
@@ -86,6 +134,56 @@ def test_theme_use(self):
86134
self.style.theme_use(curr_theme)
87135

88136

137+
def test_configure_custom_copy(self):
138+
if not self.wantobjects:
139+
self.skipTest("requires wantobjects=1")
140+
141+
style = self.style
142+
143+
curr_theme = self.style.theme_use()
144+
self.addCleanup(self.style.theme_use, curr_theme)
145+
for theme in self.style.theme_names():
146+
self.style.theme_use(theme)
147+
148+
for name in CLASS_NAMES:
149+
default = style.configure(name)
150+
if not default:
151+
continue
152+
if support.verbose >= 2:
153+
print('configure', theme, name, default)
154+
newname = f'C.{name}'
155+
self.assertEqual(style.configure(newname), None)
156+
style.configure(newname, **default)
157+
self.assertEqual(style.configure(newname), default)
158+
for key, value in default.items():
159+
self.assertEqual(style.configure(newname, key), value)
160+
161+
162+
def test_map_custom_copy(self):
163+
if not self.wantobjects:
164+
self.skipTest("requires wantobjects=1")
165+
166+
style = self.style
167+
168+
curr_theme = self.style.theme_use()
169+
self.addCleanup(self.style.theme_use, curr_theme)
170+
for theme in self.style.theme_names():
171+
self.style.theme_use(theme)
172+
173+
for name in CLASS_NAMES:
174+
default = style.map(name)
175+
if not default:
176+
continue
177+
if support.verbose >= 2:
178+
print('map', theme, name, default)
179+
newname = f'C.{name}'
180+
self.assertEqual(style.map(newname), {})
181+
style.map(newname, **default)
182+
self.assertEqual(style.map(newname), default)
183+
for key, value in default.items():
184+
self.assertEqual(style.map(newname, key), value)
185+
186+
89187
tests_gui = (StyleTest, )
90188

91189
if __name__ == "__main__":

Lib/tkinter/ttk.py

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -81,8 +81,6 @@ def _mapdict_values(items):
8181
# ['active selected', 'grey', 'focus', [1, 2, 3, 4]]
8282
opt_val = []
8383
for *state, val in items:
84-
# hacks for backward compatibility
85-
state[0] # raise IndexError if empty
8684
if len(state) == 1:
8785
# if it is empty (something that evaluates to False), then
8886
# format it to Tcl code to denote the "normal" state
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
Fixed :meth:`tkinter.ttk.Style.map`. It accepts now the default state as
2+
returned by ``Style.map()``.

0 commit comments

Comments
 (0)