pyqt5:qgraphicsscene:单击和掉落的鼠标项目(无需按住)

发布于 2025-02-05 21:33:49 字数 952 浏览 0 评论 0原文

qgraphicsscene中的默认mouseevent函数允许通过按下,按下,移动和发布来移动项目。我正在尝试覆盖qgraphicsscene mouseevent()函数,以使用按下启动键(选择项目),移动(不举行按下),按下,按下,按下 - 释放扭曲(Drop item)。尽管我将很简单:

self.moving = False
def mousePressEvent(self, event):
    if event.button() == Qt.LeftButton:
        if self.moving == False: # first click, pick up, start moving
            super().mousePressEvent(event)
            self.moving = True
        else:                    # second click, drop, end moving item
            super().mouseReleaseEvent(event)
            self.moving = False    

def mouseMoveEvent(self, event):      
    if self.moving == True:
        super().mousePressEvent(event)
        super().mouseMoveEvent(event)

def mouseReleaseEvent(self, event):      
    pass

到目前为止,我无法捡起并移动该物品,但有人发现任何错误吗?另外,我在哪里可以找到qgraphicsscene Mouseevent函数的原始实现

谢谢你!

The default mouseEvent function in QGraphicsScene allows to move the Item by press-and-hold, move, and release. I am trying to overwrite the QGraphicsScene mouseEvent() function to accomplish the same movement with press-and-release-once (pick item), move (without holding press), press-and-release-twice (drop item). I though it would be as simple as:

self.moving = False
def mousePressEvent(self, event):
    if event.button() == Qt.LeftButton:
        if self.moving == False: # first click, pick up, start moving
            super().mousePressEvent(event)
            self.moving = True
        else:                    # second click, drop, end moving item
            super().mouseReleaseEvent(event)
            self.moving = False    

def mouseMoveEvent(self, event):      
    if self.moving == True:
        super().mousePressEvent(event)
        super().mouseMoveEvent(event)

def mouseReleaseEvent(self, event):      
    pass

I am not able to pick up and move the item so far, does anyone spot anything wrong? Also, where can I find the original implementation of the QGraphicsScene mouseEvent function?

Thank you!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

热鲨 2025-02-12 21:33:49

首先,您将永远不要 调用具有错误参数类型的事件处理程序的基本实现。
使用鼠标移动事件来调用MousePressEvent,因为其参数在概念上是错误的,虽然在图形项目上完成时通常并不“危险”,但在窗口小部件上完成时通常会导致致命的崩溃。

也就是说,如果您想在鼠标上按下鼠标按下和释放,这显然意味着您必须在Mouserelealeaseevent() handler中更改“关注状态”,不是MousePressEvent()一个。

Furthermore, you must ensure that:

  1. the item becomes the mouse grabber 已发布鼠标按钮;
  2. 该项目还接受悬停事件 /em>悬停事件(最重要的是,悬停移动事件)总是由该项目接收和处理;

大约在第一个点,一般而言,当按下任何鼠标按钮并释放该按钮时,通常说一个小部件(或图形项)将变成鼠标抓取。成为鼠标抓手意味着将所有鼠标事件都派遣到该项目,直到释放鼠标为止。由于您希望能够在释放鼠标按钮后能够接收鼠标事件,因此在释放按钮时必须明确抓取(或删除)鼠标。

在下面的示例中,我将显示如何实现上述内容,并提供对已经移动的项目的支持(通过itemismobleable flag)。显示“可移动标志”文本的项目也可以在按下左键时移动,否则仅提供其他项目的按下/发布支持。

请注意,基于鼠标事件的移动项目必须考虑项目转换:事件的pos()始终在本地坐标中映射,因此,如果您单击矩形的左上角,项目旋转,您将始终获得左上角的位置。由于setPos()使用 parent 坐标系统,因此我们必须将这些位置映射到父级,以实现正确的运动。这也意味着复杂的转换(考虑父母)可能会使事情变得更加困难,因此请注意。对于更复杂的方案,您可能必须进一步实施目标位置的计算,或者坚持默认行为。

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class NoMouseButtonMoveRectItem(QGraphicsRectItem):
    moving = False
    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if event.button() == Qt.LeftButton:
            # by defaults, mouse press events are not accepted/handled,
            # meaning that no further mouseMoveEvent or mouseReleaseEvent
            # will *ever* be received by this item; with the following,
            # those events will be properly dispatched
            event.accept()
            self.pressPos = event.screenPos()

    def mouseMoveEvent(self, event):
        if self.moving:
            # map the position to the parent in order to ensure that the
            # transformations are properly considered:
            currentParentPos = self.mapToParent(
                self.mapFromScene(event.scenePos()))
            originParentPos = self.mapToParent(
                self.mapFromScene(event.buttonDownScenePos(Qt.LeftButton)))
            self.setPos(self.startPos + currentParentPos - originParentPos)
        else:
            super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        if (event.button() != Qt.LeftButton 
            or event.pos() not in self.boundingRect()):
                return

        # the following code block is to allow compatibility with the
        # ItemIsMovable flag: if the item has the flag set and was moved while
        # keeping the left mouse button pressed, we proceed with our
        # no-mouse-button-moved approach only *if* the difference between the
        # pressed and released mouse positions is smaller than the application
        # default value for drag movements; in this way, small, involuntary
        # movements usually created between pressing and releasing the mouse
        # button will still be considered as candidates for our implementation;
        # if you are *not* interested in this flag, just ignore this code block
        distance = (event.screenPos() - self.pressPos).manhattanLength()
        if (not self.moving and distance > QApplication.startDragDistance()):
            return
        # end of ItemIsMovable support

        self.moving = not self.moving
        # the following is *mandatory*
        self.setAcceptHoverEvents(self.moving)
        if self.moving:
            self.startPos = self.pos()
            self.grabMouse()
        else:
            self.ungrabMouse()


if __name__ == '__main__':
    import sys
    from random import randrange, choice
    app = QApplication(sys.argv)
    scene = QGraphicsScene()
    view = QGraphicsView(scene)
    view.resize(QApplication.primaryScreen().size() * 2 / 3)
    # create random items that support click/release motion
    for i in range(10):
        item = NoMouseButtonMoveRectItem(0, 0, 100, 100)
        item.setPos(randrange(500), randrange(500))
        item.setPen(QColor(*(randrange(255) for _ in range(3))))
        if choice((0, 1)):
            item.setFlags(item.ItemIsMovable)
            QGraphicsSimpleTextItem('Movable flag', item)
        else:
            item.setBrush(QColor(*(randrange(255) for _ in range(3))))
        scene.addItem(item)
    view.show()
    sys.exit(app.exec_())

First of all, you shall never call the base implementation of an event handler with a wrong argument type.
Calling mousePressEvent using a mouse move event as its argument is conceptually wrong, and while it's generally not "dangerous" when done on graphics items, it usually causes a fatal crash when done on widgets.

That said, if you want to follow the mouse upon mouse press and release, that obviously means that you have to change the "follow state" within the mouseReleaseEvent() handler, not the mousePressEvent() one.

Furthermore, you must ensure that:

  1. the item becomes the mouse grabber after the mouse button has been released;;
  2. the item also accepts hover events, so that any hover event (most importantly, hover move events) are always received and handled by the item;

About the first point, generally speaking a widget (or graphics item) becomes the mouse grabber when any mouse button is pressed on it and until that button has been released. Becoming a mouse grabber means that all mouse events are dispatched to that item until it releases the mouse; since you want to be able to receive mouse events after releasing the mouse button, you have to explicitly grab (or ungrab) the mouse when the button is released.

In the following example I'm showing how to implement the above, and also providing support for items that are already movable (through the ItemIsMovable flag). Items that show the "Movable flag" text can also be moved while keeping the left button pressed, otherwise only the press/release support is provided for other items.

Be aware that moving items based on mouse events has to consider the item transformations: the pos() of events is always mapped in local coordinates, so if you click on the top left corner of a rectangle and the item is rotated, you will always get that top left corner position. Since setPos() uses the parent coordinate system, we have to map those positions to the parent in order to achieve proper movement. This also means that complex transformations (considering the parent) could make things much more difficult, so be aware of that. For more complex scenarios, you might have to further implement the computation of the target position, or stick with the default behavior.

from PyQt5.QtGui import *
from PyQt5.QtWidgets import *
from PyQt5.QtCore import *

class NoMouseButtonMoveRectItem(QGraphicsRectItem):
    moving = False
    def mousePressEvent(self, event):
        super().mousePressEvent(event)
        if event.button() == Qt.LeftButton:
            # by defaults, mouse press events are not accepted/handled,
            # meaning that no further mouseMoveEvent or mouseReleaseEvent
            # will *ever* be received by this item; with the following,
            # those events will be properly dispatched
            event.accept()
            self.pressPos = event.screenPos()

    def mouseMoveEvent(self, event):
        if self.moving:
            # map the position to the parent in order to ensure that the
            # transformations are properly considered:
            currentParentPos = self.mapToParent(
                self.mapFromScene(event.scenePos()))
            originParentPos = self.mapToParent(
                self.mapFromScene(event.buttonDownScenePos(Qt.LeftButton)))
            self.setPos(self.startPos + currentParentPos - originParentPos)
        else:
            super().mouseMoveEvent(event)

    def mouseReleaseEvent(self, event):
        super().mouseReleaseEvent(event)
        if (event.button() != Qt.LeftButton 
            or event.pos() not in self.boundingRect()):
                return

        # the following code block is to allow compatibility with the
        # ItemIsMovable flag: if the item has the flag set and was moved while
        # keeping the left mouse button pressed, we proceed with our
        # no-mouse-button-moved approach only *if* the difference between the
        # pressed and released mouse positions is smaller than the application
        # default value for drag movements; in this way, small, involuntary
        # movements usually created between pressing and releasing the mouse
        # button will still be considered as candidates for our implementation;
        # if you are *not* interested in this flag, just ignore this code block
        distance = (event.screenPos() - self.pressPos).manhattanLength()
        if (not self.moving and distance > QApplication.startDragDistance()):
            return
        # end of ItemIsMovable support

        self.moving = not self.moving
        # the following is *mandatory*
        self.setAcceptHoverEvents(self.moving)
        if self.moving:
            self.startPos = self.pos()
            self.grabMouse()
        else:
            self.ungrabMouse()


if __name__ == '__main__':
    import sys
    from random import randrange, choice
    app = QApplication(sys.argv)
    scene = QGraphicsScene()
    view = QGraphicsView(scene)
    view.resize(QApplication.primaryScreen().size() * 2 / 3)
    # create random items that support click/release motion
    for i in range(10):
        item = NoMouseButtonMoveRectItem(0, 0, 100, 100)
        item.setPos(randrange(500), randrange(500))
        item.setPen(QColor(*(randrange(255) for _ in range(3))))
        if choice((0, 1)):
            item.setFlags(item.ItemIsMovable)
            QGraphicsSimpleTextItem('Movable flag', item)
        else:
            item.setBrush(QColor(*(randrange(255) for _ in range(3))))
        scene.addItem(item)
    view.show()
    sys.exit(app.exec_())
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文