https://github.com/python/cpython/commit/e51b616efff845ea2a7d312aa43d5f5100064d88
commit: e51b616efff845ea2a7d312aa43d5f5100064d88
branch: main
author: Serhiy Storchaka <[email protected]>
committer: serhiy-storchaka <[email protected]>
date: 2026-06-20T14:31:40Z
summary:

gh-151678: Add tests for tkinter widget virtual events (GH-151793)

Verify the virtual events that widgets emit in response to user
interaction, driven by generated events: <<ListboxSelect>> (Listbox),
<<Increment>> and <<Decrement>> (ttk Spinbox), and <<TreeviewSelect>>,
<<TreeviewOpen>> and <<TreeviewClose>> (ttk Treeview).

Co-authored-by: Claude Opus 4.8 <[email protected]>

files:
M Lib/test/test_tkinter/test_widgets.py
M Lib/test/test_ttk/test_widgets.py

diff --git a/Lib/test/test_tkinter/test_widgets.py 
b/Lib/test/test_tkinter/test_widgets.py
index 8ce71bc37ca2e4f..4b51d219d87e5be 100644
--- a/Lib/test/test_tkinter/test_widgets.py
+++ b/Lib/test/test_tkinter/test_widgets.py
@@ -1926,6 +1926,23 @@ def test_selection(self):
         self.assertRaisesRegex(TclError, 'bad listbox index "spam"',
                                lb.selection_includes, 'spam')
 
+    def test_selection_event(self):
+        # Keyboard navigation changes the selection and fires the
+        # <<ListboxSelect>> 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('<<ListboxSelect>>', lambda e: 
events.append(lb.curselection()))
+        lb.focus_force()
+        lb.activate(0)
+        lb.event_generate('<Down>')
+        lb.event_generate('<Down>')
+        lb.update()
+        self.assertEqual(events, [(1,), (2,)])
+        self.assertEqual(lb.curselection(), (2,))
+
 
 @add_configure_tests(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 adcd736cd40b194..a3b3c88b46edd2b 100644
--- a/Lib/test/test_ttk/test_widgets.py
+++ b/Lib/test/test_ttk/test_widgets.py
@@ -1307,6 +1307,19 @@ def test_configure_command(self):
         self.spin.update()
         self.assertEqual(len(success), 2)
 
+    def test_increment_decrement_events(self):
+        # Clicking the arrows fires the <<Increment>> and <<Decrement>>
+        # virtual events.
+        events = []
+        self.spin.bind('<<Increment>>', lambda e: events.append('increment'))
+        self.spin.bind('<<Decrement>>', 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
@@ -1945,6 +1958,37 @@ def test_selection(self):
         self.tv.selection_toggle((c1, c3))
         self.assertEqual(self.tv.selection(), (c3, item2))
 
+    def test_virtual_events(self):
+        # Keyboard navigation fires the <<TreeviewSelect>>, <<TreeviewOpen>>
+        # and <<TreeviewClose>> 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('<<TreeviewSelect>>',
+                     lambda e: selects.append(self.tv.selection()))
+        self.tv.bind('<<TreeviewOpen>>', lambda e: 
opens.append(self.tv.focus()))
+        self.tv.bind('<<TreeviewClose>>', 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('<Right>')  # Open the focused parent.
+        self.tv.update()
+        self.assertEqual(opens, [parent])
+
+        self.tv.event_generate('<Left>')  # Close it again.
+        self.tv.update()
+        self.assertEqual(closes, [parent])
+
+        self.tv.event_generate('<Down>')  # 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'])

_______________________________________________
Python-checkins mailing list -- [email protected]
To unsubscribe send an email to [email protected]
https://mail.python.org/mailman3//lists/python-checkins.python.org
Member address: [email protected]

Reply via email to