在主窗口中添加子窗口

问题描述

我在主窗口中添加一个可拖动的红色圆圈。可以将红色圆圈移动到主窗口上您想要的任何位置。但我想设置一个允许红色圆圈移动的边框。可能应该用子窗口来完成?任何有想法如何做到这一点的人?我走了这么远:

import sys

from PyQt5.QtWidgets import QApplication,QGraphicsView,QWidget,QGraphicsEllipseItem,QMainWindow,qgroupbox,QGraphicsScene,QHBoxLayout
from PyQt5.QtCore import Qt,QPointF,QRect


class MovingObject(QGraphicsEllipseItem):
    def __init__(self,x,y,r):
        #de meegegeven waardes gebruiken om beginpositie en grootte ellips te bepalen
        super().__init__(0,r,r)
        self.setPos(x,y)
        self.setBrush(Qt.red)

    ##mousepressEvent checkt of er wel of niet wordt geklikt
    def mousepressEvent(self,event):
        pass

    ##mouseMoveEvent is om de item te kunnen draggen
    def mouseMoveEvent(self,event):
        orig_cursor_position = event.lastScenePos()
        updated_cursor_position = event.scenePos()

        orig_position = self.scenePos()

        updated_cursor_x = updated_cursor_position.x() - orig_cursor_position.x() + orig_position.x()
        updated_cursor_y = updated_cursor_position.y() - orig_cursor_position.y() + orig_position.y()
        self.setPos(QPointF(updated_cursor_x,updated_cursor_y))


class GraphicView(QGraphicsView):
    def __init__(self):
        super().__init__()

        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.setSceneRect(0,60,60)


        #waardes x,r waarvan x en y beginpositie van ellips is en r is straal van ellips
        self.scene.addItem(MovingObject(0,40))

class Window(QMainWindow):
    def __init__(self):
        super().__init__()
        self.setGeometry(800,500,400,400)
        self.setwindowTitle("MainWindow")

        #set GraphicView in Window
        self.graphicView = GraphicView()
        self.setCentralWidget(self.graphicView)


app = QApplication(sys.argv)

GUI = Window()
GUI.show()

sys.exit(app.exec_())

解决方法

首先,无需重新实现鼠标事件以允许使用鼠标移动 QGraphicsItem,因为设置 ItemIsMovable 标志就足够了。

设置标志后,需要过滤几何变化并对其做出反应。

这是通过设置 ItemSendsGeometryChanges 标志并重新实现 itemChange() 函数来实现的。使用 ItemPositionChange 更改,您可以在应用之前接收“future”位置,并最终更改(并返回)接收到的值;返回值是移动过程中将应用的实际最终位置。

剩下的就是给个参考检查一下。
在下面的示例中,我将设置具有更大边距的 scene 矩形(而不是您所做的 view 矩形),并为项目设置这些边距;您显然可以为此设置任何 QRectF

我还实现了 drawBackground() 函数以显示用作限制的场景矩形。

class MovingObject(QGraphicsEllipseItem):
    def __init__(self,x,y,r):
        super().__init__(0,r,r)
        self.setPos(x,y)
        self.setBrush(Qt.red)
        self.setFlag(self.ItemIsMovable,True)
        self.setFlag(self.ItemSendsGeometryChanges,True)
        self.margins = None

    def setMargins(self,margins):
        self.margins = margins

    def itemChange(self,change,value):
        if change == self.ItemPositionChange and self.margins:
            newRect = self.boundingRect().translated(value)
            if newRect.x() < self.margins.x():
                # beyond left margin,reset
                value.setX(self.margins.x())
            elif newRect.right() > self.margins.right():
                # beyond right margin
                value.setX(self.margins.right() - newRect.width())
            if newRect.y() < self.margins.y():
                # beyond top margin
                value.setY(self.margins.y())
            elif newRect.bottom() > self.margins.bottom():
                # beyond bottom margin
                value.setY(self.margins.bottom() - newRect.height())
        return super().itemChange(change,value)


class GraphicView(QGraphicsView):
    def __init__(self):
        super().__init__()

        self.scene = QGraphicsScene()
        self.setScene(self.scene)
        self.scene.setSceneRect(0,120,120)

        self.movingObject = MovingObject(0,40)
        self.scene.addItem(self.movingObject)
        self.movingObject.setMargins(self.scene.sceneRect())

    def drawBackground(self,painter,rect):
        painter.drawRect(self.sceneRect())

请注意,图形视图框架功能强大,但真正很难了解和理解。鉴于您提出的问题(“可能应该使用子窗口完成?”),很明显您仍然需要了解它的工作原理,因为使用子窗口是完全不同的事情。
我强烈建议您仔细查看它的 documentation 和与 QGraphicsItem 相关的一切(函数和属性),它是所有图形的基类项。

现有属性永远不应该被覆盖; scene() 是 QGraphicsView 的一个基本属性,因此您可以选择另一个名称作为实例属性(self.myScene = QGraphicsScene()),或者您只使用局部变量(scene = QGraphicsScene())并始终使用 {{1 }} 在 self.scene() 之外。