Separately track modifier keys for mouse events. · matplotlib/matplotlib@794c623 · GitHub
Skip to content

Commit 794c623

Browse files
committed
Separately track modifier keys for mouse events.
Whether the event modifiers are directly available on enter/leave events depends on the backend, but all are handled here (except possibly for macos, which I haven't checked).
1 parent 49724bf commit 794c623

10 files changed

Lines changed: 255 additions & 119 deletions

File tree

lib/matplotlib/backend_bases.py

Lines changed: 10 additions & 4 deletions

lib/matplotlib/backends/_backend_tk.py

Lines changed: 34 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -273,16 +273,19 @@ def _event_mpl_coords(self, event):
273273
def motion_notify_event(self, event):
274274
MouseEvent("motion_notify_event", self,
275275
*self._event_mpl_coords(event),
276+
modifiers=self._mpl_modifiers(event),
276277
guiEvent=event)._process()
277278

278279
def enter_notify_event(self, event):
279280
LocationEvent("figure_enter_event", self,
280281
*self._event_mpl_coords(event),
282+
modifiers=self._mpl_modifiers(event),
281283
guiEvent=event)._process()
282284

283285
def leave_notify_event(self, event):
284286
LocationEvent("figure_leave_event", self,
285287
*self._event_mpl_coords(event),
288+
modifiers=self._mpl_modifiers(event),
286289
guiEvent=event)._process()
287290

288291
def button_press_event(self, event, dblclick=False):
@@ -294,6 +297,7 @@ def button_press_event(self, event, dblclick=False):
294297
num = {2: 3, 3: 2}.get(num, num)
295298
MouseEvent("button_press_event", self,
296299
*self._event_mpl_coords(event), num, dblclick=dblclick,
300+
modifiers=self._mpl_modifiers(event),
297301
guiEvent=event)._process()
298302

299303
def button_dblclick_event(self, event):
@@ -305,13 +309,15 @@ def button_release_event(self, event):
305309
num = {2: 3, 3: 2}.get(num, num)
306310
MouseEvent("button_release_event", self,
307311
*self._event_mpl_coords(event), num,
312+
modifiers=self._mpl_modifiers(event),
308313
guiEvent=event)._process()
309314

310315
def scroll_event(self, event):
311316
num = getattr(event, 'num', None)
312317
step = 1 if num == 4 else -1 if num == 5 else 0
313318
MouseEvent("scroll_event", self,
314319
*self._event_mpl_coords(event), step=step,
320+
modifiers=self._mpl_modifiers(event),
315321
guiEvent=event)._process()
316322

317323
def scroll_event_windows(self, event):
@@ -325,12 +331,10 @@ def scroll_event_windows(self, event):
325331
- self._tkcanvas.canvasy(event.y_root - w.winfo_rooty()))
326332
step = event.delta / 120
327333
MouseEvent("scroll_event", self,
328-
x, y, step=step, guiEvent=event)._process()
329-
330-
def _get_key(self, event):
331-
unikey = event.char
332-
key = cbook._unikey_or_keysym_to_mplkey(unikey, event.keysym)
334+
x, y, step=step, modifiers=self._mpl_modifiers(event),
335+
guiEvent=event)._process()
333336

337+
def _mpl_modifiers(self, event, *, exclude=None):
334338
# add modifier keys to the key string. Bit details originate from
335339
# http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
336340
# BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004;
@@ -339,32 +343,33 @@ def _get_key(self, event):
339343
# In general, the modifier key is excluded from the modifier flag,
340344
# however this is not the case on "darwin", so double check that
341345
# we aren't adding repeat modifier flags to a modifier key.
342-
if sys.platform == 'win32':
343-
modifiers = [(2, 'ctrl', 'control'),
344-
(17, 'alt', 'alt'),
345-
(0, 'shift', 'shift'),
346-
]
347-
elif sys.platform == 'darwin':
348-
modifiers = [(2, 'ctrl', 'control'),
349-
(4, 'alt', 'alt'),
350-
(0, 'shift', 'shift'),
351-
(3, 'super', 'super'),
352-
]
353-
else:
354-
modifiers = [(2, 'ctrl', 'control'),
355-
(3, 'alt', 'alt'),
356-
(0, 'shift', 'shift'),
357-
(6, 'super', 'super'),
358-
]
346+
modifiers = [
347+
("ctrl", 2, "control"),
348+
("alt", 17, "alt"),
349+
("shift", 0, "shift"),
350+
] if sys.platform == "win32" else [
351+
("ctrl", 2, "control"),
352+
("alt", 4, "alt"),
353+
("shift", 0, "shift"),
354+
("super", 3, "super"),
355+
] if sys.platform == "darwin" else [
356+
("ctrl", 2, "control"),
357+
("alt", 3, "alt"),
358+
("shift", 0, "shift"),
359+
("super", 6, "super"),
360+
]
361+
return [name for name, mod, key in modifiers
362+
if event.state & (1 << mod) and exclude != key]
359363

364+
def _get_key(self, event):
365+
unikey = event.char
366+
key = cbook._unikey_or_keysym_to_mplkey(unikey, event.keysym)
360367
if key is not None:
361-
# shift is not added to the keys as this is already accounted for
362-
for bitmask, prefix, key_name in modifiers:
363-
if event.state & (1 << bitmask) and key_name not in key:
364-
if not (prefix == 'shift' and unikey):
365-
key = '{0}+{1}'.format(prefix, key)
366-
367-
return key
368+
mods = self._mpl_modifiers(event)
369+
# shift is not added to the keys as this is already accounted for.
370+
if "shift" in mods and unikey:
371+
mods.remove("shift")
372+
return "+".join([*mods, key])
368373

369374
def key_press(self, event):
370375
KeyEvent("key_press_event", self,

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 27 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -133,19 +133,23 @@ def _mpl_coords(self, event=None):
133133

134134
def scroll_event(self, widget, event):
135135
step = 1 if event.direction == Gdk.ScrollDirection.UP else -1
136-
MouseEvent("scroll_event", self, *self._mpl_coords(event), step=step,
136+
MouseEvent("scroll_event", self,
137+
*self._mpl_coords(event), step=step,
138+
modifiers=self._mpl_modifiers(event.state),
137139
guiEvent=event)._process()
138140
return False # finish event propagation?
139141

140142
def button_press_event(self, widget, event):
141143
MouseEvent("button_press_event", self,
142144
*self._mpl_coords(event), event.button,
145+
modifiers=self._mpl_modifiers(event.state),
143146
guiEvent=event)._process()
144147
return False # finish event propagation?
145148

146149
def button_release_event(self, widget, event):
147150
MouseEvent("button_release_event", self,
148151
*self._mpl_coords(event), event.button,
152+
modifiers=self._mpl_modifiers(event.state),
149153
guiEvent=event)._process()
150154
return False # finish event propagation?
151155

@@ -163,15 +167,22 @@ def key_release_event(self, widget, event):
163167

164168
def motion_notify_event(self, widget, event):
165169
MouseEvent("motion_notify_event", self, *self._mpl_coords(event),
170+
modifiers=self._mpl_modifiers(event.state),
166171
guiEvent=event)._process()
167172
return False # finish event propagation?
168173

169174
def enter_notify_event(self, widget, event):
175+
gtk_mods = Gdk.Keymap.get_for_display(
176+
self.get_display()).get_modifier_state()
170177
LocationEvent("figure_enter_event", self, *self._mpl_coords(event),
178+
modifiers=self._mpl_modifiers(gtk_mods),
171179
guiEvent=event)._process()
172180

173181
def leave_notify_event(self, widget, event):
182+
gtk_mods = Gdk.Keymap.get_for_display(
183+
self.get_display()).get_modifier_state()
174184
LocationEvent("figure_leave_event", self, *self._mpl_coords(event),
185+
modifiers=self._mpl_modifiers(gtk_mods),
175186
guiEvent=event)._process()
176187

177188
def size_allocate(self, widget, allocation):
@@ -182,22 +193,24 @@ def size_allocate(self, widget, allocation):
182193
ResizeEvent("resize_event", self)._process()
183194
self.draw_idle()
184195

196+
@staticmethod
197+
def _mpl_modifiers(event_state):
198+
mod_table = [
199+
("ctrl", Gdk.ModifierType.CONTROL_MASK),
200+
("alt", Gdk.ModifierType.MOD1_MASK),
201+
("shift", Gdk.ModifierType.SHIFT_MASK),
202+
("super", Gdk.ModifierType.MOD4_MASK),
203+
]
204+
return [name for name, mask in mod_table if event_state & mask]
205+
185206
def _get_key(self, event):
186207
unikey = chr(Gdk.keyval_to_unicode(event.keyval))
187208
key = cbook._unikey_or_keysym_to_mplkey(
188-
unikey,
189-
Gdk.keyval_name(event.keyval))
190-
modifiers = [
191-
(Gdk.ModifierType.CONTROL_MASK, 'ctrl'),
192-
(Gdk.ModifierType.MOD1_MASK, 'alt'),
193-
(Gdk.ModifierType.SHIFT_MASK, 'shift'),
194-
(Gdk.ModifierType.MOD4_MASK, 'super'),
195-
]
196-
for key_mask, prefix in modifiers:
197-
if event.state & key_mask:
198-
if not (prefix == 'shift' and unikey.isprintable()):
199-
key = f'{prefix}+{key}'
200-
return key
209+
unikey, Gdk.keyval_name(event.keyval))
210+
mods = self._mpl_modifiers(event.state)
211+
if "shift" in mods and unikey.isprintable():
212+
mods.remove("shift")
213+
return "+".join([*mods, key])
201214

202215
def _update_device_pixel_ratio(self, *args, **kwargs):
203216
# We need to be careful in cases with mixed resolution displays if

lib/matplotlib/backends/backend_gtk4.py

Lines changed: 51 additions & 22 deletions

0 commit comments

Comments
 (0)