I am trying unsucessfully to implement a resizable rectangle QGraphicsItem in the pyside6 QGraphics framework by inheriting from QGraphicsRectItem
. In the ResizableRectItem
class, I create resize handles that are themselves QGraphicsRectItems
and children of the ResizableRectItem
, which allows them to be translated together with their parent item.
The problem I encounter and can't seem to solve, is that I want these handles to only appear when the Rectangle is selected with the mouse. This works fine (by overriding the itemChange
method), but the problem is that the handles can only be moved when the Rectangle is NOT selected. While the rectangle is selected, they cannot be moved.
Moreover, I don't know how, once I get the handles to move, I can propagate the movement of a specific handle to the parent rectangle, so as to resize its shape accordingly. I initially thought about using signals/slots, however this mechanism is unavailable since QGraphicsItem
does not inherit from QObject
. I read that there is also a QGraphicsObject
to provide signals/slots, but I suspect there might be a more elegant solution which I'm not seeing at the moment. I would be glad if someone could help me out here. I have googled this question extensively and not found a satisfactory answer. Thank you in advance!
import sys
from PySide6.QtCore import Qt
from PySide6.QtGui import QPen, QColor, QBrush
from PySide6.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsRectItem, QGraphicsItem
class ResizableRectItem(QGraphicsRectItem):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setPen(QPen(Qt.black))
self.setBrush(QBrush(Qt.gray))
self.setFlags(QGraphicsItem.ItemIsMovable | QGraphicsItem.ItemIsSelectable)
self.handleSize = 8
self.handles = {}
self.directions = ['topLeft', 'topRight', 'bottomLeft', 'bottomRight']
self.createHandles()
def createHandles(self):
rect = self.rect()
pen = QPen(QColor(0, 0, 0))
for direction in self.directions:
handle = QGraphicsRectItem(-self.handleSize/2, -self.handleSize/2, self.handleSize, self.handleSize, self)
handle.setPen(pen)
handle.setFlags(QGraphicsItem.ItemIsMovable)
handle.setVisible(False)
# Use getattr to replace this calls like this: rect.upperLeft()
handle.setPos(getattr(rect, direction)())
self.handles[direction] = handle
def itemChange(self, change, value):
# Intercept selection event to change visibility of handles
if change == QGraphicsItem.GraphicsItemChange.ItemSelectedChange:
for handle in self.handles.values():
handle.setVisible(bool(value))
# Pass to original method to handle all other changes
return super().itemChange(change, value)
class MainWindow(QMainWindow):
def __init__(self, *args, **kwargs):
super().__init__(*args, **kwargs)
self.setGeometry(500, 500, 500, 500)
self.view = QGraphicsView()
self.scene = QGraphicsScene()
self.view.setScene(self.scene)
rectItem = ResizableRectItem(100, 100, 200, 200)
self.scene.addItem(rectItem)
self.setCentralWidget(self.view)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = MainWindow()
window.show()
sys.exit(app.exec())