First pass at implementing the diff from PR307

This commit is contained in:
Ogi Moore 2020-06-24 22:20:47 -07:00
parent b41c4a71e5
commit ce6da3e93f
3 changed files with 65 additions and 35 deletions

View File

@ -1,4 +1,5 @@
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
import time
import weakref import weakref
import warnings import warnings
@ -8,6 +9,9 @@ from .. import functions as fn
from .. import ptime as ptime from .. import ptime as ptime
from .mouseEvents import * from .mouseEvents import *
from .. import debug as debug from .. import debug as debug
from .. import getConfigOption
getMillis = lambda: int(round(time.time() * 1000))
if hasattr(QtCore, 'PYQT_VERSION'): if hasattr(QtCore, 'PYQT_VERSION'):
@ -114,6 +118,7 @@ class GraphicsScene(QtGui.QGraphicsScene):
self.contextMenu[0].triggered.connect(self.showExportDialog) self.contextMenu[0].triggered.connect(self.showExportDialog)
self.exportDialog = None self.exportDialog = None
self._lastMoveEventTime = 0
def render(self, *args): def render(self, *args):
self.prepareForPaint() self.prepareForPaint()
@ -161,39 +166,60 @@ class GraphicsScene(QtGui.QGraphicsScene):
if i.isEnabled() and i.isVisible() and int(i.flags() & i.ItemIsFocusable) > 0: if i.isEnabled() and i.isVisible() and int(i.flags() & i.ItemIsFocusable) > 0:
i.setFocus(QtCore.Qt.MouseFocusReason) i.setFocus(QtCore.Qt.MouseFocusReason)
break 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): def mouseMoveEvent(self, ev):
self.sigMouseMoved.emit(ev.scenePos()) # ignore high frequency events
if self._moveEventIsAllowed():
## First allow QGraphicsScene to deliver hoverEnter/Move/ExitEvents self._lastMoveEventTime = getMillis()
QtGui.QGraphicsScene.mouseMoveEvent(self, ev) self.sigMouseMoved.emit(ev.scenePos())
## Next deliver our own HoverEvents # First allow QGraphicsScene to eliver hoverEvent/Move/Exit Events
self.sendHoverEvents(ev)
if int(ev.buttons()) != 0: ## button is pressed; send mouseMoveEvents and mouseDragEvents
QtGui.QGraphicsScene.mouseMoveEvent(self, ev) QtGui.QGraphicsScene.mouseMoveEvent(self, ev)
if self.mouseGrabberItem() is None: # Next Deliver our own Hover Events
now = ptime.time() self.sendHoverEvents(ev)
init = False if int(ev.buttons()) != 0:
## keep track of which buttons are involved in dragging # button is pressed' send mouseMoveEvents and mouseDragEvents
for btn in [QtCore.Qt.LeftButton, QtCore.Qt.MidButton, QtCore.Qt.RightButton]: QtGui.QGraphicsScene.mouseMoveEvent(self, ev)
if int(ev.buttons() & btn) == 0: if self.mouseGrabberItem() is None:
continue now = ptime.time()
if int(btn) not in self.dragButtons: ## see if we've dragged far enough yet init = False
cev = [e for e in self.clickEvents if int(e.button()) == int(btn)] ## keep track of which buttons are involved in dragging
if cev: for btn in [QtCore.Qt.LeftButton, QtCore.Qt.MidButton, QtCore.Qt.RightButton]:
cev = cev[0] if int(ev.buttons() & btn) == 0:
dist = Point(ev.scenePos() - cev.scenePos()).length() continue
if dist == 0 or (dist < self._moveDistance and now - cev.time() < self.minDragTime): if int(btn) not in self.dragButtons: ## see if we've dragged far enough yet
continue cev = [e for e in self.clickEvents if int(e.button()) == int(btn)]
init = init or (len(self.dragButtons) == 0) ## If this is the first button to be dragged, then init=True if cev:
self.dragButtons.append(int(btn)) cev = cev[0]
dist = Point(ev.scenePos() - cev.scenePos()).length()
## If we have dragged buttons, deliver a drag event if dist == 0 or (dist < self._moveDistance and now - cev.time() < self.minDragTime):
if len(self.dragButtons) > 0: continue
if self.sendDragEvent(ev, init=init): init = init or (len(self.dragButtons) == 0) ## If this is the first button to be dragged, then init=True
ev.accept() 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 def leaveEvent(self, ev): ## inform items that mouse is gone
if len(self.dragButtons) == 0: if len(self.dragButtons) == 0:

View File

@ -56,6 +56,7 @@ CONFIG_OPTIONS = {
'exitCleanup': True, ## Attempt to work around some exit crash bugs in PyQt and PySide '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) '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 '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. '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. # For 'col-major', image data is expected in reversed (col, row) order.
# The default is 'col-major' for backward compatibility, but this may # The default is 'col-major' for backward compatibility, but this may

View File

@ -5,16 +5,19 @@ pg.mkQApp()
def test_InfiniteLine(): def test_InfiniteLine():
# disable delay of mouse move events because events is called immediately in test
pg.setConfigOption('mouseRateLimit', -1)
# Test basic InfiniteLine API # Test basic InfiniteLine API
plt = pg.plot() plt = pg.plot()
plt.setXRange(-10, 10) plt.setXRange(-10, 10)
plt.setYRange(-10, 10) plt.setYRange(-10, 10)
plt.resize(600, 600) plt.resize(600, 600)
# seemingly arbitrary requirements; might need longer wait time for some platforms.. # seemingly arbitrary requirements; might need longer wait time for some platforms..
QtTest.QTest.qWaitForWindowShown(plt) QtTest.QTest.qWaitForWindowShown(plt)
QtTest.QTest.qWait(100) QtTest.QTest.qWait(100)
vline = plt.addLine(x=1) vline = plt.addLine(x=1)
assert vline.angle == 90 assert vline.angle == 90
br = vline.mapToView(QtGui.QPolygonF(vline.boundingRect())) br = vline.mapToView(QtGui.QPolygonF(vline.boundingRect()))
@ -29,14 +32,14 @@ def test_InfiniteLine():
assert vline.value() == 2 assert vline.value() == 2
vline.setPos(pg.Point(4, -5)) vline.setPos(pg.Point(4, -5))
assert vline.value() == 4 assert vline.value() == 4
oline = pg.InfiniteLine(angle=30) oline = pg.InfiniteLine(angle=30)
plt.addItem(oline) plt.addItem(oline)
oline.setPos(pg.Point(1, -1)) oline.setPos(pg.Point(1, -1))
assert oline.angle == 30 assert oline.angle == 30
assert oline.pos() == pg.Point(1, -1) assert oline.pos() == pg.Point(1, -1)
assert oline.value() == [1, -1] assert oline.value() == [1, -1]
# test bounding rect for oblique line # test bounding rect for oblique line
br = oline.mapToScene(oline.boundingRect()) br = oline.mapToScene(oline.boundingRect())
pos = oline.mapToScene(pg.Point(2, 0)) pos = oline.mapToScene(pg.Point(2, 0))
@ -55,7 +58,7 @@ def test_mouseInteraction():
hline2 = plt.addLine(y=-1, movable=False) hline2 = plt.addLine(y=-1, movable=False)
plt.setXRange(-10, 10) plt.setXRange(-10, 10)
plt.setYRange(-10, 10) plt.setYRange(-10, 10)
# test horizontal drag # test horizontal drag
pos = plt.plotItem.vb.mapViewToScene(pg.Point(0,5)).toPoint() pos = plt.plotItem.vb.mapViewToScene(pg.Point(0,5)).toPoint()
pos2 = pos - QtCore.QPoint(200, 200) pos2 = pos - QtCore.QPoint(200, 200)