diff --git a/Lib/tkinter/__init__.py b/Lib/tkinter/__init__.py index dd7b3e138f4236..c0bf2a54226990 100644 --- a/Lib/tkinter/__init__.py +++ b/Lib/tkinter/__init__.py @@ -255,6 +255,10 @@ class Event: type - type of the event as a number widget - widget in which the event occurred delta - delta of wheel movement (MouseWheel) + detail - certain fixed strings (see tcl/tk documentation) + (Enter, Leave, FocusIn, FocusOut, ConfigureRequest) + user_data - data string which was passed to event_generate or empty string + (VirtualEvent) """ def __repr__(self): @@ -1711,7 +1715,7 @@ def _root(self): w = self while w.master is not None: w = w.master return w - _subst_format = ('%#', '%b', '%f', '%h', '%k', + _subst_format = ('%#', '%b', '%d', '%f', '%h', '%k', '%s', '%t', '%w', '%x', '%y', '%A', '%E', '%K', '%N', '%W', '%T', '%X', '%Y', '%D') _subst_format_str = " ".join(_subst_format) @@ -1732,11 +1736,14 @@ def getint_event(s): if any(isinstance(s, tuple) for s in args): args = [s[0] if isinstance(s, tuple) and len(s) == 1 else s for s in args] - nsign, b, f, h, k, s, t, w, x, y, A, E, K, N, W, T, X, Y, D = args - # Missing: (a, c, d, m, o, v, B, R) + nsign, b, d, f, h, k, s, t, w, x, y, A, E, K, N, W, T, X, Y, D = args + # Missing: (a, c, m, o, v, B, R) e = Event() # serial field: valid for all events # number of button: ButtonPress and ButtonRelease events only + # detail: for Enter, Leave, FocusIn, FocusOut and ConfigureRequest + # events certain fixed strings (see tcl/tk documentation) + # user_data: data string from a virtual event or an empty string # height field: Configure, ConfigureRequest, Create, # ResizeRequest, and Expose events only # keycode field: KeyPress and KeyRelease events only @@ -1750,6 +1757,8 @@ def getint_event(s): # KeyRelease, and Motion events e.serial = getint(nsign) e.num = getint_event(b) + e.user_data = d + e.detail = d try: e.focus = getboolean(f) except TclError: pass e.height = getint_event(h) diff --git a/Lib/tkinter/test/test_tkinter/test_event.py b/Lib/tkinter/test/test_tkinter/test_event.py new file mode 100644 index 00000000000000..d21c32ba709fd8 --- /dev/null +++ b/Lib/tkinter/test/test_tkinter/test_event.py @@ -0,0 +1,49 @@ +import unittest +import tkinter +import _tkinter +from test.support import requires, run_unittest +from tkinter.test.support import AbstractTkTest + +requires('gui') + +class EventTest(AbstractTkTest, unittest.TestCase): + + called = False + test_data = None + + # pump_events function 'borrowed' from ivan_pozdeev on stackoverflow + def pump_events(self): + while self.root.dooneevent(_tkinter.ALL_EVENTS | _tkinter.DONT_WAIT): + pass + + def test_virtual_event(self): + def receive(e): + EventTest.called = True + self.assertIsInstance(e, tkinter.Event) + self.assertEqual(e.type, tkinter.EventType.VirtualEvent) + self.assertIsInstance(e.user_data, str) + if EventTest.test_data is not None: + self.assertEqual(e.user_data, EventTest.test_data) + else: + self.assertEqual(e.user_data, '') + self.pump_events() + EventTest.called = False + b = self.root.bind('<>', lambda e:receive(e)) + self.root.event_generate('<>') + self.pump_events() + self.assertTrue(EventTest.called) + EventTest.called = False + EventTest.test_data = 'test' + self.root.event_generate('<>', data='test') + self.pump_events() + self.assertTrue(EventTest.called) + self.root.unbind('<>', b) + EventTest.called = False + self.root.event_generate('<>') + self.pump_events() + self.assertFalse(EventTest.called) + +tests_gui = (EventTest, ) + +if __name__ == "__main__": + run_unittest(*tests_gui) diff --git a/Misc/NEWS.d/next/Library/2018-05-11-12-26-16.bpo-3405.CacMw9.rst b/Misc/NEWS.d/next/Library/2018-05-11-12-26-16.bpo-3405.CacMw9.rst new file mode 100644 index 00000000000000..8caa5bccddb3c3 --- /dev/null +++ b/Misc/NEWS.d/next/Library/2018-05-11-12-26-16.bpo-3405.CacMw9.rst @@ -0,0 +1 @@ +Add support for user data of Tk virtual events to :mod:`tkinter`.