From ce6da3e93fd7c817aaf27a63e6858bd4f5723696 Mon Sep 17 00:00:00 2001 From: Ogi Moore Date: Wed, 24 Jun 2020 22:20:47 -0700 Subject: [PATCH] First pass at implementing the diff from PR307 --- pyqtgraph/GraphicsScene/GraphicsScene.py | 86 ++++++++++++------- pyqtgraph/__init__.py | 1 + .../graphicsItems/tests/test_InfiniteLine.py | 13 +-- 3 files changed, 65 insertions(+), 35 deletions(-) diff --git a/pyqtgraph/GraphicsScene/GraphicsScene.py b/pyqtgraph/GraphicsScene/GraphicsScene.py index b67e44ef..6b2e0692 100644 --- a/pyqtgraph/GraphicsScene/GraphicsScene.py +++ b/pyqtgraph/GraphicsScene/GraphicsScene.py @@ -1,4 +1,5 @@ # -*- coding: utf-8 -*- +import time import weakref import warnings @@ -8,6 +9,9 @@ from .. import functions as fn from .. import ptime as ptime from .mouseEvents import * from .. import debug as debug +from .. import getConfigOption + +getMillis = lambda: int(round(time.time() * 1000)) if hasattr(QtCore, 'PYQT_VERSION'): @@ -114,6 +118,7 @@ class GraphicsScene(QtGui.QGraphicsScene): self.contextMenu[0].triggered.connect(self.showExportDialog) self.exportDialog = None + self._lastMoveEventTime = 0 def render(self, *args): self.prepareForPaint() @@ -161,39 +166,60 @@ class GraphicsScene(QtGui.QGraphicsScene): if i.isEnabled() and i.isVisible() and int(i.flags() & i.ItemIsFocusable) > 0: i.setFocus(QtCore.Qt.MouseFocusReason) break + + def _moveEventIsAllowed(self): + # For ignoring events that are too close together + + # Max number of events per second + rateLimit = getConfigOption('mouseRateLimit') + if rateLimit <= 0: + return True + + # Delay between events (in milliseconds) + delay = 1000.0 / rateLimit + if getMillis() - self._lastMoveEventTime >= delay: + return True + return False + def mouseMoveEvent(self, ev): - self.sigMouseMoved.emit(ev.scenePos()) - - ## First allow QGraphicsScene to deliver hoverEnter/Move/ExitEvents - QtGui.QGraphicsScene.mouseMoveEvent(self, ev) - - ## Next deliver our own HoverEvents - self.sendHoverEvents(ev) - - if int(ev.buttons()) != 0: ## button is pressed; send mouseMoveEvents and mouseDragEvents + # ignore high frequency events + if self._moveEventIsAllowed(): + self._lastMoveEventTime = getMillis() + self.sigMouseMoved.emit(ev.scenePos()) + + # First allow QGraphicsScene to eliver hoverEvent/Move/Exit Events QtGui.QGraphicsScene.mouseMoveEvent(self, ev) - if self.mouseGrabberItem() is None: - now = ptime.time() - init = False - ## keep track of which buttons are involved in dragging - for btn in [QtCore.Qt.LeftButton, QtCore.Qt.MidButton, QtCore.Qt.RightButton]: - if int(ev.buttons() & btn) == 0: - continue - if int(btn) not in self.dragButtons: ## see if we've dragged far enough yet - cev = [e for e in self.clickEvents if int(e.button()) == int(btn)] - if cev: - cev = cev[0] - dist = Point(ev.scenePos() - cev.scenePos()).length() - if dist == 0 or (dist < self._moveDistance and now - cev.time() < self.minDragTime): - continue - init = init or (len(self.dragButtons) == 0) ## If this is the first button to be dragged, then init=True - self.dragButtons.append(int(btn)) - - ## If we have dragged buttons, deliver a drag event - if len(self.dragButtons) > 0: - if self.sendDragEvent(ev, init=init): - ev.accept() + # Next Deliver our own Hover Events + self.sendHoverEvents(ev) + if int(ev.buttons()) != 0: + # button is pressed' send mouseMoveEvents and mouseDragEvents + QtGui.QGraphicsScene.mouseMoveEvent(self, ev) + if self.mouseGrabberItem() is None: + now = ptime.time() + init = False + ## keep track of which buttons are involved in dragging + for btn in [QtCore.Qt.LeftButton, QtCore.Qt.MidButton, QtCore.Qt.RightButton]: + if int(ev.buttons() & btn) == 0: + continue + if int(btn) not in self.dragButtons: ## see if we've dragged far enough yet + cev = [e for e in self.clickEvents if int(e.button()) == int(btn)] + if cev: + cev = cev[0] + dist = Point(ev.scenePos() - cev.scenePos()).length() + if dist == 0 or (dist < self._moveDistance and now - cev.time() < self.minDragTime): + continue + init = init or (len(self.dragButtons) == 0) ## If this is the first button to be dragged, then init=True + self.dragButtons.append(int(btn)) + ## if we have dragged buttons, deliver a drag event + if len(self.dragButtons) > 0: + if self.sendDragEvent(ev, init=init): + ev.accept() + + else: + QtGui.QGraphicsScene.mouseMoveEvent(self, ev) + # if you do not accept event (which is ignored) then cursor will disappear + ev.accept() def leaveEvent(self, ev): ## inform items that mouse is gone if len(self.dragButtons) == 0: diff --git a/pyqtgraph/__init__.py b/pyqtgraph/__init__.py index bc36e891..a07cf9ef 100644 --- a/pyqtgraph/__init__.py +++ b/pyqtgraph/__init__.py @@ -56,6 +56,7 @@ CONFIG_OPTIONS = { 'exitCleanup': True, ## Attempt to work around some exit crash bugs in PyQt and PySide 'enableExperimental': False, ## Enable experimental features (the curious can search for this key in the code) 'crashWarning': False, # If True, print warnings about situations that may result in a crash + 'mouseRateLimit': 100, # For ignoring frequent mouse events, max number of mouse move events per second, if <= 0, then it is switched off 'imageAxisOrder': 'col-major', # For 'row-major', image data is expected in the standard (row, col) order. # For 'col-major', image data is expected in reversed (col, row) order. # The default is 'col-major' for backward compatibility, but this may diff --git a/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py b/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py index 24438864..39ce0e48 100644 --- a/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py +++ b/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py @@ -5,16 +5,19 @@ pg.mkQApp() def test_InfiniteLine(): + # disable delay of mouse move events because events is called immediately in test + pg.setConfigOption('mouseRateLimit', -1) + # Test basic InfiniteLine API plt = pg.plot() plt.setXRange(-10, 10) plt.setYRange(-10, 10) plt.resize(600, 600) - + # seemingly arbitrary requirements; might need longer wait time for some platforms.. QtTest.QTest.qWaitForWindowShown(plt) QtTest.QTest.qWait(100) - + vline = plt.addLine(x=1) assert vline.angle == 90 br = vline.mapToView(QtGui.QPolygonF(vline.boundingRect())) @@ -29,14 +32,14 @@ def test_InfiniteLine(): assert vline.value() == 2 vline.setPos(pg.Point(4, -5)) assert vline.value() == 4 - + oline = pg.InfiniteLine(angle=30) plt.addItem(oline) oline.setPos(pg.Point(1, -1)) assert oline.angle == 30 assert oline.pos() == pg.Point(1, -1) assert oline.value() == [1, -1] - + # test bounding rect for oblique line br = oline.mapToScene(oline.boundingRect()) pos = oline.mapToScene(pg.Point(2, 0)) @@ -55,7 +58,7 @@ def test_mouseInteraction(): hline2 = plt.addLine(y=-1, movable=False) plt.setXRange(-10, 10) plt.setYRange(-10, 10) - + # test horizontal drag pos = plt.plotItem.vb.mapViewToScene(pg.Point(0,5)).toPoint() pos2 = pos - QtCore.QPoint(200, 200)