Skip to content

Commit f8ce51a

Browse files
gh-47655: Add support for user data and detail of Tk events to tkinter (GH-7142)
Expose the %d substitution as the tkinter.Event attributes: * "detail" for Enter, Leave, FocusIn, FocusOut, and ConfigureRequest events * "user_data" for virtual events Co-authored-by: Serhiy Storchaka <storchaka@gmail.com>
1 parent 1ac9d13 commit f8ce51a

File tree

4 files changed

+56
-5
lines changed

4 files changed

+56
-5
lines changed

Doc/whatsnew/3.15.rst

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1059,6 +1059,11 @@ tkinter
10591059
with outdated names.
10601060
(Contributed by Serhiy Storchaka in :gh:`143754`.)
10611061

1062+
* Added :class:`!Event` attributes :attr:`!user_data` for Tk virtual events
1063+
and :attr:`!detail` for ``Enter``, ``Leave``, ``FocusIn``, ``FocusOut``,
1064+
and ``ConfigureRequest`` events.
1065+
(Contributed by Matthias Kievernagel and Serhiy Storchaka in :gh:`47655`.)
1066+
10621067

10631068
.. _whatsnew315-tomllib-1-1-0:
10641069

Lib/test/test_tkinter/test_misc.py

Lines changed: 31 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -636,6 +636,8 @@ def test_focus(self):
636636
self.assertEqual(e.x_root, '??')
637637
self.assertEqual(e.y_root, '??')
638638
self.assertEqual(e.delta, 0)
639+
self.assertEqual(e.user_data, '??')
640+
self.assertEqual(e.detail, 'NotifyAncestor')
639641
self.assertEqual(repr(e), '<FocusIn event>')
640642

641643
def test_configure(self):
@@ -669,6 +671,8 @@ def test_configure(self):
669671
self.assertEqual(e.x_root, '??')
670672
self.assertEqual(e.y_root, '??')
671673
self.assertEqual(e.delta, 0)
674+
self.assertEqual(e.user_data, '??')
675+
self.assertEqual(e.detail, '??')
672676
self.assertEqual(repr(e), '<Configure event x=0 y=0 width=150 height=100>')
673677

674678
def test_event_generate_key_press(self):
@@ -705,6 +709,8 @@ def test_event_generate_key_press(self):
705709
self.assertEqual(e.x_root, -1)
706710
self.assertEqual(e.y_root, -1)
707711
self.assertEqual(e.delta, 0)
712+
self.assertEqual(e.user_data, '??')
713+
self.assertEqual(e.detail, '??')
708714
self.assertEqual(repr(e),
709715
f"<KeyPress event state={e.state:#x} "
710716
f"keysym=z keycode={e.keycode} char='z' x={e.x} y={e.y}>")
@@ -740,8 +746,17 @@ def test_event_generate_enter(self):
740746
self.assertEqual(e.x_root, 100 + f.winfo_rootx())
741747
self.assertEqual(e.y_root, 50 + f.winfo_rooty())
742748
self.assertEqual(e.delta, 0)
749+
self.assertEqual(e.user_data, '??')
750+
self.assertEqual(e.detail, 'NotifyAncestor')
743751
self.assertEqual(repr(e), '<Enter event focus=False x=100 y=50>')
744752

753+
f.event_generate('<Enter>', x=100, y=50, detail='NotifyPointer')
754+
self.assertEqual(len(events), 2, events)
755+
e = events[1]
756+
self.assertIs(e.type, tkinter.EventType.Enter)
757+
self.assertEqual(e.user_data, '??')
758+
self.assertEqual(e.detail, 'NotifyPointer')
759+
745760
def test_event_generate_button_press(self):
746761
f = tkinter.Frame(self.root, width=150, height=100)
747762
f.pack()
@@ -774,6 +789,8 @@ def test_event_generate_button_press(self):
774789
self.assertEqual(e.x_root, f.winfo_rootx() + 100)
775790
self.assertEqual(e.y_root, f.winfo_rooty() + 50)
776791
self.assertEqual(e.delta, 0)
792+
self.assertEqual(e.user_data, '??')
793+
self.assertEqual(e.detail, '??')
777794
self.assertEqual(repr(e), '<ButtonPress event num=1 x=100 y=50>')
778795

779796
def test_event_generate_motion(self):
@@ -808,6 +825,8 @@ def test_event_generate_motion(self):
808825
self.assertEqual(e.x_root, f.winfo_rootx() + 100)
809826
self.assertEqual(e.y_root, f.winfo_rooty() + 50)
810827
self.assertEqual(e.delta, 0)
828+
self.assertEqual(e.user_data, '??')
829+
self.assertEqual(e.detail, '??')
811830
self.assertEqual(repr(e), '<Motion event state=Button1 x=100 y=50>')
812831

813832
def test_event_generate_mouse_wheel(self):
@@ -842,9 +861,11 @@ def test_event_generate_mouse_wheel(self):
842861
self.assertEqual(e.x_root, f.winfo_rootx() + 100)
843862
self.assertEqual(e.y_root, f.winfo_rooty() + 50)
844863
self.assertEqual(e.delta, -5)
864+
self.assertEqual(e.user_data, '??')
865+
self.assertEqual(e.detail, '??')
845866
self.assertEqual(repr(e), '<MouseWheel event delta=-5 x=100 y=50>')
846867

847-
def test_generate_event_virtual_event(self):
868+
def test_event_generate_virtual_event(self):
848869
f = tkinter.Frame(self.root, width=150, height=100)
849870
f.pack()
850871
self.root.wait_visibility() # needed on Windows
@@ -876,9 +897,18 @@ def test_generate_event_virtual_event(self):
876897
self.assertEqual(e.x_root, f.winfo_rootx() + 50)
877898
self.assertEqual(e.y_root, -1)
878899
self.assertEqual(e.delta, 0)
900+
self.assertEqual(e.user_data, '')
901+
self.assertEqual(e.detail, '??')
879902
self.assertEqual(repr(e),
880903
f"<VirtualEvent event x=50 y=0>")
881904

905+
f.event_generate('<<Spam>>', data='spam')
906+
self.assertEqual(len(events), 2, events)
907+
e = events[1]
908+
self.assertIs(e.type, tkinter.EventType.VirtualEvent)
909+
self.assertEqual(e.user_data, 'spam')
910+
self.assertEqual(e.detail, '??')
911+
882912

883913
class BindTest(AbstractTkTest, unittest.TestCase):
884914

Lib/tkinter/__init__.py

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -255,6 +255,10 @@ class Event:
255255
type - type of the event as a number
256256
widget - widget in which the event occurred
257257
delta - delta of wheel movement (MouseWheel)
258+
detail - certain fixed strings (see Tcl/Tk documentation)
259+
(Enter, Leave, FocusIn, FocusOut, ConfigureRequest)
260+
user_data - data string which was passed to event_generate() or empty
261+
string (VirtualEvent)
258262
"""
259263

260264
def __repr__(self):
@@ -1538,7 +1542,7 @@ def bind(self, sequence=None, func=None, add=None):
15381542
<Alt-A> for pressing A and the Alt key (KeyPress can be omitted).
15391543
An event pattern can also be a virtual event of the form
15401544
<<AString>> where AString can be arbitrary. This
1541-
event can be generated by event_generate.
1545+
event can be generated by event_generate().
15421546
If events are concatenated they must appear shortly
15431547
after each other.
15441548
@@ -1723,7 +1727,7 @@ def _root(self):
17231727
w = self
17241728
while w.master is not None: w = w.master
17251729
return w
1726-
_subst_format = ('%#', '%b', '%f', '%h', '%k',
1730+
_subst_format = ('%#', '%b', '%d', '%f', '%h', '%k',
17271731
'%s', '%t', '%w', '%x', '%y',
17281732
'%A', '%E', '%K', '%N', '%W', '%T', '%X', '%Y', '%D')
17291733
_subst_format_str = " ".join(_subst_format)
@@ -1744,11 +1748,14 @@ def getint_event(s):
17441748
if any(isinstance(s, tuple) for s in args):
17451749
args = [s[0] if isinstance(s, tuple) and len(s) == 1 else s
17461750
for s in args]
1747-
nsign, b, f, h, k, s, t, w, x, y, A, E, K, N, W, T, X, Y, D = args
1748-
# Missing: (a, c, d, m, o, v, B, R)
1751+
nsign, b, d, f, h, k, s, t, w, x, y, A, E, K, N, W, T, X, Y, D = args
1752+
# Missing: (a, c, m, o, v, B, R)
17491753
e = Event()
17501754
# serial field: valid for all events
17511755
# number of button: ButtonPress and ButtonRelease events only
1756+
# detail: for Enter, Leave, FocusIn, FocusOut and ConfigureRequest
1757+
# events certain fixed strings (see Tcl/Tk documentation)
1758+
# user_data: data string from a virtual event or an empty string
17521759
# height field: Configure, ConfigureRequest, Create,
17531760
# ResizeRequest, and Expose events only
17541761
# keycode field: KeyPress and KeyRelease events only
@@ -1762,6 +1769,12 @@ def getint_event(s):
17621769
# KeyRelease, and Motion events
17631770
e.serial = getint(nsign)
17641771
e.num = getint_event(b)
1772+
if T == EventType.VirtualEvent:
1773+
e.user_data = d
1774+
e.detail = '??'
1775+
else:
1776+
e.user_data = '??'
1777+
e.detail = d
17651778
try: e.focus = getboolean(f)
17661779
except TclError: pass
17671780
e.height = getint_event(h)
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
Add support for user data of Tk virtual events and detail for
2+
``Enter``, ``Leave``, ``FocusIn``, ``FocusOut``, and
3+
``ConfigureRequest`` events to :mod:`tkinter`.

0 commit comments

Comments
 (0)