Skip to content

Commit ba0cae1

Browse files
miss-islingtonserhiy-storchakaclaude
authored
[3.15] gh-151678: Add tests for tkinter.ttk methods (GH-151736) (GH-151741)
Cover previously-untested ttk methods: * Progressbar.step, start and stop; * Treeview.parent, next, prev, see and identify_element; * Style.theme_settings; * OptionMenu.set_menu. (cherry picked from commit 7d4a0aa) Co-authored-by: Serhiy Storchaka <storchaka@gmail.com> Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 81ceb56 commit ba0cae1

3 files changed

Lines changed: 99 additions & 0 deletions

File tree

Lib/test/test_ttk/test_extensions.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -281,6 +281,24 @@ def cb_test(item):
281281

282282
optmenu.destroy()
283283

284+
def test_set_menu(self):
285+
optmenu = ttk.OptionMenu(self.root, self.textvar, 'a', 'a', 'b', 'c')
286+
menu = optmenu['menu']
287+
self.assertEqual(menu.index('end'), 2)
288+
289+
# set_menu rebuilds the menu with new values and an optional default.
290+
optmenu.set_menu('y', 'x', 'y', 'z')
291+
self.assertEqual(self.textvar.get(), 'y')
292+
self.assertEqual([menu.entrycget(i, 'label') for i in range(3)],
293+
['x', 'y', 'z'])
294+
295+
# Without a default the variable is left unchanged.
296+
optmenu.set_menu(None, 'p', 'q')
297+
self.assertEqual(self.textvar.get(), 'y')
298+
self.assertEqual([menu.entrycget(i, 'label') for i in range(2)],
299+
['p', 'q'])
300+
optmenu.destroy()
301+
284302
def test_unique_radiobuttons(self):
285303
# check that radiobuttons are unique across instances (bpo25684)
286304
items = ('a', 'b', 'c')

Lib/test/test_ttk/test_style.py

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,6 +124,22 @@ def test_theme_use(self):
124124

125125
self.style.theme_use(curr_theme)
126126

127+
def test_theme_settings(self):
128+
style = self.style
129+
theme = style.theme_use()
130+
style.theme_settings(theme, {
131+
'Test.TLabel': {
132+
'configure': {'foreground': 'red', 'background': 'blue'},
133+
'map': {'foreground': [('active', 'green')]},
134+
},
135+
})
136+
self.assertEqual(style.lookup('Test.TLabel', 'foreground'), 'red')
137+
self.assertEqual(style.lookup('Test.TLabel', 'background'), 'blue')
138+
self.assertEqual(style.map('Test.TLabel', 'foreground'),
139+
[('active', 'green')])
140+
self.assertRaises(tkinter.TclError, style.theme_settings,
141+
'nonexistingname', {})
142+
127143
def test_configure_custom_copy(self):
128144
style = self.style
129145

Lib/test/test_ttk/test_widgets.py

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -973,6 +973,27 @@ def test_configure_value(self):
973973

974974
test_configure_wraplength = requires_tk(8, 7)(StandardOptionsTests.test_configure_wraplength)
975975

976+
def test_step(self):
977+
widget = self.create(maximum=100, mode='determinate')
978+
self.assertEqual(float(widget['value']), 0.0)
979+
widget.step() # The default increment is 1.0.
980+
self.assertEqual(float(widget['value']), 1.0)
981+
widget.step(5)
982+
self.assertEqual(float(widget['value']), 6.0)
983+
widget.step(-2)
984+
self.assertEqual(float(widget['value']), 4.0)
985+
986+
def test_start_stop(self):
987+
widget = self.create(maximum=100, mode='determinate')
988+
widget.pack()
989+
widget.start() # Schedule autoincrement; no exception.
990+
widget.update()
991+
widget.stop() # Cancel it.
992+
# After stopping, the value no longer changes.
993+
value = float(widget['value'])
994+
widget.update()
995+
self.assertEqual(float(widget['value']), value)
996+
976997

977998
@unittest.skipIf(sys.platform == 'darwin',
978999
'ttk.Scrollbar is special on MacOSX')
@@ -1639,6 +1660,50 @@ def test_exists(self):
16391660
# in the tcl interpreter since tk requires an item.
16401661
self.assertRaises(tkinter.TclError, self.tv.exists, None)
16411662

1663+
def test_parent(self):
1664+
a = self.tv.insert('', 'end')
1665+
b = self.tv.insert(a, 'end')
1666+
self.assertEqual(self.tv.parent(b), a)
1667+
self.assertEqual(self.tv.parent(a), '')
1668+
self.assertRaises(tkinter.TclError, self.tv.parent, 'nonexistent')
1669+
1670+
def test_next_prev(self):
1671+
a = self.tv.insert('', 'end')
1672+
b = self.tv.insert('', 'end')
1673+
c = self.tv.insert('', 'end')
1674+
self.assertEqual(self.tv.next(a), b)
1675+
self.assertEqual(self.tv.next(b), c)
1676+
self.assertEqual(self.tv.next(c), '')
1677+
self.assertEqual(self.tv.prev(c), b)
1678+
self.assertEqual(self.tv.prev(b), a)
1679+
self.assertEqual(self.tv.prev(a), '')
1680+
self.assertRaises(tkinter.TclError, self.tv.next, 'nonexistent')
1681+
self.assertRaises(tkinter.TclError, self.tv.prev, 'nonexistent')
1682+
1683+
def test_see(self):
1684+
a = self.tv.insert('', 'end')
1685+
b = self.tv.insert(a, 'end')
1686+
# see() opens all of the item's ancestors.
1687+
self.assertFalse(self.tv.tk.getboolean(self.tv.item(a, 'open')))
1688+
self.tv.see(b)
1689+
self.assertTrue(self.tv.tk.getboolean(self.tv.item(a, 'open')))
1690+
self.assertRaises(tkinter.TclError, self.tv.see, 'nonexistent')
1691+
1692+
def test_identify_element(self):
1693+
self.tv.pack()
1694+
self.tv.wait_visibility()
1695+
parent = self.tv.insert('', 'end', text='parent')
1696+
self.tv.insert(parent, 'end', text='child')
1697+
self.tv.update()
1698+
x, y, w, h = self.tv.bbox(parent)
1699+
# The Treeitem.indicator element is packed at the left of the row in
1700+
# the Item layout on every platform and theme.
1701+
element = self.tv.identify_element(x + 8, y + h // 2)
1702+
self.assertRegex(element, r'.*indicator\z')
1703+
# The empty string is returned outside the widget.
1704+
self.assertEqual(self.tv.identify_element(-1, -1), '')
1705+
self.assertRaises(tkinter.TclError, self.tv.identify_element, None, 5)
1706+
16421707
def test_focus(self):
16431708
# nothing is focused right now
16441709
self.assertEqual(self.tv.focus(), '')

0 commit comments

Comments
 (0)