1

I'm using Qt's Graphics View Framework to display a large number of images, and implementing it using PyQt4 and Python 2.7. I instantiate a number of QPixmapItem objects and add them to my QGraphicsScene. It all works as I'd expect it to until I exit the application, and the program crashes instead of exiting normally.

class ImgDisplay(QtGui.QWidget):
    NUM_ITEMS = 5000

    VIEW_WIDTH,VIEW_HEIGHT = 400, 400

    def __init__(self):
        super(ImgDisplay, self).__init__()

        self.scene = QtGui.QGraphicsScene(QtCore.QRectF(0,0,ImgDisplay.VIEW_WIDTH,ImgDisplay.VIEW_HEIGHT))

        self.view = QtGui.QGraphicsView(self.scene)
        self.view.setParent(self)

        #Load the texture
        self.texture = QtGui.QPixmap('image.png')

        self.populateScene()

    def populateScene(self):

        for i in range(0, ImgDisplay.NUM_ITEMS-1):
            item = QtGui.QGraphicsPixmapItem(self.texture)

            self.scene.addItem(item)

I'm thinking that all those PixMapItems I'm creating aren't being cleaned up properly, or maybe I need to free the texture that I load (there doesn't seem to be a method to free it, so I assumed it happened in the background).

I've tried calling self.scene.clear in a destructor to delete the PixmapItems, but it didn't help.

Any suggestions on how I can fix this problem?

*I'm aware that the posted code just puts the images all on top of each other, my actual program assigns them random positions and rotations, but I wanted to reduce this to the minimal problem.

Josh
  • 373
  • 4
  • 15

2 Answers2

1

OK, understood. Problem is QtGui.QGraphicsPixmapItem can't clear itself your have to manual just like you says, but not destructor. I recommend doing after have signal close program by using closeEvent, like this;

def closeEvent (self, eventQCloseEvent):
    self.scene.clear() # Clear QGraphicsPixmapItem
    eventQCloseEvent.accept() # Accept to close program

and this implemented you code;

import sys
from PyQt4 import QtCore, QtGui

class ImgDisplay (QtGui.QWidget):
    NUM_ITEMS = 5000
    VIEW_WIDTH,VIEW_HEIGHT = 400, 400

    def __init__ (self):
        super(ImgDisplay, self).__init__()
        self.scene = QtGui.QGraphicsScene(QtCore.QRectF(0,0,ImgDisplay.VIEW_WIDTH,ImgDisplay.VIEW_HEIGHT))
        self.view = QtGui.QGraphicsView(self.scene)
        self.view.setParent(self)
        #Load the texture
        self.texture = QtGui.QPixmap('image.png')
        self.populateScene()

    def populateScene (self):
        for i in range(0, ImgDisplay.NUM_ITEMS-1):
            item = QtGui.QGraphicsPixmapItem(self.texture)
            self.scene.addItem(item)

    def closeEvent (self, eventQCloseEvent):
        self.scene.clear()
        eventQCloseEvent.accept()

app = QtGui.QApplication([])
window = ImgDisplay()
window.show()
sys.exit(app.exec_())

I use PyQt4 (Windows 7).

And this useful to implement close event, Hope is helps;

QWidget Close Event Reference : http://pyqt.sourceforge.net/Docs/PyQt4/qwidget.html#closeEvent


LAST EDITED : 8 / 11 / 2014 01:51

If your want to control your parent & child widget to delete together, I have to implement destructor method (As your say). By use safe delete method QObject.deleteLater (self), Like this;

import sys
from PyQt4 import QtCore, QtGui

class ImgDisplay (QtGui.QWidget):
    NUM_ITEMS = 5000
    VIEW_WIDTH, VIEW_HEIGHT = 400, 400

    def __init__ (self, parent = None):
        super(ImgDisplay, self).__init__(parent)
        self.scene = QtGui.QGraphicsScene(QtCore.QRectF(0,0,ImgDisplay.VIEW_WIDTH,ImgDisplay.VIEW_HEIGHT), parent = self)
        self.view = QtGui.QGraphicsView(self.scene, parent = self)
        #Load the texture
        self.texture = QtGui.QPixmap('image.png')
        self.populateScene()

    def populateScene (self):
        for i in range(0, ImgDisplay.NUM_ITEMS-1):
            item = QtGui.QGraphicsPixmapItem(self.texture)
            self.scene.addItem(item)

    def __del__ (self):
        self.deleteLater() # Schedules this object for deletion 

app = QtGui.QApplication([])
window = ImgDisplay()
window.show()
sys.exit(app.exec_())

Warning : Don't forget set parent in your widget ! (Because some time It can't be delete itself.)

deleteLater Reference : http://pyqt.sourceforge.net/Docs/PyQt4/qobject.html#deleteLater


Regards,

Bandhit Suksiri
  • 3,390
  • 1
  • 18
  • 20
  • For some reason my closeEvent() reimplemntation wasn't being run at exit. (At least the print statement I put in it was never being displayed.) I did manage to prevent the crash by explicitly calling setParent() in a chain all the way from my main window to the QGraphicsScene. With the parent hierarchy in place, the scene gets cleaned up automatically. – Josh Aug 10 '14 at 17:04
  • Your can just use 'QObject.deleteLater (self)' in Destructor to schedules this object for deletion. When object has been deleted. Please read my last edited answer. – Bandhit Suksiri Aug 10 '14 at 18:50
1

I would like to highlight Josh's answer in the comments. If you create the scene by setting the QGraphicsView as parent the crash will automatically go away.

 self.view = QtGui.QGraphicsView(parent = self)
 self.scene = QtGui.QGraphicsScene(QtCore.QRectF(0,0,ImgDisplay.VIEW_WIDTH,ImgDisplay.VIEW_HEIGHT), parent = self.view)
 self.view.setScene(self.scene)