From 15f9e62df37bd4b1220d0d6db53fe6f2d1b11fea Mon Sep 17 00:00:00 2001 From: "Miss Islington (bot)" <31488909+miss-islington@users.noreply.github.com> Date: Sat, 20 Jun 2026 16:56:01 +0200 Subject: [PATCH] [3.13] gh-151678: Add tests for tkinter widget virtual events (GH-151793) (GH-151807) Verify the virtual events that widgets emit in response to user interaction, driven by generated events: <> (Listbox), <> and <> (ttk Spinbox), and <>, <> and <> (ttk Treeview). (cherry picked from commit e51b616efff845ea2a7d312aa43d5f5100064d88) Co-authored-by: Serhiy Storchaka Co-authored-by: Claude Opus 4.8 --- Lib/test/test_tkinter/test_widgets.py | 17 +++++++++++ Lib/test/test_ttk/test_widgets.py | 44 +++++++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/Lib/test/test_tkinter/test_widgets.py b/Lib/test/test_tkinter/test_widgets.py index 6c319cbb2c38..4d67c3ebc613 100644 --- a/Lib/test/test_tkinter/test_widgets.py +++ b/Lib/test/test_tkinter/test_widgets.py @@ -1837,6 +1837,23 @@ class ListboxTest(AbstractWidgetTest, unittest.TestCase): self.assertRaisesRegex(TclError, 'bad listbox index "spam"', lb.selection_includes, 'spam') + def test_selection_event(self): + # Keyboard navigation changes the selection and fires the + # <> virtual event. + lb = self.create(selectmode='browse', exportselection=False) + lb.insert(0, *('el%d' % i for i in range(5))) + lb.pack() + lb.update() + events = [] + lb.bind('<>', lambda e: events.append(lb.curselection())) + lb.focus_force() + lb.activate(0) + lb.event_generate('') + lb.event_generate('') + lb.update() + self.assertEqual(events, [(1,), (2,)]) + self.assertEqual(lb.curselection(), (2,)) + @add_standard_options(PixelSizeTests, StandardOptionsTests) class ScaleTest(AbstractWidgetTest, unittest.TestCase): diff --git a/Lib/test/test_ttk/test_widgets.py b/Lib/test/test_ttk/test_widgets.py index 5758298e8fe3..9c16a717e5d2 100644 --- a/Lib/test/test_ttk/test_widgets.py +++ b/Lib/test/test_ttk/test_widgets.py @@ -1276,6 +1276,19 @@ class SpinboxTest(EntryTest, unittest.TestCase): self.spin.update() self.assertEqual(len(success), 2) + def test_increment_decrement_events(self): + # Clicking the arrows fires the <> and <> + # virtual events. + events = [] + self.spin.bind('<>', lambda e: events.append('increment')) + self.spin.bind('<>', lambda e: events.append('decrement')) + self.spin.update() + self._click_increment_arrow() + self.spin.update() + self._click_decrement_arrow() + self.spin.update() + self.assertEqual(events, ['increment', 'decrement']) + def test_configure_to(self): self.spin['from'] = 0 self.spin['to'] = 5 @@ -1910,6 +1923,37 @@ class TreeviewTest(AbstractWidgetTest, unittest.TestCase): self.tv.selection_toggle((c1, c3)) self.assertEqual(self.tv.selection(), (c3, item2)) + def test_virtual_events(self): + # Keyboard navigation fires the <>, <> + # and <> virtual events. + parent = self.tv.insert('', 'end') + self.tv.insert(parent, 'end') + item2 = self.tv.insert('', 'end') + self.tv.pack() + self.tv.update() + selects, opens, closes = [], [], [] + self.tv.bind('<>', + lambda e: selects.append(self.tv.selection())) + self.tv.bind('<>', lambda e: opens.append(self.tv.focus())) + self.tv.bind('<>', lambda e: closes.append(self.tv.focus())) + self.tv.focus_force() + self.tv.focus(parent) + self.tv.selection_set(parent) + self.tv.update() + + self.tv.event_generate('') # Open the focused parent. + self.tv.update() + self.assertEqual(opens, [parent]) + + self.tv.event_generate('') # Close it again. + self.tv.update() + self.assertEqual(closes, [parent]) + + self.tv.event_generate('') # Move the selection. + self.tv.update() + self.assertEqual(self.tv.selection(), (item2,)) + self.assertIn((item2,), selects) + def test_set(self): self.tv['columns'] = ['A', 'B'] item = self.tv.insert('', 'end', values=['a', 'b']) -- 2.47.3