5

我正在尝试创建具有以下行为的 QGraphicsView:

  • 当按住 control 键并按下鼠标左键时,应将视图设置为 ScrollHandDrag 模式以允许用户平移。

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

  • 如果按住控制键,单击鼠标左键,然后释放控制键,则视图应保持在 ScrollHandDrag 模式,直到释放鼠标,或者如果当时按下控制键,则将保持此模式鼠标被释放。

对我来说,这听起来应该相当简单。我已经实现了来自链接问题的逻辑,以及一些额外的逻辑来满足我的额外要求。然而,这似乎会导致以下两个问题:

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

  • 双击视图会导致一个 mousePressEvent,然后是两个 mouseReleaseEvents!这打破了我的逻辑。

所以我想知道如何解决当 QGraphicsView 的In ScrollHandDrag 模式中的逻辑时项目冻结的问题,如何停止 QGraphicsItems 在场景中的移动?使用,以及如何处理奇怪的双击鼠标事件 - 有没有办法关闭它们?

这是我的代码(这也是我的 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()
4

1 回答 1

5

如果您在平移时不调用mouse*Events 中的基本实现,则项目选择不会成为问题。但是,这现在需要重新实现内置的平移功能。幸运的是,实现它并不难。

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

  • 按住CTRL键启用平移。按下鼠标时,释放CTRL键保持平移模式。
  • 只需按下鼠标即可激活橡胶选择
  • 所有动作都被控制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)
于 2013-02-23T17:18:01.727 回答