From 4896de5ee49715da47f7719bc68b5da9b8360bc9 Mon Sep 17 00:00:00 2001 From: Luke Campagnola Date: Fri, 15 Aug 2014 12:45:35 -0400 Subject: [PATCH] Fixed item context menus appearing after mouse has exited the item area. This occurred because the scene does not receive mouse move events while a context menu is displayed. If the user right-clicks on a new location while the menu is open, then the click event is delieverd as if the mouse had not moved. Corrected by sending a just-in-time hover event immediately before mouse press, if the cursor has moved. --- pyqtgraph/GraphicsScene/GraphicsScene.py | 37 ++++++++++-------------- pyqtgraph/GraphicsScene/mouseEvents.py | 3 ++ 2 files changed, 18 insertions(+), 22 deletions(-) diff --git a/pyqtgraph/GraphicsScene/GraphicsScene.py b/pyqtgraph/GraphicsScene/GraphicsScene.py index a57cca34..c6afbe0f 100644 --- a/pyqtgraph/GraphicsScene/GraphicsScene.py +++ b/pyqtgraph/GraphicsScene/GraphicsScene.py @@ -135,8 +135,13 @@ class GraphicsScene(QtGui.QGraphicsScene): def mousePressEvent(self, ev): #print 'scenePress' QtGui.QGraphicsScene.mousePressEvent(self, ev) - #print "mouseGrabberItem: ", self.mouseGrabberItem() if self.mouseGrabberItem() is None: ## nobody claimed press; we are free to generate drag/click events + if self.lastHoverEvent is not None: + # If the mouse has moved since the last hover event, send a new one. + # This can happen if a context menu is open while the mouse is moving. + if ev.scenePos() != self.lastHoverEvent.scenePos(): + self.sendHoverEvents(ev) + self.clickEvents.append(MouseClickEvent(ev)) ## set focus on the topmost focusable item under this click @@ -145,10 +150,6 @@ class GraphicsScene(QtGui.QGraphicsScene): if i.isEnabled() and i.isVisible() and int(i.flags() & i.ItemIsFocusable) > 0: i.setFocus(QtCore.Qt.MouseFocusReason) break - #else: - #addr = sip.unwrapinstance(sip.cast(self.mouseGrabberItem(), QtGui.QGraphicsItem)) - #item = GraphicsScene._addressCache.get(addr, self.mouseGrabberItem()) - #print "click grabbed by:", item def mouseMoveEvent(self, ev): self.sigMouseMoved.emit(ev.scenePos()) @@ -189,7 +190,6 @@ class GraphicsScene(QtGui.QGraphicsScene): def mouseReleaseEvent(self, ev): #print 'sceneRelease' if self.mouseGrabberItem() is None: - #print "sending click/drag event" if ev.button() in self.dragButtons: if self.sendDragEvent(ev, final=True): #print "sent drag event" @@ -231,6 +231,8 @@ class GraphicsScene(QtGui.QGraphicsScene): prevItems = list(self.hoverItems.keys()) + #print "hover prev items:", prevItems + #print "hover test items:", items for item in items: if hasattr(item, 'hoverEvent'): event.currentItem = item @@ -248,6 +250,7 @@ class GraphicsScene(QtGui.QGraphicsScene): event.enter = False event.exit = True + #print "hover exit items:", prevItems for item in prevItems: event.currentItem = item try: @@ -257,9 +260,13 @@ class GraphicsScene(QtGui.QGraphicsScene): finally: del self.hoverItems[item] - if hasattr(ev, 'buttons') and int(ev.buttons()) == 0: + # Update last hover event unless: + # - mouse is dragging (move+buttons); in this case we want the dragged + # item to continue receiving events until the drag is over + # - event is not a mouse event (QEvent.Leave sometimes appears here) + if (ev.type() == ev.GraphicsSceneMousePress or + (ev.type() == ev.GraphicsSceneMouseMove and int(ev.buttons()) == 0)): self.lastHoverEvent = event ## save this so we can ask about accepted events later. - def sendDragEvent(self, ev, init=False, final=False): ## Send a MouseDragEvent to the current dragItem or to @@ -323,7 +330,6 @@ class GraphicsScene(QtGui.QGraphicsScene): acceptedItem = self.lastHoverEvent.clickItems().get(ev.button(), None) else: acceptedItem = None - if acceptedItem is not None: ev.currentItem = acceptedItem try: @@ -345,22 +351,9 @@ class GraphicsScene(QtGui.QGraphicsScene): if int(item.flags() & item.ItemIsFocusable) > 0: item.setFocus(QtCore.Qt.MouseFocusReason) break - #if not ev.isAccepted() and ev.button() is QtCore.Qt.RightButton: - #print "GraphicsScene emitting sigSceneContextMenu" - #self.sigMouseClicked.emit(ev) - #ev.accept() self.sigMouseClicked.emit(ev) return ev.isAccepted() - #def claimEvent(self, item, button, eventType): - #key = (button, eventType) - #if key in self.claimedEvents: - #return False - #self.claimedEvents[key] = item - #print "event", key, "claimed by", item - #return True - - def items(self, *args): #print 'args:', args items = QtGui.QGraphicsScene.items(self, *args) diff --git a/pyqtgraph/GraphicsScene/mouseEvents.py b/pyqtgraph/GraphicsScene/mouseEvents.py index 7809d464..2e472e04 100644 --- a/pyqtgraph/GraphicsScene/mouseEvents.py +++ b/pyqtgraph/GraphicsScene/mouseEvents.py @@ -355,6 +355,9 @@ class HoverEvent(object): return Point(self.currentItem.mapFromScene(self._lastScenePos)) def __repr__(self): + if self.exit: + return "" + if self.currentItem is None: lp = self._lastScenePos p = self._scenePos