Search code examples
pythonpython-3.xpyqtpyqt5fbs

PyQt5 : Wrapped c/c++ object has been deleted


I am trying to create my own graphicsitem with a circle and its label at the center.

class circle(QGraphicsItem):
    def __init__(self, radius=None, name=None, x=None, y=None, parent=None):
        super(circle, self).__init__(parent)
        self.radius = radius if radius else random.random()*500
        self.label = name if name else "cirA"
        self.x = x if x else random.randint(0, 900)
        self.y = y if y else random.randint(0, 600)
        # self.center = complex(self.x, self.y)

    def boundingRect(self):
        penWidth = 1.0
        return QRectF(-self.x - penWidth / 2, -self.y - penWidth / 2,
                      self.x + penWidth, self.y + penWidth)

    def paint(self, painter, option, widget):
        painter.drawEllipse(0, 0, self.radius, self.radius)
        painter.drawText(self.label)

Now in my main gui, a list of a circles is held as circleList and I am trying to add the circle item as

for cir in self.circleList:
            self.painter.addItem(cir)

but this returns

RuntimeError: wrapped C/C++ object of type circle has been deleted

help?

this should do the trick for the minimal reproduce able example

note: FBS is required and project should be started using

fbs startproject

the circle is also to be added along with the following

from fbs_runtime.application_context.PyQt5 import ApplicationContext
from PyQt5.QtWidgets import *
from PyQt5.QtGui import *
from PyQt5.QtCore import *
class gui(QDialog):
    def __init__(self, parent=None):
        super(gui, self).__init__(parent)
        self.painter = QGraphicsScene(10, 10, 900, 600)
        self.canvas = QGraphicsView(self.painter)
        mainLayout = QGridLayout()
        mainLayout.addWidget(self.canvas, 0, 0, 6, 2)
        self.setLayout(mainLayout)
        self.circleList = []

    def drawCircle(self):
        pen = QPen(Qt.black, 2, Qt.SolidLine)
        self.painter.clear()
        for cir in self.circleList:
            self.painter.addItem(cir)
        self.painter.update()
        self.canvas.update()
        self.update()

    def newCircle(self, cir):
        self.circleList.append(cir)
        self.drawCircle()

    def addCircle(self):
        return self.newCircle(circle())

if __name__ == "__main__":
    app = ApplicationContext()
    test = gui()
    test.show()
    exit(app.app.exec_())

Solution

  • Graphics View Framework does not need you to remove and add the items. In your case, when using clear(), you are removing the items from the scene and you are removing them from the memory, so circleList will have variables that no longer have allocated memory.

    On the other hand if you want a custom QGraphicsItem that draws a circle and a text in the center then the boundingRect must have the size of the rectangle ex-inscribed to the circle.

    Considering the above, the solution is as follows:

    class Circle(QGraphicsItem):
        def __init__(self, radius=None, name="", x=0, y=0, parent=None):
            super(Circle, self).__init__(parent)
            self.radius = radius or random.random() * 500
            self.label = name if name else "cirA"
            self.setPos(x or random.randint(0, 900), y or random.randint(0, 900))
    
        def boundingRect(self):
            return QRectF(0, 0, self.radius, self.radius)
    
        def paint(self, painter, option, widget):
            painter.drawEllipse(0, 0, self.radius, self.radius)
            painter.drawText(0, 0, self.radius, self.radius, Qt.AlignCenter, self.label)
    
    class Gui(QDialog):
        def __init__(self, parent=None):
            super(Gui, self).__init__(parent)
            self.painter = QGraphicsScene(10, 10, 900, 600)
            self.canvas = QGraphicsView(self.painter)
            mainLayout = QGridLayout(self)
            mainLayout.addWidget(self.canvas)
    
        @property
        def circleList(self):
            return [item for item in self.painter.items() if isinstance(item, Circle)]
    
        def newCircle(self, cir):
            self.painter.addItem(cir)
            return cir
    
        def addCircle(self):
            return self.newCircle(Circle())