Skip to content

Commit 6d441fa

Browse files
committed
Added mouse event modifiers
1 parent 4c33d97 commit 6d441fa

File tree

7 files changed

+136
-61
lines changed

7 files changed

+136
-61
lines changed

lib/matplotlib/backend_bases.py

Lines changed: 19 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1606,10 +1606,15 @@ class MouseEvent(LocationEvent):
16061606
key : None, or str
16071607
the key depressed when the mouse event triggered (see
16081608
:class:`KeyEvent`)
1609+
Note: .key is only processed if the FigureCanvas received keyboard
1610+
focus. For safer retrieval of modifier keys use .modifiers instead
16091611
16101612
step : scalar
16111613
number of scroll steps (positive for 'up', negative for 'down')
16121614
1615+
modifiers : set
1616+
modifier keys depressed when mouse event is triggered
1617+
16131618
Examples
16141619
--------
16151620
Usage::
@@ -1620,17 +1625,18 @@ def on_press(event):
16201625
cid = fig.canvas.mpl_connect('button_press_event', on_press)
16211626
16221627
"""
1623-
x = None # x position - pixels from left of canvas
1624-
y = None # y position - pixels from right of canvas
1625-
button = None # button pressed None, 1, 2, 3
1626-
dblclick = None # whether or not the event is the result of a double click
1627-
inaxes = None # the Axes instance if mouse us over axes
1628-
xdata = None # x coord of mouse in data coords
1629-
ydata = None # y coord of mouse in data coords
1630-
step = None # scroll steps for scroll events
1628+
x = None # x position - pixels from left of canvas
1629+
y = None # y position - pixels from right of canvas
1630+
button = None # button pressed None, 1, 2, 3
1631+
dblclick = None # whether or not the event is the result of a double click
1632+
inaxes = None # the Axes instance if mouse us over axes
1633+
xdata = None # x coord of mouse in data coords
1634+
ydata = None # y coord of mouse in data coords
1635+
step = None # scroll steps for scroll events
1636+
modifiers = None # depressed modifier keys
16311637

16321638
def __init__(self, name, canvas, x, y, button=None, key=None,
1633-
step=0, dblclick=False, guiEvent=None):
1639+
step=0, dblclick=False, guiEvent=None, modifiers=None):
16341640
"""
16351641
x, y in figure coords, 0,0 = bottom, left
16361642
button pressed None, 1, 2, 3, 'up', 'down'
@@ -1640,6 +1646,7 @@ def __init__(self, name, canvas, x, y, button=None, key=None,
16401646
self.key = key
16411647
self.step = step
16421648
self.dblclick = dblclick
1649+
self.modifiers = modifiers
16431650

16441651
def __str__(self):
16451652
return ("MPL MouseEvent: xy=(%d,%d) xydata=(%s,%s) button=%s " +
@@ -1921,7 +1928,7 @@ def scroll_event(self, x, y, step, guiEvent=None):
19211928
step=step, guiEvent=guiEvent)
19221929
self.callbacks.process(s, mouseevent)
19231930

1924-
def button_press_event(self, x, y, button, dblclick=False, guiEvent=None):
1931+
def button_press_event(self, x, y, button, dblclick=False, guiEvent=None, modifiers=None):
19251932
"""
19261933
Backend derived classes should call this function on any mouse
19271934
button press. x,y are the canvas coords: 0,0 is lower, left.
@@ -1932,8 +1939,9 @@ def button_press_event(self, x, y, button, dblclick=False, guiEvent=None):
19321939
"""
19331940
self._button = button
19341941
s = 'button_press_event'
1942+
19351943
mouseevent = MouseEvent(s, self, x, y, button, self._key,
1936-
dblclick=dblclick, guiEvent=guiEvent)
1944+
dblclick=dblclick, guiEvent=guiEvent, modifiers=modifiers)
19371945
self.callbacks.process(s, mouseevent)
19381946

19391947
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
@@ -163,6 +163,10 @@ class FigureCanvasGTK (gtk.DrawingArea, FigureCanvasBase):
163163
65406 : 'alt',
164164
65289 : 'tab',
165165
}
166+
modifier_keys = [[gdk.MOD4_MASK, 'super'],
167+
[gdk.MOD1_MASK, 'alt'],
168+
[gdk.CONTROL_MASK, 'ctrl'],
169+
[gdk.SHIFT_MASK, 'shift']]
166170

167171
# Setting this as a static constant prevents
168172
# this resulting expression from leaking
@@ -250,7 +254,9 @@ def button_press_event(self, widget, event):
250254
del self.last_downclick[event.button] # we do not want to eat more than one event.
251255
return False # eat.
252256
self.last_downclick[event.button] = current_time
253-
FigureCanvasBase.button_press_event(self, x, y, event.button, dblclick=dblclick, guiEvent=event)
257+
modifiers = {prefix for key_mask, prefix in modifier_keys if event.state & key_mask}
258+
FigureCanvasBase.button_press_event(self, x, y, event.button,
259+
dblclick=dblclick, guiEvent=event, modifiers=modifiers)
254260
return False # finish event propagation?
255261

256262
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
@@ -151,6 +151,10 @@ class FigureCanvasGTK3(Gtk.DrawingArea, FigureCanvasBase):
151151
65439 : 'dec',
152152
65421 : 'enter',
153153
}
154+
modifier_keys = [[gdk.MOD4_MASK, 'super'],
155+
[gdk.MOD1_MASK, 'alt'],
156+
[gdk.CONTROL_MASK, 'ctrl'],
157+
[gdk.SHIFT_MASK, 'shift']]
154158

155159
# Setting this as a static constant prevents
156160
# this resulting expression from leaking
@@ -212,7 +216,8 @@ def button_press_event(self, widget, event):
212216
x = event.x
213217
# flipy so y=0 is bottom of canvas
214218
y = self.get_allocation().height - event.y
215-
FigureCanvasBase.button_press_event(self, x, y, event.button, guiEvent=event)
219+
modifiers = {prefix for key_mask, prefix in modifier_keys if event.state & key_mask}
220+
FigureCanvasBase.button_press_event(self, x, y, event.button, guiEvent=event, modifiers=modifiers)
216221
return False # finish event propagation?
217222

218223
def button_release_event(self, widget, event):

lib/matplotlib/backends/backend_qt5.py

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -237,17 +237,23 @@ def mouseEventCoords(self, pos):
237237
def mousePressEvent(self, event):
238238
x, y = self.mouseEventCoords(event.pos())
239239
button = self.buttond.get(event.button())
240+
modifiers = {name for name, mod_key, qt_key in MODIFIER_KEYS
241+
if event.modifiers() & mod_key}
240242
if button is not None:
241243
FigureCanvasBase.button_press_event(self, x, y, button,
242-
guiEvent=event)
244+
guiEvent=event,
245+
modifiers=modifiers)
243246

244247
def mouseDoubleClickEvent(self, event):
245248
x, y = self.mouseEventCoords(event.pos())
246249
button = self.buttond.get(event.button())
250+
modifiers = {name for name, mod_key, qt_key in MODIFIER_KEYS
251+
if event.modifiers() & mod_key}
247252
if button is not None:
248253
FigureCanvasBase.button_press_event(self, x, y,
249254
button, dblclick=True,
250-
guiEvent=event)
255+
guiEvent=event,
256+
modifiers=modifiers)
251257

252258
def mouseMoveEvent(self, event):
253259
x, y = self.mouseEventCoords(event)

lib/matplotlib/backends/backend_tkagg.py

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

212+
# Dictionary for adding modifier keys to the key string.
213+
# Bit details originate from
214+
# http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
215+
# BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004;
216+
# BIT_LEFT_ALT = 0x008; BIT_NUMLOCK = 0x010; BIT_RIGHT_ALT = 0x080;
217+
# BIT_MB_1 = 0x100; BIT_MB_2 = 0x200; BIT_MB_3 = 0x400;
218+
# In general, the modifier key is excluded from the modifier flag,
219+
# however this is not the case on "darwin", so double check that
220+
# we aren't adding repeat modifier flags to a modifier key.
221+
if sys.platform == 'win32':
222+
self.MOD_KEYS = [(17, 'alt', 'alt'),
223+
(2, 'ctrl', 'control'),
224+
]
225+
elif sys.platform == 'darwin':
226+
self.MOD_KEYS = [(3, 'super', 'super'),
227+
(4, 'alt', 'alt'),
228+
(2, 'ctrl', 'control'),
229+
]
230+
else:
231+
self.MOD_KEYS = [(6, 'super', 'super'),
232+
(3, 'alt', 'alt'),
233+
(2, 'ctrl', 'control'),
234+
]
235+
212236
self._master = master
213237
self._tkcanvas.focus_set()
214238

@@ -344,16 +368,18 @@ def button_press_event(self, event, dblclick=False):
344368
# flipy so y=0 is bottom of canvas
345369
y = self.figure.bbox.height - event.y
346370
num = getattr(event, 'num', None)
371+
modifiers = self._get_modifiers(event)
347372

348373
if sys.platform=='darwin':
349374
# 2 and 3 were reversed on the OSX platform I
350375
# tested under tkagg
351376
if num==2: num=3
352377
elif num==3: num=2
353378

354-
FigureCanvasBase.button_press_event(self, x, y, num, dblclick=dblclick, guiEvent=event)
379+
FigureCanvasBase.button_press_event(self, x, y, num, dblclick=dblclick,
380+
guiEvent=event, modifiers=modifiers)
355381

356-
def button_dblclick_event(self,event):
382+
def button_dblclick_event(self, event):
357383
self.button_press_event(event,dblclick=True)
358384

359385
def button_release_event(self, event):
@@ -404,37 +430,19 @@ def _get_key(self, event):
404430
else:
405431
key = None
406432

407-
# add modifier keys to the key string. Bit details originate from
408-
# http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm
409-
# BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004;
410-
# BIT_LEFT_ALT = 0x008; BIT_NUMLOCK = 0x010; BIT_RIGHT_ALT = 0x080;
411-
# BIT_MB_1 = 0x100; BIT_MB_2 = 0x200; BIT_MB_3 = 0x400;
412-
# In general, the modifier key is excluded from the modifier flag,
413-
# however this is not the case on "darwin", so double check that
414-
# we aren't adding repeat modifier flags to a modifier key.
415-
if sys.platform == 'win32':
416-
modifiers = [(17, 'alt', 'alt'),
417-
(2, 'ctrl', 'control'),
418-
]
419-
elif sys.platform == 'darwin':
420-
modifiers = [(3, 'super', 'super'),
421-
(4, 'alt', 'alt'),
422-
(2, 'ctrl', 'control'),
423-
]
424-
else:
425-
modifiers = [(6, 'super', 'super'),
426-
(3, 'alt', 'alt'),
427-
(2, 'ctrl', 'control'),
428-
]
429-
430433
if key is not None:
431434
# note, shift is not added to the keys as this is already accounted for
432-
for bitmask, prefix, key_name in modifiers:
435+
for bitmask, prefix, key_name in self.MOD_KEYS:
433436
if event.state & (1 << bitmask) and key_name not in key:
434437
key = '{0}+{1}'.format(prefix, key)
435438

436439
return key
437440

441+
def _get_modifiers(self, event):
442+
modifiers = {prefix for bitmask, prefix, key_name in self.MOD_KEYS
443+
if event.state & (1 << bitmask)}
444+
return modifiers
445+
438446
def key_press(self, event):
439447
key = self._get_key(event)
440448
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
@@ -1004,6 +1004,19 @@ def _get_key(self, evt):
10041004

10051005
return key
10061006

1007+
def _get_modifiers(self, evt):
1008+
"""
1009+
Extracts modifier keys from a wx MouseEvent
1010+
"""
1011+
MOD_KEYS = {"AltDown": "alt",
1012+
"CmdDown": "ctrl",
1013+
"ControlDown": "ctrl",
1014+
"ShiftDown": "shift"
1015+
}
1016+
modifiers = {prefix for attr, prefix in MOD_KEYS
1017+
if getattr(evt, attr)()}
1018+
return modifiers
1019+
10071020
def _onKeyDown(self, evt):
10081021
"""Capture key press."""
10091022
key = self._get_key(evt)
@@ -1034,16 +1047,21 @@ def _onRightButtonDown(self, evt):
10341047
y = self.figure.bbox.height - evt.GetY()
10351048
evt.Skip()
10361049
self._set_capture(True)
1037-
FigureCanvasBase.button_press_event(self, x, y, 3, guiEvent=evt)
1050+
modifiers = self._get_modifiers(evt)
1051+
FigureCanvasBase.button_press_event(self, x, y, 3,
1052+
guiEvent=evt,
1053+
modifiers=modifiers)
10381054

10391055
def _onRightButtonDClick(self, evt):
10401056
"""Start measuring on an axis."""
10411057
x = evt.GetX()
10421058
y = self.figure.bbox.height - evt.GetY()
10431059
evt.Skip()
10441060
self._set_capture(True)
1061+
modifiers = self._get_modifiers(evt)
10451062
FigureCanvasBase.button_press_event(self, x, y, 3,
1046-
dblclick=True, guiEvent=evt)
1063+
dblclick=True, guiEvent=evt,
1064+
modifiers=modifiers)
10471065

10481066
def _onRightButtonUp(self, evt):
10491067
"""End measuring on an axis."""
@@ -1059,16 +1077,21 @@ def _onLeftButtonDown(self, evt):
10591077
y = self.figure.bbox.height - evt.GetY()
10601078
evt.Skip()
10611079
self._set_capture(True)
1062-
FigureCanvasBase.button_press_event(self, x, y, 1, guiEvent=evt)
1080+
modifiers = self._get_modifiers(evt)
1081+
FigureCanvasBase.button_press_event(self, x, y, 1,
1082+
guiEvent=evt,
1083+
modifiers=modifiers)
10631084

10641085
def _onLeftButtonDClick(self, evt):
10651086
"""Start measuring on an axis."""
10661087
x = evt.GetX()
10671088
y = self.figure.bbox.height - evt.GetY()
10681089
evt.Skip()
10691090
self._set_capture(True)
1091+
modifiers = self._get_modifiers(evt)
10701092
FigureCanvasBase.button_press_event(self, x, y, 1,
1071-
dblclick=True, guiEvent=evt)
1093+
dblclick=True, guiEvent=evt,
1094+
modifiers=modifiers)
10721095

10731096
def _onLeftButtonUp(self, evt):
10741097
"""End measuring on an axis."""
@@ -1086,16 +1109,21 @@ def _onMiddleButtonDown(self, evt):
10861109
y = self.figure.bbox.height - evt.GetY()
10871110
evt.Skip()
10881111
self._set_capture(True)
1089-
FigureCanvasBase.button_press_event(self, x, y, 2, guiEvent=evt)
1112+
modifiers = self._get_modifiers(evt)
1113+
FigureCanvasBase.button_press_event(self, x, y, 2,
1114+
guiEvent=evt,
1115+
modifiers=modifiers)
10901116

10911117
def _onMiddleButtonDClick(self, evt):
10921118
"""Start measuring on an axis."""
10931119
x = evt.GetX()
10941120
y = self.figure.bbox.height - evt.GetY()
10951121
evt.Skip()
10961122
self._set_capture(True)
1123+
modifiers = self._get_modifiers(evt)
10971124
FigureCanvasBase.button_press_event(self, x, y, 2,
1098-
dblclick=True, guiEvent=evt)
1125+
dblclick=True, guiEvent=evt,
1126+
modifiers=modifiers)
10991127

11001128
def _onMiddleButtonUp(self, evt):
11011129
"""End measuring on an axis."""

src/_macosx.m

Lines changed: 28 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2332,21 +2332,23 @@ - (void)mouseDown:(NSEvent *)event
23322332
location = [self convertPoint: location fromView: nil];
23332333
x = location.x * device_scale;
23342334
y = location.y * device_scale;
2335+
2336+
unsigned int modifier = [event modifierFlags];
2337+
PyObject* modifiers = PySet_New(NULL);
2338+
if (modifier & NSControlKeyMask)
2339+
PySet_Add(PyString_FromString("ctrl");
2340+
if (modifier & NSAlternateKeyMask)
2341+
PySet_Add(PyString_FromString("alt");
2342+
if (modifier & NSShiftKeyMask)
2343+
PySet_Add(PyString_FromString("shift");
2344+
if (modifier & NSCommandKeyMask)
2345+
PySet_Add(PyString_FromString("cmd");
2346+
23352347
switch ([event type])
23362348
{ case NSLeftMouseDown:
2337-
{ unsigned int modifier = [event modifierFlags];
2338-
if (modifier & NSControlKeyMask)
2339-
/* emulate a right-button click */
2340-
num = 3;
2341-
else if (modifier & NSAlternateKeyMask)
2342-
/* emulate a middle-button click */
2343-
num = 2;
2344-
else
2345-
{
2346-
num = 1;
2347-
if ([NSCursor currentCursor]==[NSCursor openHandCursor])
2349+
{ if ([NSCursor currentCursor]==[NSCursor openHandCursor])
23482350
[[NSCursor closedHandCursor] set];
2349-
}
2351+
num = 1;
23502352
break;
23512353
}
23522354
case NSOtherMouseDown: num = 2; break;
@@ -2357,7 +2359,8 @@ - (void)mouseDown:(NSEvent *)event
23572359
dblclick = 1;
23582360
}
23592361
gstate = PyGILState_Ensure();
2360-
result = PyObject_CallMethod(canvas, "button_press_event", "iiii", x, y, num, dblclick);
2362+
result = PyObject_CallMethod(canvas, "button_press_event", "iiii",
2363+
x, y, num, dblclick, Py_None, modifiers);
23612364
if(result)
23622365
Py_DECREF(result);
23632366
else
@@ -2445,7 +2448,18 @@ - (void)rightMouseDown:(NSEvent *)event
24452448
if ([event clickCount] == 2) {
24462449
dblclick = 1;
24472450
}
2448-
result = PyObject_CallMethod(canvas, "button_press_event", "iiii", x, y, num, dblclick);
2451+
unsigned int modifier = [event modifierFlags];
2452+
PyObject* modifiers = PySet_New(NULL);
2453+
if (modifier & NSControlKeyMask)
2454+
PySet_Add(PyString_FromString("ctrl");
2455+
if (modifier & NSAlternateKeyMask)
2456+
PySet_Add(PyString_FromString("alt");
2457+
if (modifier & NSShiftKeyMask)
2458+
PySet_Add(PyString_FromString("shift");
2459+
if (modifier & NSCommandKeyMask)
2460+
PySet_Add(PyString_FromString("cmd");
2461+
result = PyObject_CallMethod(canvas, "button_press_event", "iiii",
2462+
x, y, num, dblclick, Py_None, modifiers);
24492463
if(result)
24502464
Py_DECREF(result);
24512465
else

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