QGraphicsView双击事件和ScrollHandDrag模式项

时间:2021-09-15 23:01:57

I'm attempting to create a QGraphicsView with the following behaviour:

我正在尝试创建一个QGraphicsView,其行为如下:

  • When the control key is held and the left mouse is down, the view should be set to ScrollHandDrag mode to allow the user to pan around.

    当控制键被按住,鼠标左键下降时,视图应该设置为滚动拖动模式,以允许用户在周围移动。

  • When in ScrollHandDrag mode, items should not be selectable/movable, as in the question here: In ScrollHandDrag mode of QGraphicsView, How to stop movement of QGraphicsItems on scene?

    在ScrollHandDrag模式中,项目不应该是可选择的/可移动的,如在这里的问题:在QGraphicsView的ScrollHandDrag模式中,如何在场景中停止QGraphicsItems的移动?

  • If the control key was held, the left mouse was clicked, and then the control key was released, then the view should stay in ScrollHandDrag mode until the mouse is released, or will stay in this mode should the control key be down at the time the mouse is released.

    如果按住control键,点击鼠标左键,然后释放control键,那么视图应该保持ScrollHandDrag模式直到鼠标被释放,或者当鼠标被释放时控制键处于关闭状态时,视图应该保持这种模式。

To me this sounds like it should be fairly straightforward. I've implemented the logic from the linked question, and some additional logic for my extra requirements. However this seems to cause the following two showstoppers:

对我来说,这听起来应该很简单。我已经实现了来自链接问题的逻辑,以及一些针对我的额外需求的附加逻辑。然而,这似乎引起了以下两个问题:

  • In the mousePressEvent, setting the item under the mouse to not have the movable and selectable flags, calling the base class, and then re-applying the flags causes the item to become "frozen". The only way to solve this seems to be a control + click, release control + release click a few times outside of the item. Also when it gets into this state no items can be moved (although they can still be selected).

    在mousePressEvent中,将鼠标下方的项目设置为没有可移动的和可选择的标志,调用基类,然后重新应用标志将导致项目“冻结”。解决这个问题的唯一方法似乎是控件+单击、释放控件+在项目之外单击几次。当它进入这个状态时,任何项都不能被移动(尽管它们仍然可以被选择)。

  • Double clicking the view causes a mousePressEvent, which is then followed by two mouseReleaseEvents! This breaks my logic.

    双击该视图将导致一个mousePressEvent,然后是两个mousereleaseevent !这让我的逻辑。

So I would like to know how I can solve the issue of the items becoming frozen when the logic from In ScrollHandDrag mode of QGraphicsView, How to stop movement of QGraphicsItems on scene? is used, and how to deal with the strange double click mouse events - is there a way to turn them off?

所以我想知道如何解决QGraphicsView的ScrollHandDrag模式下的逻辑出现冻结的问题,如何在现场停止QGraphicsItems的运动?如何处理奇怪的双击鼠标事件-是否有办法关闭它们?

Here is my code (this is also pretty much my hello world Python, so if I've made some horrible Python mistake please let me know):

这是我的代码(这也是我的hello world Python,所以如果我犯了一些严重的Python错误,请告诉我):

#! /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()

1 个解决方案

#1


5  

If you don't call the base implementation in mouse*Events while panning, item selection won't be an issue. However, this now requires re-implementing the built-in panning function. Fortunately, it's not hard to implement it.

如果在平移时不调用鼠标*事件中的基本实现,那么项选择就不会成为问题。然而,这现在需要重新实现内置的平移函数。幸运的是,实现它并不难。

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

在IRC (#pyqt @ freenode)进行了一些迭代之后,这是最终实现:

  • Pressing and holding CTRL key enables panning. While mouse is pressed, releasing CTRL key keeps panning mode.
  • 按下并按住CTRL键就可以进行平移。当鼠标按下时,释放CTRL键保持平移模式。
  • Just mouse press activates Rubber selection
  • 只要鼠标按一下就可以启动橡胶选择
  • All actions are controlled with 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)

#1


5  

If you don't call the base implementation in mouse*Events while panning, item selection won't be an issue. However, this now requires re-implementing the built-in panning function. Fortunately, it's not hard to implement it.

如果在平移时不调用鼠标*事件中的基本实现,那么项选择就不会成为问题。然而,这现在需要重新实现内置的平移函数。幸运的是,实现它并不难。

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

在IRC (#pyqt @ freenode)进行了一些迭代之后,这是最终实现:

  • Pressing and holding CTRL key enables panning. While mouse is pressed, releasing CTRL key keeps panning mode.
  • 按下并按住CTRL键就可以进行平移。当鼠标按下时,释放CTRL键保持平移模式。
  • Just mouse press activates Rubber selection
  • 只要鼠标按一下就可以启动橡胶选择
  • All actions are controlled with 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)