]> git.ipfire.org Git - thirdparty/Python/cpython.git/commitdiff
gh-151678: Add tests for tkinter widget virtual events (GH-151793)
authorSerhiy Storchaka <storchaka@gmail.com>
Sat, 20 Jun 2026 14:31:40 +0000 (17:31 +0300)
committerGitHub <noreply@github.com>
Sat, 20 Jun 2026 14:31:40 +0000 (14:31 +0000)
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 <noreply@anthropic.com>
Lib/test/test_tkinter/test_widgets.py
Lib/test/test_ttk/test_widgets.py

index 8ce71bc37ca2e4f27aa6c4bdbe717fb832c0baae..4b51d219d87e5be9d0d659efc535986f4e039ee9 100644 (file)
@@ -1926,6 +1926,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
+        # <<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):
index adcd736cd40b194b1f9be7e69e02eac90f0bd4d7..a3b3c88b46edd2bd80b3b8621758cde84edd93db 100644 (file)
@@ -1307,6 +1307,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 <<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 @@ 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 <<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'])