I have a QGraphicsScene where I have QGraphicsItems. These items are movable and I can move them all over the QGraphicsScene but I would like to limit the area where these items can be moved. The sizes of the QGraphicsScene don't have to change. I would really appreciate if someone gave me an example of how to do it in python.
Here's what I have now
from PySide2.QtCore import QPointF
from PySide2.QtWidgets import QWidget, QVBoxLayout, QGraphicsView, \
QGraphicsScene, QGraphicsPolygonItem, QApplication
from PySide2.QtGui import QPen, QColor, QBrush, QPolygonF
class Test(QWidget):
def __init__(self, parent=None):
super(Test, self).__init__(parent)
self.resize(1000, 800)
self.layout_ = QVBoxLayout()
self.view_ = GraphicsView()
self.layout_.addWidget(self.view_)
self.setLayout(self.layout_)
class GraphicsView(QGraphicsView):
def __init__(self):
super(GraphicsView, self).__init__()
self.scene_ = QGraphicsScene()
self.polygon_creation = self.PolyCreation()
self.scene_.setSceneRect(0, 0, 400, 400)
self.setScene(self.scene_)
self.polyCreator()
def polyCreator(self):
self.polygon_creation.poly()
polygon = self.polygon_creation.polygon()
new_poly = self.scene().addPolygon(polygon)
new_poly.setBrush(QBrush(QColor("gray")))
new_poly.setPen(QPen(QColor("gray")))
new_poly.setFlag(QGraphicsPolygonItem.ItemIsSelectable)
new_poly.setFlag(QGraphicsPolygonItem.ItemIsMovable)
new_poly.setFlag(QGraphicsPolygonItem.ItemIsFocusable)
new_poly.setPos(0, 0)
class PolyCreation(QGraphicsPolygonItem):
def __init__(self):
super().__init__()
self.setAcceptHoverEvents(True)
def poly(self):
self.poly_points = (QPointF(0, 0),
QPointF(0, 50),
QPointF(50, 50),
QPointF(50, 0))
self.shape = QPolygonF(self.poly_points)
self.setPolygon(self.shape)
if __name__ == '__main__':
app = QApplication([])
win = Test()
win.show()
app.exec_()
I've also found an answer in cpp, but I can't understand it very well, so if someone could "translate" it in python that'd be great too. Here's the link restrict movable area of qgraphicsitem (Please check @Robert's answer)
The concept is to restrict the new position before it's finally applied.
To achieve so, you need to also set the ItemSendsGeometryChanges
flag and check for ItemPositionChange
changes, then compare the item bounding rect with that of the scene, and eventually return a different position after correcting it.
class PolyCreation(QGraphicsPolygonItem):
def __init__(self):
super().__init__(QPolygonF([
QPointF(0, 0),
QPointF(0, 50),
QPointF(50, 50),
QPointF(50, 0)
]))
self.setBrush(QBrush(QColor("gray")))
self.setPen(QPen(QColor("blue")))
self.setFlags(
self.ItemIsSelectable
| self.ItemIsMovable
| self.ItemIsFocusable
| self.ItemSendsGeometryChanges
)
self.setAcceptHoverEvents(True)
def itemChange(self, change, value):
if change == self.ItemPositionChange and self.scene():
br = self.polygon().boundingRect().translated(value)
sceneRect = self.scene().sceneRect()
if not sceneRect.contains(br):
if br.right() > sceneRect.right():
br.moveRight(sceneRect.right())
if br.x() < sceneRect.x():
br.moveLeft(sceneRect.x())
if br.bottom() > sceneRect.bottom():
br.moveBottom(sceneRect.bottom())
if br.y() < sceneRect.y():
br.moveTop(sceneRect.top())
return br.topLeft()
return super().itemChange(change, value)
class GraphicsView(QGraphicsView):
def __init__(self):
super(GraphicsView, self).__init__()
self.scene_ = QGraphicsScene()
self.scene_.setSceneRect(0, 0, 400, 400)
self.setScene(self.scene_)
self.scene_.addItem(PolyCreation())
Notes:
sceneTransform()
to get the actual bounding rect of the polygon;boundingRect()
of the item and adjust it by using half the pen width;(0, 0)
by default, specifying it again is pointless;shape()
is an existing (and quite important) function of all items, you shall not overwrite it;