diff --git a/lib/matplotlib/backends/backend_qt.py b/lib/matplotlib/backends/backend_qt.py index d0aded5fff63..055a92376387 100644 --- a/lib/matplotlib/backends/backend_qt.py +++ b/lib/matplotlib/backends/backend_qt.py @@ -1,3 +1,4 @@ +from collections import namedtuple import functools import os import sys @@ -213,6 +214,9 @@ def _timer_stop(self): self._timer.stop() +Crosshair = namedtuple('Crosshair', 'x, y, x0, x1, y0, y1') + + class FigureCanvasQT(FigureCanvasBase, QtWidgets.QWidget): required_interactive_framework = "qt" _timer_cls = TimerQT @@ -231,6 +235,7 @@ class FigureCanvasQT(FigureCanvasBase, QtWidgets.QWidget): def __init__(self, figure=None): _create_qApp() super().__init__(figure=figure) + self._crosshair = None self._draw_pending = False self._is_drawing = False @@ -305,6 +310,21 @@ def mouseEventCoords(self, pos=None): y = self.figure.bbox.height / self.device_pixel_ratio - pos.y() return x * self.device_pixel_ratio, y * self.device_pixel_ratio + def _update_crosshair(self, x, y): + previous_crosshair = self._crosshair + ax = self.inaxes((x, y)) + if ax is None: + self._crosshair = None + else: + bbox = ax.get_position() # in figure coords + x0 = int(bbox.x0 * self.width()) + x1 = int(bbox.x1 * self.width()) + y0 = int((1 - bbox.y0) * self.height()) + y1 = int((1 - bbox.y1) * self.height()) + self._crosshair = Crosshair(x, y, x0, x1, y0, y1) + needs_repaint = previous_crosshair is not None or self._crosshair is not None + return needs_repaint + def enterEvent(self, event): # Force querying of the modifiers, as the cached modifier state can # have been invalidated while the window was out of focus. @@ -344,8 +364,12 @@ def mouseDoubleClickEvent(self, event): def mouseMoveEvent(self, event): if self.figure is None: return + mouse_xy = self.mouseEventCoords(event) + needs_repaint = self._update_crosshair(*mouse_xy) + if needs_repaint: + self.repaint() MouseEvent("motion_notify_event", self, - *self.mouseEventCoords(event), + *mouse_xy, buttons=self._mpl_buttons(event.buttons()), modifiers=self._mpl_modifiers(), guiEvent=event)._process() diff --git a/lib/matplotlib/backends/backend_qtagg.py b/lib/matplotlib/backends/backend_qtagg.py index 256e50a3d1c3..4b33e507e31d 100644 --- a/lib/matplotlib/backends/backend_qtagg.py +++ b/lib/matplotlib/backends/backend_qtagg.py @@ -68,6 +68,17 @@ def paintEvent(self, event): ctypes.c_long.from_address(id(buf)).value = 1 self._draw_rect_callback(painter) + + if self._crosshair is not None: + x = self._crosshair.x + y = rect.height() - int(self._crosshair.y) + x0 = rect.left() + self._crosshair.x0 + x1 = rect.left() + self._crosshair.x1 + y0 = rect.top() + self._crosshair.y0 + y1 = rect.top() + self._crosshair.y1 + painter.setPen(QtGui.QPen(QtCore.Qt.GlobalColor.red)) + painter.drawLine(x0, y, x1, y) + painter.drawLine(x, y0, x, y1) finally: painter.end()