Skip to content

Commit 1d4569b

Browse files
committed
Implemented modifier-key tracking via sets in MouseEvents
Added support for gtk, qt, tkinter, wx
1 parent 60eaf0d commit 1d4569b

File tree

6 files changed

+89
-40
lines changed

6 files changed

+89
-40
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1513,6 +1513,8 @@ class MouseEvent(LocationEvent):
15131513
*key*
15141514
the key depressed when the mouse event triggered (see
15151515
:class:`KeyEvent`)
1516+
Note: .key is only processed if the FigureCanvas received keyboard
1517+
focus. For safer retrieval of modifier keys use .modifiers instead
15161518
15171519
*step*
15181520
number of scroll steps (positive for 'up', negative for 'down')
@@ -1891,7 +1893,7 @@ def scroll_event(self, x, y, step, guiEvent=None):
18911893
step=step, guiEvent=guiEvent)
18921894
self.callbacks.process(s, mouseevent)
18931895

1894-
def button_press_event(self, x, y, button, dblclick=False, modifiers=None, guiEvent=None):
1896+
def button_press_event(self, x, y, button, dblclick=False, guiEvent=None, modifiers=None):
18951897
"""
18961898
Backend derived classes should call this function on any mouse
18971899
button press. x,y are the canvas coords: 0,0 is lower, left.
@@ -1905,7 +1907,7 @@ def button_press_event(self, x, y, button, dblclick=False, modifiers=None, guiEv
19051907
s = 'button_press_event'
19061908

19071909
mouseevent = MouseEvent(s, self, x, y, button, self._key,
1908-
dblclick=dblclick, modifiers=modifiers, guiEvent=guiEvent)
1910+
dblclick=dblclick, guiEvent=guiEvent, modifiers=modifiers)
19091911
self.callbacks.process(s, mouseevent)
19101912

19111913
def button_release_event(self, x, y, button, guiEvent=None):

lib/matplotlib/backends/backend_gtk.py

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -202,6 +202,10 @@ class FigureCanvasGTK (gtk.DrawingArea, FigureCanvasBase):
202202
65406 : 'alt',
203203
65289 : 'tab',
204204
}
205+
modifier_keys = [[gdk.MOD4_MASK, 'super'],
206+
[gdk.MOD1_MASK, 'alt'],
207+
[gdk.CONTROL_MASK, 'ctrl'],
208+
[gdk.SHIFT_MASK, 'shift']]
205209

206210
# Setting this as a static constant prevents
207211
# this resulting expression from leaking
@@ -284,7 +288,9 @@ def button_press_event(self, widget, event):
284288
del self.last_downclick[event.button] # we do not want to eat more than one event.
285289
return False # eat.
286290
self.last_downclick[event.button] = current_time
287-
FigureCanvasBase.button_press_event(self, x, y, event.button, dblclick=dblclick, guiEvent=event)
291+
modifiers = {prefix for key_mask, prefix in modifier_keys if event.state & key_mask}
292+
FigureCanvasBase.button_press_event(self, x, y, event.button,
293+
dblclick=dblclick, guiEvent=event, modifiers=modifiers)
288294
return False # finish event propagation?
289295

290296
def button_release_event(self, widget, event):

lib/matplotlib/backends/backend_gtk3.py

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,10 @@ class FigureCanvasGTK3 (Gtk.DrawingArea, FigureCanvasBase):
170170
65439 : 'dec',
171171
65421 : 'enter',
172172
}
173+
modifier_keys = [[gdk.MOD4_MASK, 'super'],
174+
[gdk.MOD1_MASK, 'alt'],
175+
[gdk.CONTROL_MASK, 'ctrl'],
176+
[gdk.SHIFT_MASK, 'shift']]
173177

174178
# Setting this as a static constant prevents
175179
# this resulting expression from leaking
@@ -235,7 +239,8 @@ def button_press_event(self, widget, event):
235239
x = event.x
236240
# flipy so y=0 is bottom of canvas
237241
y = self.get_allocation().height - event.y
238-
FigureCanvasBase.button_press_event(self, x, y, event.button, guiEvent=event)
242+
modifiers = {prefix for key_mask, prefix in modifier_keys if event.state & key_mask}
243+
FigureCanvasBase.button_press_event(self, x, y, event.button, guiEvent=event, modifiers=modifiers)
239244
return False # finish event propagation?
240245

241246
def button_release_event(self, widget, event):

lib/matplotlib/backends/backend_qt5.py

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -254,12 +254,12 @@ def mousePressEvent(self, event):
254254
# flipy so y=0 is bottom of canvas
255255
y = self.figure.bbox.height - event.pos().y()
256256
button = self.buttond.get(event.button())
257-
modifiers = "+".join([name for name, mod_key, qt_key in MODIFIER_KEYS
258-
if (int(event.modifiers()) & mod_key) == mod_key])
257+
modifiers = {name for name, mod_key, qt_key in MODIFIER_KEYS
258+
if event.modifiers() & mod_key}
259259
if button is not None:
260260
FigureCanvasBase.button_press_event(self, x, y, button,
261-
modifiers=modifiers,
262-
guiEvent=event)
261+
guiEvent=event,
262+
modifiers=modifiers)
263263
if DEBUG:
264264
print('button pressed:', event.button())
265265

lib/matplotlib/backends/backend_tkagg.py

Lines changed: 34 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -260,6 +260,30 @@ def filter_destroy(evt):
260260
self.close_event()
261261
root.bind("<Destroy>", filter_destroy, "+")
262262

263+
# Dictionary for adding modifier keys to the key string.
264+
# Bit details originate from
265+
# http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
266+
# BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004;
267+
# BIT_LEFT_ALT = 0x008; BIT_NUMLOCK = 0x010; BIT_RIGHT_ALT = 0x080;
268+
# BIT_MB_1 = 0x100; BIT_MB_2 = 0x200; BIT_MB_3 = 0x400;
269+
# In general, the modifier key is excluded from the modifier flag,
270+
# however this is not the case on "darwin", so double check that
271+
# we aren't adding repeat modifier flags to a modifier key.
272+
if sys.platform == 'win32':
273+
self.MOD_KEYS = [(17, 'alt', 'alt'),
274+
(2, 'ctrl', 'control'),
275+
]
276+
elif sys.platform == 'darwin':
277+
self.MOD_KEYS = [(3, 'super', 'super'),
278+
(4, 'alt', 'alt'),
279+
(2, 'ctrl', 'control'),
280+
]
281+
else:
282+
self.MOD_KEYS = [(6, 'super', 'super'),
283+
(3, 'alt', 'alt'),
284+
(2, 'ctrl', 'control'),
285+
]
286+
263287
self._master = master
264288
self._tkcanvas.focus_set()
265289

@@ -395,16 +419,18 @@ def button_press_event(self, event, dblclick=False):
395419
# flipy so y=0 is bottom of canvas
396420
y = self.figure.bbox.height - event.y
397421
num = getattr(event, 'num', None)
422+
modifiers = self._get_modifiers(event)
398423

399424
if sys.platform=='darwin':
400425
# 2 and 3 were reversed on the OSX platform I
401426
# tested under tkagg
402427
if num==2: num=3
403428
elif num==3: num=2
404429

405-
FigureCanvasBase.button_press_event(self, x, y, num, dblclick=dblclick, guiEvent=event)
430+
FigureCanvasBase.button_press_event(self, x, y, num, dblclick=dblclick,
431+
guiEvent=event, modifiers=modifiers)
406432

407-
def button_dblclick_event(self,event):
433+
def button_dblclick_event(self, event):
408434
self.button_press_event(event,dblclick=True)
409435

410436
def button_release_event(self, event):
@@ -455,37 +481,19 @@ def _get_key(self, event):
455481
else:
456482
key = None
457483

458-
# add modifier keys to the key string. Bit details originate from
459-
# http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
460-
# BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004;
461-
# BIT_LEFT_ALT = 0x008; BIT_NUMLOCK = 0x010; BIT_RIGHT_ALT = 0x080;
462-
# BIT_MB_1 = 0x100; BIT_MB_2 = 0x200; BIT_MB_3 = 0x400;
463-
# In general, the modifier key is excluded from the modifier flag,
464-
# however this is not the case on "darwin", so double check that
465-
# we aren't adding repeat modifier flags to a modifier key.
466-
if sys.platform == 'win32':
467-
modifiers = [(17, 'alt', 'alt'),
468-
(2, 'ctrl', 'control'),
469-
]
470-
elif sys.platform == 'darwin':
471-
modifiers = [(3, 'super', 'super'),
472-
(4, 'alt', 'alt'),
473-
(2, 'ctrl', 'control'),
474-
]
475-
else:
476-
modifiers = [(6, 'super', 'super'),
477-
(3, 'alt', 'alt'),
478-
(2, 'ctrl', 'control'),
479-
]
480-
481484
if key is not None:
482485
# note, shift is not added to the keys as this is already accounted for
483-
for bitmask, prefix, key_name in modifiers:
486+
for bitmask, prefix, key_name in self.MOD_KEYS:
484487
if event.state & (1 << bitmask) and key_name not in key:
485488
key = '{0}+{1}'.format(prefix, key)
486489

487490
return key
488491

492+
def _get_modifiers(self, event):
493+
modifiers = {prefix for bitmask, prefix, key_name in self.MOD_KEYS
494+
if event.state & (1 << bitmask)}
495+
return modifiers
496+
489497
def key_press(self, event):
490498
key = self._get_key(event)
491499
FigureCanvasBase.key_press_event(self, key, guiEvent=event)

lib/matplotlib/backends/backend_wx.py

Lines changed: 34 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1013,6 +1013,19 @@ def _get_key(self, evt):
10131013

10141014
return key
10151015

1016+
def _get_modifiers(self, evt):
1017+
"""
1018+
Extracts modifier keys from a wx MouseEvent
1019+
"""
1020+
MOD_KEYS = {"AltDown": "alt",
1021+
"CmdDown": "ctrl",
1022+
"ControlDown": "ctrl",
1023+
"ShiftDown": "shift"
1024+
}
1025+
modifiers = {prefix for attr, prefix in MOD_KEYS
1026+
if getattr(evt, attr)()}
1027+
return modifiers
1028+
10161029
def _onIdle(self, evt):
10171030
'a GUI idle event'
10181031
evt.Skip()
@@ -1037,16 +1050,21 @@ def _onRightButtonDown(self, evt):
10371050
y = self.figure.bbox.height - evt.GetY()
10381051
evt.Skip()
10391052
self.CaptureMouse()
1040-
FigureCanvasBase.button_press_event(self, x, y, 3, guiEvent=evt)
1053+
modifiers = self._get_modifiers(evt)
1054+
FigureCanvasBase.button_press_event(self, x, y, 3,
1055+
guiEvent=evt,
1056+
modifiers=modifiers)
10411057

10421058
def _onRightButtonDClick(self, evt):
10431059
"""Start measuring on an axis."""
10441060
x = evt.GetX()
10451061
y = self.figure.bbox.height - evt.GetY()
10461062
evt.Skip()
10471063
self.CaptureMouse()
1064+
modifiers = self._get_modifiers(evt)
10481065
FigureCanvasBase.button_press_event(self, x, y, 3,
1049-
dblclick=True, guiEvent=evt)
1066+
dblclick=True, guiEvent=evt,
1067+
modifiers=modifiers)
10501068

10511069
def _onRightButtonUp(self, evt):
10521070
"""End measuring on an axis."""
@@ -1063,16 +1081,21 @@ def _onLeftButtonDown(self, evt):
10631081
y = self.figure.bbox.height - evt.GetY()
10641082
evt.Skip()
10651083
self.CaptureMouse()
1066-
FigureCanvasBase.button_press_event(self, x, y, 1, guiEvent=evt)
1084+
modifiers = self._get_modifiers(evt)
1085+
FigureCanvasBase.button_press_event(self, x, y, 1,
1086+
guiEvent=evt,
1087+
modifiers=modifiers)
10671088

10681089
def _onLeftButtonDClick(self, evt):
10691090
"""Start measuring on an axis."""
10701091
x = evt.GetX()
10711092
y = self.figure.bbox.height - evt.GetY()
10721093
evt.Skip()
10731094
self.CaptureMouse()
1095+
modifiers = self._get_modifiers(evt)
10741096
FigureCanvasBase.button_press_event(self, x, y, 1,
1075-
dblclick=True, guiEvent=evt)
1097+
dblclick=True, guiEvent=evt,
1098+
modifiers=modifiers)
10761099

10771100
def _onLeftButtonUp(self, evt):
10781101
"""End measuring on an axis."""
@@ -1091,16 +1114,21 @@ def _onMiddleButtonDown(self, evt):
10911114
y = self.figure.bbox.height - evt.GetY()
10921115
evt.Skip()
10931116
self.CaptureMouse()
1094-
FigureCanvasBase.button_press_event(self, x, y, 2, guiEvent=evt)
1117+
modifiers = self._get_modifiers(evt)
1118+
FigureCanvasBase.button_press_event(self, x, y, 2,
1119+
guiEvent=evt,
1120+
modifiers=modifiers)
10951121

10961122
def _onMiddleButtonDClick(self, evt):
10971123
"""Start measuring on an axis."""
10981124
x = evt.GetX()
10991125
y = self.figure.bbox.height - evt.GetY()
11001126
evt.Skip()
11011127
self.CaptureMouse()
1128+
modifiers = self._get_modifiers(evt)
11021129
FigureCanvasBase.button_press_event(self, x, y, 2,
1103-
dblclick=True, guiEvent=evt)
1130+
dblclick=True, guiEvent=evt,
1131+
modifiers=modifiers)
11041132

11051133
def _onMiddleButtonUp(self, evt):
11061134
"""End measuring on an axis."""

0 commit comments

Comments
 (0)
pFad - Phonifier reborn

Pfad - The Proxy pFad of © 2024 Garber Painting. All rights reserved.

Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.


Alternative Proxies:

Alternative Proxy

pFad Proxy

pFad v3 Proxy

pFad v4 Proxy