Warning: file_get_contents(/data/phpspider/zhask/data//catemap/2/python/312.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Python 在pyqt中使用sceneEventFilter捕获悬停事件_Python_Pyqt_Pyqt5_Pyqtgraph_Eventfilter - Fatal编程技术网

Python 在pyqt中使用sceneEventFilter捕获悬停事件

Python 在pyqt中使用sceneEventFilter捕获悬停事件,python,pyqt,pyqt5,pyqtgraph,eventfilter,Python,Pyqt,Pyqt5,Pyqtgraph,Eventfilter,我在图形场景中有用户可调整的注释。注释的大小/旋转是通过围绕注释拖动矩形的角来处理的。我使用的是一个自定义矩形(而不是boundingRect),因此它遵循父注释的旋转。控制角由两个椭圆标记,其父对象是rect,因此rect/椭圆/注释的变换是无缝的 我想检测光标何时在某个角上,它是哪个角,以及精确的坐标。对于这个任务,我似乎应该使用sceneEventFilter用父rect过滤hoverevents 我尝试过无数种实现sceneEventFilter的方法,但都没有效果。所有事件直接进入ho

我在图形场景中有用户可调整的注释。注释的大小/旋转是通过围绕注释拖动矩形的角来处理的。我使用的是一个自定义矩形(而不是boundingRect),因此它遵循父注释的旋转。控制角由两个椭圆标记,其父对象是rect,因此rect/椭圆/注释的变换是无缝的

我想检测光标何时在某个角上,它是哪个角,以及精确的坐标。对于这个任务,我似乎应该使用sceneEventFilter用父rect过滤hoverevents

我尝试过无数种实现sceneEventFilter的方法,但都没有效果。所有事件直接进入hoverEnterEvent函数。我只找到了一些类似这样的示例代码,但我完全被卡住了。顺便说一句,在过去的3个月里,我完全自学了Python和QT,所以请容忍我。我肯定我错过了一些非常基本的东西。代码是一个带有两个省略号的简化gui。我们希望在sceneEventFilter中捕获事件,但总是转到hoverEnterEvent

from pyqtgraph.Qt import QtGui, QtCore
import pyqtgraph as pg
from PyQt5.QtWidgets import QGraphicsScene, QGraphicsView, QGraphicsItem
import sys

class myHandle(QtGui.QGraphicsEllipseItem):
    def __init__(self, parent = None):
        super(myHandle, self).__init__(parent)

    def addTheHandle(self, h_parent = 'null', kind = 'null'):
        handle_w = 40
        if kind == 'scaling handle':
            handle_x = h_parent.boundingRect().topRight().x() - handle_w/2
            handle_y = h_parent.boundingRect().topRight().y() - handle_w/2
        if kind == 'rotation handle':
            handle_x = h_parent.boundingRect().topLeft().x() - handle_w/2
            handle_y = h_parent.boundingRect().topLeft().y() - handle_w/2
        the_handle = QtGui.QGraphicsEllipseItem(QtCore.QRectF(handle_x, handle_y, handle_w, handle_w))
        the_handle.setPen(QtGui.QPen(QtGui.QColor(255, 100, 0), 3))
        the_handle.setParentItem(h_parent)
        the_handle.setAcceptHoverEvents(True)
        the_handle.kind = kind

        return the_handle

class myRect(QtGui.QGraphicsRectItem):
    def __init__(self, parent = None):
        super(myRect, self).__init__(parent)

    def rectThing(self, boundingrectangle):
        self.setAcceptHoverEvents(True)
        self.setRect(boundingrectangle)
        mh = myHandle()
        rotation_handle = mh.addTheHandle(h_parent = self, kind = 'rotation handle')
        scaling_handle  = mh.addTheHandle(h_parent = self, kind = 'scaling handle')        
        self.installSceneEventFilter(rotation_handle)
        self.installSceneEventFilter(scaling_handle)

        return self, rotation_handle, scaling_handle

    def sceneEventFilter(self, event):    
        print('scene ev filter')
        return False

    def hoverEnterEvent(self, event):
        print('hover enter event')

class Basic(QtGui.QMainWindow):
    def __init__(self):
        super(Basic, self).__init__()
        self.initUI()

    def eventFilter(self, source, event):
        return QtGui.QMainWindow.eventFilter(self, source, event)

    def exit_the_program(self):
        pg.exit()

    def initUI(self):
        self.resize(300, 300)

        self.centralwidget = QtGui.QWidget()
        self.setCentralWidget(self.centralwidget)        
        self.h_layout = QtGui.QHBoxLayout(self.centralwidget)

        self.exit_program = QtGui.QPushButton('Exit')
        self.exit_program.clicked.connect(self.exit_the_program)        
        self.h_layout.addWidget(self.exit_program)

        self.this_scene = QGraphicsScene()
        self.this_view = QGraphicsView(self.this_scene)
        self.this_view.setMouseTracking(True)
        self.this_view.viewport().installEventFilter(self)

        self.h_layout.addWidget(self.this_view)        
        self.circle = self.this_scene.addEllipse(QtCore.QRectF(40, 40, 65, 65), QtGui.QPen(QtCore.Qt.black))

        mr = myRect()
        the_rect, rotation_handle, scaling_handle = mr.rectThing(self.circle.boundingRect())
        the_rect.setPen(QtGui.QPen(QtCore.Qt.black))
        the_rect.setParentItem(self.circle)

        self.this_scene.addItem(the_rect)
        self.this_scene.addItem(rotation_handle)
        self.this_scene.addItem(scaling_handle)        

def main():
    app = QtGui.QApplication([])
    main = Basic()
    main.show()
    sys.exit(app.exec_())

if __name__ == '__main__':
    main()

主要问题是您正在矩形上安装目标项的事件过滤器:矩形的事件过滤器将永远不会接收任何内容。此外,
sceneEventFilter
接受两个参数(监视的项目和事件),但您只使用了一个

您应该做的是在目标项上安装矩形的事件过滤器:

    rotation_handle.installSceneEventFilter(self)
    scaling_handle.installSceneEventFilter(self)
也就是说,如果您想使用这些椭圆项来缩放或旋转源圆,那么您的方法一开始就有点错误

from math import sqrt
# ...

class myRect(QtGui.QGraphicsRectItem):
    def __init__(self, parent):
        super(myRect, self).__init__(parent)
        self.setRect(parent.boundingRect())
        # rotation is usually based on the center of an object
        self.parentItem().setTransformOriginPoint(self.parentItem().rect().center())

        # a rectangle that has a center at (0, 0)
        handleRect = QtCore.QRectF(-20, -20, 40, 40)

        self.rotation_handle = QtGui.QGraphicsEllipseItem(handleRect, self)
        self.scaling_handle = QtGui.QGraphicsEllipseItem(handleRect, self)
        # position the handles by centering them at the right corners
        self.rotation_handle.setPos(self.rect().topLeft())
        self.scaling_handle.setPos(self.rect().topRight())
        for source in (self.rotation_handle, self.scaling_handle):
            # install the *self* event filter on the handles
            source.installSceneEventFilter(self)
            source.setPen(QtGui.QPen(QtGui.QColor(255, 100, 0), 3))

    def sceneEventFilter(self, source, event):
        if event.type() == QtCore.QEvent.GraphicsSceneMouseMove:
            # map the handle event position to the ellipse parent item; we could
            # also map to "self", but using the parent is more consistent
            localPos = self.parentItem().mapFromItem(source, event.pos())
            if source == self.rotation_handle:
                # create a temporary line to get the rotation angle
                line = QtCore.QLineF(self.boundingRect().center(), localPos)
                # add the current rotation to the angle between the center and the
                # top left corner, then subtract the new line angle
                self.parentItem().setRotation(135 + self.parentItem().rotation() - line.angle())
                # note that I'm assuming that the ellipse is a circle, so the top
                # left angle will always be at 135°; if it's not a circle, the
                # rect width and height won't match and the angle will be
                # different, so you'll need to compute that

                # parentRect = self.parentItem().rect()
                # oldLine = QtCore.QLineF(parentRect.center(), parentRect.topLeft())
                # self.parentItem().setRotation(
                #     oldLine.angle() + self.parentItem().rotation() - line.angle())

            elif source == self.scaling_handle:
                # still assuming a perfect circle, so the rectangle is a square;
                # the line from the center to the top right corner is used to
                # compute the square side size, which is the double of a
                # right-triangle cathetus where the hypotenuse is the line
                # between the center and any of its corners;
                # if the ellipse is not a perfect circle, you'll have to
                # compute both of the catheti
                hyp = QtCore.QLineF(self.boundingRect().center(), localPos)
                size = sqrt(2) * hyp.length()
                rect = QtCore.QRectF(0, 0, size, size)
                rect.moveCenter(self.rect().center())
                self.parentItem().setRect(rect)
                self.setRect(rect)
                # update the positions of both handles
                self.rotation_handle.setPos(self.rect().topLeft())
                self.scaling_handle.setPos(self.rect().topRight())
                return True
        elif event.type() == QtCore.QEvent.GraphicsSceneMousePress:
            # return True to the press event (which is almost as setting it as
            # accepted, so that it won't be processed further more by the scene,
            # allowing the sceneEventFilter to capture the following mouseMove
            # events that the watched graphics items will receive
            return True
        return super(myRect, self).sceneEventFilter(source, event)

class Basic(QtGui.QMainWindow):
    # ...
    def initUI(self):
        # ...
        self.circle = self.this_scene.addEllipse(QtCore.QRectF(40, 40, 65, 65), QtGui.QPen(QtCore.Qt.black))

        mr = myRect(self.circle)
        self.this_scene.addItem(mr)


主要问题是您正在矩形上安装目标项的事件过滤器:矩形的事件过滤器将永远不会接收任何内容。此外,
sceneEventFilter
接受两个参数(监视的项目和事件),但您只使用了一个

您应该做的是在目标项上安装矩形的事件过滤器:

    rotation_handle.installSceneEventFilter(self)
    scaling_handle.installSceneEventFilter(self)
也就是说,如果您想使用这些椭圆项来缩放或旋转源圆,那么您的方法一开始就有点错误

from math import sqrt
# ...

class myRect(QtGui.QGraphicsRectItem):
    def __init__(self, parent):
        super(myRect, self).__init__(parent)
        self.setRect(parent.boundingRect())
        # rotation is usually based on the center of an object
        self.parentItem().setTransformOriginPoint(self.parentItem().rect().center())

        # a rectangle that has a center at (0, 0)
        handleRect = QtCore.QRectF(-20, -20, 40, 40)

        self.rotation_handle = QtGui.QGraphicsEllipseItem(handleRect, self)
        self.scaling_handle = QtGui.QGraphicsEllipseItem(handleRect, self)
        # position the handles by centering them at the right corners
        self.rotation_handle.setPos(self.rect().topLeft())
        self.scaling_handle.setPos(self.rect().topRight())
        for source in (self.rotation_handle, self.scaling_handle):
            # install the *self* event filter on the handles
            source.installSceneEventFilter(self)
            source.setPen(QtGui.QPen(QtGui.QColor(255, 100, 0), 3))

    def sceneEventFilter(self, source, event):
        if event.type() == QtCore.QEvent.GraphicsSceneMouseMove:
            # map the handle event position to the ellipse parent item; we could
            # also map to "self", but using the parent is more consistent
            localPos = self.parentItem().mapFromItem(source, event.pos())
            if source == self.rotation_handle:
                # create a temporary line to get the rotation angle
                line = QtCore.QLineF(self.boundingRect().center(), localPos)
                # add the current rotation to the angle between the center and the
                # top left corner, then subtract the new line angle
                self.parentItem().setRotation(135 + self.parentItem().rotation() - line.angle())
                # note that I'm assuming that the ellipse is a circle, so the top
                # left angle will always be at 135°; if it's not a circle, the
                # rect width and height won't match and the angle will be
                # different, so you'll need to compute that

                # parentRect = self.parentItem().rect()
                # oldLine = QtCore.QLineF(parentRect.center(), parentRect.topLeft())
                # self.parentItem().setRotation(
                #     oldLine.angle() + self.parentItem().rotation() - line.angle())

            elif source == self.scaling_handle:
                # still assuming a perfect circle, so the rectangle is a square;
                # the line from the center to the top right corner is used to
                # compute the square side size, which is the double of a
                # right-triangle cathetus where the hypotenuse is the line
                # between the center and any of its corners;
                # if the ellipse is not a perfect circle, you'll have to
                # compute both of the catheti
                hyp = QtCore.QLineF(self.boundingRect().center(), localPos)
                size = sqrt(2) * hyp.length()
                rect = QtCore.QRectF(0, 0, size, size)
                rect.moveCenter(self.rect().center())
                self.parentItem().setRect(rect)
                self.setRect(rect)
                # update the positions of both handles
                self.rotation_handle.setPos(self.rect().topLeft())
                self.scaling_handle.setPos(self.rect().topRight())
                return True
        elif event.type() == QtCore.QEvent.GraphicsSceneMousePress:
            # return True to the press event (which is almost as setting it as
            # accepted, so that it won't be processed further more by the scene,
            # allowing the sceneEventFilter to capture the following mouseMove
            # events that the watched graphics items will receive
            return True
        return super(myRect, self).sceneEventFilter(source, event)

class Basic(QtGui.QMainWindow):
    # ...
    def initUI(self):
        # ...
        self.circle = self.this_scene.addEllipse(QtCore.QRectF(40, 40, 65, 65), QtGui.QPen(QtCore.Qt.black))

        mr = myRect(self.circle)
        self.this_scene.addItem(mr)