1

I'm trying to create a minimal QGraphicsItem that acts as a resizer to its parent. I think I'm nearly there but am drawing a blank and how to convey its position to the parent item when it is being moved. What I am going for looks something like this (without the text):

enter image description here

And here is a self contained example of what I have so far:

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *


class Resizer(QGraphicsEllipseItem):

    def __init__(self, rect=QRectF(0, 0, 10, 10), parent=None, scene=None):
        super().__init__(rect, parent, scene)

        self.setFlag(QGraphicsItem.ItemIsMovable, True)

    def mousePressEvent(self, mouseEvent):
        self.resize = True
        self.initialPos = self.scenePos()
        self.setSelected(True)

    def mouseMoveEvent(self, mouseEvent):
        if self.resize:
            self.currentPos = self.scenePos()
            self.change = self.initialPos - self.currentPos
        super().mouseMoveEvent(mouseEvent)

    def mouseReleaseEvent(self, mouseEvent):
        self.resize = False
        self.setSelected(False)


if __name__ == "__main__":

    app = QApplication(sys.argv)

    view = QGraphicsView()
    scene = QGraphicsScene()
    scene.setSceneRect(0, 0, 500, 500)
    view.setScene(scene)

    rect = QRectF(100, 100, 150, 50)
    box = QGraphicsRectItem(rect)

    scene.addItem(box)

    resizer = Resizer(parent=box)
    resizerWidth = resizer.rect().width() / 2
    resizerOffset = QPointF(resizerWidth, resizerWidth)
    resizer.setPos(box.rect().bottomRight() - resizerOffset)

    view.show()

    sys.exit(app.exec_())

So how can I convey self.change in the mouseMoveEvent to the parent in a way that the parent is resized as the Resizer is being moved? Any other suggestions are welcome.

I've tried converting the Resizer to a subclass of QGraphicsObject so that it could emit a signal on mouseMoveEvent, but QGraphicsObject does not have a paint method and I am unsure on how to display the Resizer on the scene.

Any advice on this is welcome.

Community
  • 1
  • 1
pbreach
  • 16,049
  • 27
  • 82
  • 120
  • @DanielePantaleone I somewhat agree. The way I solved it is a bit different though. I am posting my answer in case it could be useful. – pbreach Jun 16 '16 at 14:39
  • Exactly. Posting a good solution to a dupe has it's own perks. But if you post it to the original question (if you are sure that it is a new merit-able answer) then you might get more views to that (older questions with more views index more on Google Search). Take note the next time. – Bhargav Rao Jun 16 '16 at 20:24
  • @BhargavRao Great! Thanks for the advice - will post in orginal. – pbreach Jun 16 '16 at 20:26
  • Don't do so now. Keep that in mind the next time. – Bhargav Rao Jun 16 '16 at 20:28

1 Answers1

1

I ended converting the Resizer class to a subclass of QGraphicsObject. It turns out it was possible to reimplement the paint method (even though my auto-completion did not find it previously).

Resizer.itemChange now emits a signal which is assigned to the resize method in the parent Box class which uses the position change information from the resizer to adjust the rect of the box.

This way, the resizer can be resused for other items as well, without having to re-implement mouse press/move/release events as in the answer provided here.

import sys
from PyQt4.QtGui import *
from PyQt4.QtCore import *

class Box(QGraphicsRectItem):

    def __init__(self, position, rect=QRectF(0, 0, 100, 50), parent=None, scene=None):
        super().__init__(rect, parent, scene)

        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsFocusable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)

        self.setPos(position)

        self.resizer = Resizer(parent=self)
        resizerWidth = self.resizer.rect.width() / 2
        resizerOffset = QPointF(resizerWidth, resizerWidth)
        self.resizer.setPos(self.rect().bottomRight() - resizerOffset)
        self.resizer.resizeSignal.connect(self.resize)

    def paint(self, painter, option, widget=None):
        pen = QPen()
        pen.setColor(Qt.black)
        painter.setPen(pen)
        painter.setBrush(Qt.transparent)
        painter.drawRect(self.rect())

    @pyqtSlot()
    def resize(self, change):
        self.setRect(self.rect().adjusted(0, 0, change.x(), change.y()))
        self.prepareGeometryChange()
        self.update()


class Resizer(QGraphicsObject):

    resizeSignal = pyqtSignal(QPointF)

    def __init__(self, rect=QRectF(0, 0, 10, 10), parent=None):
        super().__init__(parent)

        self.setFlag(QGraphicsItem.ItemIsMovable, True)
        self.setFlag(QGraphicsItem.ItemIsSelectable, True)
        self.setFlag(QGraphicsItem.ItemSendsGeometryChanges, True)
        self.rect = rect

    def boundingRect(self):
        return self.rect

    def paint(self, painter, option, widget=None):
        if self.isSelected():
            pen = QPen()
            pen.setStyle(Qt.DotLine)
            painter.setPen(pen)
        painter.drawEllipse(self.rect)

    def itemChange(self, change, value):
        if change == QGraphicsItem.ItemPositionChange:
            if self.isSelected():
                self.resizeSignal.emit(value - self.pos())
        return value


if __name__ == "__main__":

    app = QApplication(sys.argv)

    view = QGraphicsView()
    scene = QGraphicsScene()
    scene.setSceneRect(0, 0, 500, 1000)
    view.setScene(scene)

    box = Box(QPointF(50, 50), scene=scene)

    view.show()

    sys.exit(app.exec_())
Community
  • 1
  • 1
pbreach
  • 16,049
  • 27
  • 82
  • 120