| FazBrowse GitHub Viewer | Trending | | Home |
| Tools: [Download Repo ZIP] [Original HTTPS Page] |
1 parent c010a36 commit 03589b1
9 files changed
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -1342,6 +1342,28 @@ class MouseEvent(LocationEvent): | |||
| 1342 | 1342 | If this is unset, *name* is "scroll_event", and *step* is nonzero, then | |
| 1343 | 1343 | this will be set to "up" or "down" depending on the sign of *step*. | |
| 1344 | 1344 | ||
| 1345 | + buttons : None or frozenset | ||
| 1346 | + For 'motion_notify_event', the mouse buttons currently being pressed | ||
| 1347 | + (a set of zero or more MouseButtons); | ||
| 1348 | + for other events, None. | ||
| 1349 | + | ||
| 1350 | + .. note:: | ||
| 1351 | + For 'motion_notify_event', this attribute is more accurate than | ||
| 1352 | + the ``button`` (singular) attribute, which is obtained from the last | ||
| 1353 | + 'button_press_event' or 'button_release_event' that occurred within | ||
| 1354 | + the canvas (and thus 1. be wrong if the last change in mouse state | ||
| 1355 | + occurred when the canvas did not have focus, and 2. cannot report | ||
| 1356 | + when multiple buttons are pressed). | ||
| 1357 | + | ||
| 1358 | + This attribute is not set for 'button_press_event' and | ||
| 1359 | + 'button_release_event' because GUI toolkits are inconsistent as to | ||
| 1360 | + whether they report the button state *before* or *after* the | ||
| 1361 | + press/release occurred. | ||
| 1362 | + | ||
| 1363 | + .. warning:: | ||
| 1364 | + On macOS, the Tk backends only report a single button even if | ||
| 1365 | + multiple buttons are pressed. | ||
| 1366 | + | ||
| 1345 | 1367 | key : None or str | |
| 1346 | 1368 | The key pressed when the mouse event triggered, e.g. 'shift'. | |
| 1347 | 1369 | See `KeyEvent`. | |
@@ -1374,7 +1396,8 @@ def on_press(event): | |||
| 1374 | 1396 | """ | |
| 1375 | 1397 | ||
| 1376 | 1398 | def __init__(self, name, canvas, x, y, button=None, key=None, | |
| 1377 | - step=0, dblclick=False, guiEvent=None, *, modifiers=None): | ||
| 1399 | + step=0, dblclick=False, guiEvent=None, *, | ||
| 1400 | + buttons=None, modifiers=None): | ||
| 1378 | 1401 | super().__init__( | |
| 1379 | 1402 | name, canvas, x, y, guiEvent=guiEvent, modifiers=modifiers) | |
| 1380 | 1403 | if button in MouseButton.__members__.values(): | |
@@ -1385,6 +1408,16 @@ def __init__(self, name, canvas, x, y, button=None, key=None, | |||
| 1385 | 1408 | elif step < 0: | |
| 1386 | 1409 | button = "down" | |
| 1387 | 1410 | self.button = button | |
| 1411 | + if name == "motion_notify_event": | ||
| 1412 | + self.buttons = frozenset(buttons if buttons is not None else []) | ||
| 1413 | + else: | ||
| 1414 | + # We don't support 'buttons' for button_press/release_event because | ||
| 1415 | + # toolkits are inconsistent as to whether they report the state | ||
| 1416 | + # before or after the event. | ||
| 1417 | + if buttons: | ||
| 1418 | + raise ValueError( | ||
| 1419 | + "'buttons' is only supported for 'motion_notify_event'") | ||
| 1420 | + self.buttons = None | ||
| 1388 | 1421 | self.key = key | |
| 1389 | 1422 | self.step = step | |
| 1390 | 1423 | self.dblclick = dblclick | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -19,7 +19,7 @@ | |||
| 19 | 19 | from matplotlib import _api, backend_tools, cbook, _c_internal_utils | |
| 20 | 20 | from matplotlib.backend_bases import ( | |
| 21 | 21 | _Backend, FigureCanvasBase, FigureManagerBase, NavigationToolbar2, | |
| 22 | - TimerBase, ToolContainerBase, cursors, _Mode, | ||
| 22 | + TimerBase, ToolContainerBase, cursors, _Mode, MouseButton, | ||
| 23 | 23 | CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) | |
| 24 | 24 | from matplotlib._pylab_helpers import Gcf | |
| 25 | 25 | from . import _tkagg | |
@@ -293,6 +293,7 @@ def _event_mpl_coords(self, event): | |||
| 293 | 293 | def motion_notify_event(self, event): | |
| 294 | 294 | MouseEvent("motion_notify_event", self, | |
| 295 | 295 | *self._event_mpl_coords(event), | |
| 296 | + buttons=self._mpl_buttons(event), | ||
| 296 | 297 | modifiers=self._mpl_modifiers(event), | |
| 297 | 298 | guiEvent=event)._process() | |
| 298 | 299 | ||
@@ -354,13 +355,25 @@ def scroll_event_windows(self, event): | |||
| 354 | 355 | x, y, step=step, modifiers=self._mpl_modifiers(event), | |
| 355 | 356 | guiEvent=event)._process() | |
| 356 | 357 | ||
| 358 | + @staticmethod | ||
| 359 | + def _mpl_buttons(event): # See _mpl_modifiers. | ||
| 360 | + # NOTE: This fails to report multiclicks on macOS; only one button is | ||
| 361 | + # reported (multiclicks work correctly on Linux & Windows). | ||
| 362 | + modifiers = [ | ||
| 363 | + (MouseButton.LEFT, 1 << 8), | ||
| 364 | + (MouseButton.RIGHT, 1 << 9), | ||
| 365 | + (MouseButton.MIDDLE, 1 << 10), | ||
| 366 | + (MouseButton.BACK, 1 << 11), | ||
| 367 | + (MouseButton.FORWARD, 1 << 12), | ||
| 368 | + ] | ||
| 369 | + # State *before* press/release. | ||
| 370 | + return [name for name, mask in modifiers if event.state & mask] | ||
| 371 | + | ||
| 357 | 372 | @staticmethod | |
| 358 | 373 | def _mpl_modifiers(event, *, exclude=None): | |
| 359 | - # add modifier keys to the key string. Bit details originate from | ||
| 360 | - # http://effbot.org/tkinterbook/tkinter-events-and-bindings.htm | ||
| 361 | - # BIT_SHIFT = 0x001; BIT_CAPSLOCK = 0x002; BIT_CONTROL = 0x004; | ||
| 362 | - # BIT_LEFT_ALT = 0x008; BIT_NUMLOCK = 0x010; BIT_RIGHT_ALT = 0x080; | ||
| 363 | - # BIT_MB_1 = 0x100; BIT_MB_2 = 0x200; BIT_MB_3 = 0x400; | ||
| 374 | + # Add modifier keys to the key string. Bit values are inferred from | ||
| 375 | + # the implementation of tkinter.Event.__repr__ (1, 2, 4, 8, ... = | ||
| 376 | + # Shift, Lock, Control, Mod1, ..., Mod5, Button1, ..., Button5) | ||
| 364 | 377 | # In general, the modifier key is excluded from the modifier flag, | |
| 365 | 378 | # however this is not the case on "darwin", so double check that | |
| 366 | 379 | # we aren't adding repeat modifier flags to a modifier key. | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -6,8 +6,8 @@ | |||
| 6 | 6 | import matplotlib as mpl | |
| 7 | 7 | from matplotlib import _api, backend_tools, cbook | |
| 8 | 8 | from matplotlib.backend_bases import ( | |
| 9 | - ToolContainerBase, CloseEvent, KeyEvent, LocationEvent, MouseEvent, | ||
| 10 | - ResizeEvent) | ||
| 9 | + ToolContainerBase, MouseButton, | ||
| 10 | + CloseEvent, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) | ||
| 11 | 11 | ||
| 12 | 12 | try: | |
| 13 | 13 | import gi | |
@@ -156,6 +156,7 @@ def key_release_event(self, widget, event): | |||
| 156 | 156 | ||
| 157 | 157 | def motion_notify_event(self, widget, event): | |
| 158 | 158 | MouseEvent("motion_notify_event", self, *self._mpl_coords(event), | |
| 159 | + buttons=self._mpl_buttons(event.state), | ||
| 159 | 160 | modifiers=self._mpl_modifiers(event.state), | |
| 160 | 161 | guiEvent=event)._process() | |
| 161 | 162 | return False # finish event propagation? | |
@@ -182,6 +183,18 @@ def size_allocate(self, widget, allocation): | |||
| 182 | 183 | ResizeEvent("resize_event", self)._process() | |
| 183 | 184 | self.draw_idle() | |
| 184 | 185 | ||
| 186 | + @staticmethod | ||
| 187 | + def _mpl_buttons(event_state): | ||
| 188 | + modifiers = [ | ||
| 189 | + (MouseButton.LEFT, Gdk.ModifierType.BUTTON1_MASK), | ||
| 190 | + (MouseButton.MIDDLE, Gdk.ModifierType.BUTTON2_MASK), | ||
| 191 | + (MouseButton.RIGHT, Gdk.ModifierType.BUTTON3_MASK), | ||
| 192 | + (MouseButton.BACK, Gdk.ModifierType.BUTTON4_MASK), | ||
| 193 | + (MouseButton.FORWARD, Gdk.ModifierType.BUTTON5_MASK), | ||
| 194 | + ] | ||
| 195 | + # State *before* press/release. | ||
| 196 | + return [name for name, mask in modifiers if event_state & mask] | ||
| 197 | + | ||
| 185 | 198 | @staticmethod | |
| 186 | 199 | def _mpl_modifiers(event_state, *, exclude=None): | |
| 187 | 200 | modifiers = [ | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -5,8 +5,8 @@ | |||
| 5 | 5 | import matplotlib as mpl | |
| 6 | 6 | from matplotlib import _api, backend_tools, cbook | |
| 7 | 7 | from matplotlib.backend_bases import ( | |
| 8 | - ToolContainerBase, KeyEvent, LocationEvent, MouseEvent, ResizeEvent, | ||
| 9 | - CloseEvent) | ||
| 8 | + ToolContainerBase, MouseButton, | ||
| 9 | + KeyEvent, LocationEvent, MouseEvent, ResizeEvent, CloseEvent) | ||
| 10 | 10 | ||
| 11 | 11 | try: | |
| 12 | 12 | import gi | |
@@ -151,6 +151,7 @@ def key_release_event(self, controller, keyval, keycode, state): | |||
| 151 | 151 | def motion_notify_event(self, controller, x, y): | |
| 152 | 152 | MouseEvent( | |
| 153 | 153 | "motion_notify_event", self, *self._mpl_coords((x, y)), | |
| 154 | + buttons=self._mpl_buttons(controller), | ||
| 154 | 155 | modifiers=self._mpl_modifiers(controller), | |
| 155 | 156 | )._process() | |
| 156 | 157 | ||
@@ -175,6 +176,26 @@ def resize_event(self, area, width, height): | |||
| 175 | 176 | ResizeEvent("resize_event", self)._process() | |
| 176 | 177 | self.draw_idle() | |
| 177 | 178 | ||
| 179 | + def _mpl_buttons(self, controller): | ||
| 180 | + # NOTE: This spews "Broken accounting of active state" warnings on | ||
| 181 | + # right click on macOS. | ||
| 182 | + surface = self.get_native().get_surface() | ||
| 183 | + is_over, x, y, event_state = surface.get_device_position( | ||
| 184 | + self.get_display().get_default_seat().get_pointer()) | ||
| 185 | + # NOTE: alternatively we could use | ||
| 186 | + # event_state = controller.get_current_event_state() | ||
| 187 | + # but for button_press/button_release this would report the state | ||
| 188 | + # *prior* to the event rather than after it; the above reports the | ||
| 189 | + # state *after* it. | ||
| 190 | + mod_table = [ | ||
| 191 | + (MouseButton.LEFT, Gdk.ModifierType.BUTTON1_MASK), | ||
| 192 | + (MouseButton.MIDDLE, Gdk.ModifierType.BUTTON2_MASK), | ||
| 193 | + (MouseButton.RIGHT, Gdk.ModifierType.BUTTON3_MASK), | ||
| 194 | + (MouseButton.BACK, Gdk.ModifierType.BUTTON4_MASK), | ||
| 195 | + (MouseButton.FORWARD, Gdk.ModifierType.BUTTON5_MASK), | ||
| 196 | + ] | ||
| 197 | + return {name for name, mask in mod_table if event_state & mask} | ||
| 198 | + | ||
| 178 | 199 | def _mpl_modifiers(self, controller=None): | |
| 179 | 200 | if controller is None: | |
| 180 | 201 | surface = self.get_native().get_surface() | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -329,6 +329,7 @@ def mouseMoveEvent(self, event): | |||
| 329 | 329 | return | |
| 330 | 330 | MouseEvent("motion_notify_event", self, | |
| 331 | 331 | *self.mouseEventCoords(event), | |
| 332 | + buttons=self._mpl_buttons(event.buttons()), | ||
| 332 | 333 | modifiers=self._mpl_modifiers(), | |
| 333 | 334 | guiEvent=event)._process() | |
| 334 | 335 | ||
@@ -396,6 +397,13 @@ def sizeHint(self): | |||
| 396 | 397 | def minumumSizeHint(self): | |
| 397 | 398 | return QtCore.QSize(10, 10) | |
| 398 | 399 | ||
| 400 | + @staticmethod | ||
| 401 | + def _mpl_buttons(buttons): | ||
| 402 | + buttons = _to_int(buttons) | ||
| 403 | + # State *after* press/release. | ||
| 404 | + return {button for mask, button in FigureCanvasQT.buttond.items() | ||
| 405 | + if _to_int(mask) & buttons} | ||
| 406 | + | ||
| 399 | 407 | @staticmethod | |
| 400 | 408 | def _mpl_modifiers(modifiers=None, *, exclude=None): | |
| 401 | 409 | if modifiers is None: | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -22,7 +22,7 @@ | |||
| 22 | 22 | from matplotlib import _api, backend_bases, backend_tools | |
| 23 | 23 | from matplotlib.backends import backend_agg | |
| 24 | 24 | from matplotlib.backend_bases import ( | |
| 25 | - _Backend, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) | ||
| 25 | + _Backend, MouseButton, KeyEvent, LocationEvent, MouseEvent, ResizeEvent) | ||
| 26 | 26 | ||
| 27 | 27 | _log = logging.getLogger(__name__) | |
| 28 | 28 | ||
@@ -283,10 +283,17 @@ def _handle_mouse(self, event): | |||
| 283 | 283 | y = event['y'] | |
| 284 | 284 | y = self.get_renderer().height - y | |
| 285 | 285 | self._last_mouse_xy = x, y | |
| 286 | - # JavaScript button numbers and Matplotlib button numbers are off by 1. | ||
| 287 | - button = event['button'] + 1 | ||
| 288 | - | ||
| 289 | 286 | e_type = event['type'] | |
| 287 | + button = event['button'] + 1 # JS numbers off by 1 compared to mpl. | ||
| 288 | + buttons = { # JS ordering different compared to mpl. | ||
| 289 | + button for button, mask in [ | ||
| 290 | + (MouseButton.LEFT, 1), | ||
| 291 | + (MouseButton.RIGHT, 2), | ||
| 292 | + (MouseButton.MIDDLE, 4), | ||
| 293 | + (MouseButton.BACK, 8), | ||
| 294 | + (MouseButton.FORWARD, 16), | ||
| 295 | + ] if event['buttons'] & mask # State *after* press/release. | ||
| 296 | + } | ||
| 290 | 297 | modifiers = event['modifiers'] | |
| 291 | 298 | guiEvent = event.get('guiEvent') | |
| 292 | 299 | if e_type in ['button_press', 'button_release']: | |
@@ -300,10 +307,12 @@ def _handle_mouse(self, event): | |||
| 300 | 307 | modifiers=modifiers, guiEvent=guiEvent)._process() | |
| 301 | 308 | elif e_type == 'motion_notify': | |
| 302 | 309 | MouseEvent(e_type + '_event', self, x, y, | |
| 303 | - modifiers=modifiers, guiEvent=guiEvent)._process() | ||
| 310 | + buttons=buttons, modifiers=modifiers, guiEvent=guiEvent, | ||
| 311 | + )._process() | ||
| 304 | 312 | elif e_type in ['figure_enter', 'figure_leave']: | |
| 305 | 313 | LocationEvent(e_type + '_event', self, x, y, | |
| 306 | 314 | modifiers=modifiers, guiEvent=guiEvent)._process() | |
| 315 | + | ||
| 307 | 316 | handle_button_press = handle_button_release = handle_dblclick = \ | |
| 308 | 317 | handle_figure_enter = handle_figure_leave = handle_motion_notify = \ | |
| 309 | 318 | handle_scroll = _handle_mouse | |
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -685,6 +685,22 @@ def _on_size(self, event): | |||
| 685 | 685 | ResizeEvent("resize_event", self)._process() | |
| 686 | 686 | self.draw_idle() | |
| 687 | 687 | ||
| 688 | + @staticmethod | ||
| 689 | + def _mpl_buttons(): | ||
| 690 | + state = wx.GetMouseState() | ||
| 691 | + # NOTE: Alternatively, we could use event.LeftIsDown() / etc. but this | ||
| 692 | + # fails to report multiclick drags on macOS (other OSes have not been | ||
| 693 | + # verified). | ||
| 694 | + mod_table = [ | ||
| 695 | + (MouseButton.LEFT, state.LeftIsDown()), | ||
| 696 | + (MouseButton.RIGHT, state.RightIsDown()), | ||
| 697 | + (MouseButton.MIDDLE, state.MiddleIsDown()), | ||
| 698 | + (MouseButton.BACK, state.Aux1IsDown()), | ||
| 699 | + (MouseButton.FORWARD, state.Aux2IsDown()), | ||
| 700 | + ] | ||
| 701 | + # State *after* press/release. | ||
| 702 | + return {button for button, flag in mod_table if flag} | ||
| 703 | + | ||
| 688 | 704 | @staticmethod | |
| 689 | 705 | def _mpl_modifiers(event=None, *, exclude=None): | |
| 690 | 706 | mod_table = [ | |
@@ -794,9 +810,8 @@ def _on_mouse_button(self, event): | |||
| 794 | 810 | MouseEvent("button_press_event", self, x, y, button, | |
| 795 | 811 | modifiers=modifiers, guiEvent=event)._process() | |
| 796 | 812 | elif event.ButtonDClick(): | |
| 797 | - MouseEvent("button_press_event", self, x, y, button, | ||
| 798 | - dblclick=True, modifiers=modifiers, | ||
| 799 | - guiEvent=event)._process() | ||
| 813 | + MouseEvent("button_press_event", self, x, y, button, dblclick=True, | ||
| 814 | + modifiers=modifiers, guiEvent=event)._process() | ||
| 800 | 815 | elif event.ButtonUp(): | |
| 801 | 816 | MouseEvent("button_release_event", self, x, y, button, | |
| 802 | 817 | modifiers=modifiers, guiEvent=event)._process() | |
@@ -826,6 +841,7 @@ def _on_motion(self, event): | |||
| 826 | 841 | event.Skip() | |
| 827 | 842 | MouseEvent("motion_notify_event", self, | |
| 828 | 843 | *self._mpl_coords(event), | |
| 844 | + buttons=self._mpl_buttons(), | ||
| 829 | 845 | modifiers=self._mpl_modifiers(event), | |
| 830 | 846 | guiEvent=event)._process() | |
| 831 | 847 | ||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
@@ -644,6 +644,7 @@ mpl.figure.prototype.mouse_event = function (event, name) { | |||
| 644 | 644 | y: y, | |
| 645 | 645 | button: event.button, | |
| 646 | 646 | step: event.step, | |
| 647 | + buttons: event.buttons, | ||
| 647 | 648 | modifiers: getModifiers(event), | |
| 648 | 649 | guiEvent: simpleKeys(event), | |
| 649 | 650 | }); | |
| Back | FazBrowse Home | New Git URL |
0 commit comments