Repository: pyqtgraph/pyqtgraph

Issue:
fix problems with high freequency game mouse
The number of mouseMoveEvents was limited to ~60 per second.

Issue is analog of https://github.com/vispy/vispy/issues/1146
Pull request is analog of https://github.com/vispy/vispy/pull/1153



Discussion:
I think you need to update the test_mouseInteraction test to either:
1. set the min time between updates to 0 in the GraphicsScene object (this is the `_milliseconds_between_updates` property that I suggested in-line) before testing the mouse move events 
2. import time and time.sleep(0.015) between calls to move.

I am not sure if this would fix it, but give it a try?

This is the test at `pyqtgraph/graphicsItems/tests/test_InfiniteLine.py::test_mouseInteraction`

I update "test_mouseInteraction" test, move event delay move to pyqtgraph options.

These PR lead to damage to the drag of legendItem.

Fine, this bug with the legend was not introduced by this pull request. It also happens in master branch as well.
![legend_bug](https://cloud.githubusercontent.com/assets/3264643/15014493/d02a32aa-121f-11e6-9269-e109ab86b49f.gif)
So, i think PR is ready to merge


Resolving patch:
diff --git a/pyqtgraph/GraphicsScene/GraphicsScene.py b/pyqtgraph/GraphicsScene/GraphicsScene.py
--- 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 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 @@ def __init__(self, clickRadius=2, moveDistance=5, parent=None):
         self.contextMenu[0].triggered.connect(self.showExportDialog)
         
         self.exportDialog = None
+        self._lastMoveEventTime = 0
         
     def render(self, *args):
         self.prepareForPaint()
@@ -161,39 +166,60 @@ def mousePressEvent(self, ev):
                 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
         
-    def mouseMoveEvent(self, ev):
-        self.sigMouseMoved.emit(ev.scenePos())
-        
-        ## First allow QGraphicsScene to deliver hoverEnter/Move/ExitEvents
-        QtGui.QGraphicsScene.mouseMoveEvent(self, ev)
+        # Max number of events per second
+        rateLimit = getConfigOption('mouseRateLimit')
+        if rateLimit <= 0:
+            return True
         
-        ## Next deliver our own HoverEvents
-        self.sendHoverEvents(ev)
+        # Delay between events (in milliseconds)
+        delay = 1000.0 / rateLimit
+        if getMillis() - self._lastMoveEventTime >= delay:
+            return True
+        return False
+
         
-        if int(ev.buttons()) != 0:  ## button is pressed; send mouseMoveEvents and mouseDragEvents
+    def mouseMoveEvent(self, ev):
+        # 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
--- a/pyqtgraph/__init__.py
+++ b/pyqtgraph/__init__.py
@@ -56,6 +56,7 @@
     '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


Test patch:
diff --git a/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py b/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py
--- a/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py
+++ b/pyqtgraph/graphicsItems/tests/test_InfiniteLine.py
@@ -5,16 +5,19 @@
 
 
 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)