QGraphicsView Double-Click Events and ScrollHandDrag Mode Element Issue

I am trying to create a QGraphicsView with the following behavior:

  • When the control key is held down and the left mouse is not working, the view must be set to ScrollHandDrag mode to allow the user to move.

  • In ScrollHandDrag mode, elements should not be selected / moved, as in the question here: In ScrollHandDrag QGraphicsView mode, how to stop QGraphicsItems moving on the scene?

  • If the control key was held down, the left mouse clicked, and then the control key was released, then the view should remain in ScrollHandDrag mode until the mouse is released or remains in this mode if the control key is unavailable when the mouse is released.

It seems to me that this should be pretty simple. I implemented logic from a related question and some additional logic for my additional requirements. However, this seems to trigger the following two show shows:

  • In mousePressEvent, setting the element under the mouse so that there are no movable and selectable flags, calling the base class, and then re-applying the flags, the element becomes β€œfrozen”. The only way to solve this problem is to control + click, release control + release, click several times outside the element. Also, when it falls into this state, the elements cannot move (although they can still be selected).

  • Double-clicking on the view causes mousePressEvent, followed by two mouseReleaseEvents! This breaks my logic.

So, I would like to know how I can solve the problem of freezing elements when the logic from ScrollHandDrag QGraphicsView mode, how to stop the QGraphicsItems moving on the stage? , and how to deal with strange events with a double click - is there a way to disable them?

Here is my code (this is also my Python greeting world, so if I made some terrible mistake in Python, let me know):

#! /usr/bin/env python # -*- coding: utf-8 -*- import sys from PyQt4.QtGui import * from PyQt4.QtCore import * class MyMainWindow(QMainWindow): def __init__(self): super(MyMainWindow, self).__init__() self.initUI() def initUI(self): self.setWindowTitle("Test") self.gv = MyGraphicsView() self.setCentralWidget(self.gv) self.setGeometry(170, 130, 450, 250) class MyGraphicsView(QGraphicsView): def __init__(self): super(MyGraphicsView, self).__init__() self.setup() def setup(self): self.m_MouseIsDown = False self.m_ControlKeyDown = False self.setDragMode(QGraphicsView.RubberBandDrag) def mouseMoveEvent(self, event): # print "mouseMoveEvent: " + str(event.pos().x()) + "," + str(event.pos().y()) super(MyGraphicsView, self).mouseMoveEvent(event); def mousePressEvent(self, event): print "mousePressEvent" itemUnderMouse = self.itemAt(event.pos()) if itemUnderMouse is not None: bHadMovableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsMovable bWasSelected = itemUnderMouse.isSelected() bHadSelectableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsSelectable if bHadMovableFlagSet: print "has ItemIsMovable" else: print "hasn't ItemIsMovable" if bHadSelectableFlagSet: print "has ItemIsSelectable" else: print "hasn't ItemIsSelectable" if bWasSelected: print "isSelected true" else: print "isSelected false" itemUnderMouse.setSelected(False) if event.button() == Qt.LeftButton: print "mousePressEvent: left button is now down" self.m_MouseIsDown = True if self.dragMode() == QGraphicsView.ScrollHandDrag and event.button() == Qt.LeftButton: print "mousePressEvent: left button down and ScrollHandDrag set" self.PreventItemsFromMovingOrBeingSelectedWhenPannning(event) return print "mousePressEvent: pass through" super(MyGraphicsView, self).mousePressEvent(event) def mouseReleaseEvent(self, event): print "mouseReleaseEvent" if event.button() == Qt.LeftButton: print "mouseReleaseEvent - left button is now up" self.m_MouseIsDown = False if self.dragMode() == QGraphicsView.ScrollHandDrag and self.m_ControlKeyDown == False: print "mouseReleaseEvent - left button up, in ScrollHandDrag mode and control key is not pressed, change to RubberBandDrag" self.setDragMode(QGraphicsView.RubberBandDrag) super(MyGraphicsView, self).mouseReleaseEvent(event) def keyPressEvent(self, event): if event.key() == Qt.Key_Control: print "control key down" self.m_ControlKeyDown = True # ignore if mouse already down since we don't want to suddenly change to pan mode if an item is being moved if event.key() == Qt.Key_Control and self.dragMode() != QGraphicsView.ScrollHandDrag and self.m_MouseIsDown == False: print "keyPressEvent - control key down, mouse isn't down and drag mode is not ScrollHandDrag, change to ScrollHandDrag" self.setDragMode(QGraphicsView.ScrollHandDrag) super(MyGraphicsView, self).keyPressEvent(event) def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: print "control key up" self.m_ControlKeyDown = False if event.key() == Qt.Key_Control and self.dragMode() == QGraphicsView.ScrollHandDrag and self.m_MouseIsDown == False: print "keyReleaseEvent - control key up and drag mode is ScrollHandDrag, mouse is not pressed, change to RubberBandDrag" self.setDragMode(QGraphicsView.RubberBandDrag) super(MyGraphicsView, self).keyReleaseEvent(event) def wheelEvent(self, event): factor = 1.2; if event.delta() < 0: factor = 1.0 / factor self.scale(factor, factor) def PreventItemsFromMovingOrBeingSelectedWhenPannning(self, mouseEvent): itemUnderMouse = self.itemAt(mouseEvent.pos()) if itemUnderMouse is not None: print "preventing item from moving" bHadMovableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsMovable itemUnderMouse.setFlag(QGraphicsItem.ItemIsMovable, False) bWasSelected = itemUnderMouse.isSelected() bHadSelectableFlagSet = itemUnderMouse.flags() & QGraphicsItem.ItemIsSelectable itemUnderMouse.setFlag(QGraphicsItem.ItemIsSelectable, False) super(MyGraphicsView, self).mousePressEvent(mouseEvent) if bHadMovableFlagSet: print "set ItemIsMovable" itemUnderMouse.setFlag(QGraphicsItem.ItemIsMovable, True) if bHadSelectableFlagSet: print "set ItemIsSelectable" itemUnderMouse.setFlag(QGraphicsItem.ItemIsSelectable, True) if bWasSelected: print "setSelected True" itemUnderMouse.setSelected(True) else: print "no item under mouse - pass through" super(MyGraphicsView, self).mousePressEvent(mouseEvent) class MyGraphicsScene(QGraphicsScene): def __init__(self, parent): super(MyGraphicsScene, self).__init__() def main(): a = QApplication(sys.argv) w = MyMainWindow() w.show() scene = MyGraphicsScene(w) w.gv.setScene(scene) rect = scene.addRect( 10, 10, 40, 40) rect.setFlag( QGraphicsItem.ItemIsSelectable ) rect.setFlag( QGraphicsItem.ItemIsMovable ) rect = scene.addRect( 40, 40, 40, 40) rect.setFlag( QGraphicsItem.ItemIsSelectable ) rect.setFlag( QGraphicsItem.ItemIsMovable ) sys.exit(a.exec_()) if __name__ == '__main__': main() 
+4
source share
1 answer

Unless you invoke the base implementation in mouse*Event while panning, selecting an element will not be a problem. However, now this requires re-implementing the built-in pan function. Fortunately, this is not difficult to implement.

After some iteration in IRC (#pyqt @freenode) this is the final implementation:

  • Pressing and holding the CTRL key allows panning. When the mouse is pressed, the CTRL key saves the pan mode.
  • A simple mouse click activates the rubber selection
  • All actions are controlled using the Left-Button.
 class MyGraphicsView(QGraphicsView): def __init__(self): super(MyGraphicsView, self).__init__() self.setDragMode(QGraphicsView.RubberBandDrag) self._isPanning = False self._mousePressed = False def mousePressEvent(self, event): if event.button() == Qt.LeftButton: self._mousePressed = True if self._isPanning: self.setCursor(Qt.ClosedHandCursor) self._dragPos = event.pos() event.accept() else: super(MyGraphicsView, self).mousePressEvent(event) def mouseMoveEvent(self, event): if self._mousePressed and self._isPanning: newPos = event.pos() diff = newPos - self._dragPos self._dragPos = newPos self.horizontalScrollBar().setValue(self.horizontalScrollBar().value() - diff.x()) self.verticalScrollBar().setValue(self.verticalScrollBar().value() - diff.y()) event.accept() else: super(MyGraphicsView, self).mouseMoveEvent(event) def mouseReleaseEvent(self, event): if event.button() == Qt.LeftButton: if event.modifiers() & Qt.ControlModifier: self.setCursor(Qt.OpenHandCursor) else: self._isPanning = False self.setCursor(Qt.ArrowCursor) self._mousePressed = False super(MyGraphicsView, self).mouseReleaseEvent(event) def mouseDoubleClickEvent(self, event): pass def keyPressEvent(self, event): if event.key() == Qt.Key_Control and not self._mousePressed: self._isPanning = True self.setCursor(Qt.OpenHandCursor) else: super(MyGraphicsView, self).keyPressEvent(event) def keyReleaseEvent(self, event): if event.key() == Qt.Key_Control: if not self._mousePressed: self._isPanning = False self.setCursor(Qt.ArrowCursor) else: super(MyGraphicsView, self).keyPressEvent(event) def wheelEvent(self, event): factor = 1.2; if event.delta() < 0: factor = 1.0 / factor self.scale(factor, factor) 
+5
source

All Articles