I am developing a GUI with PyQt, to perform visual analysis of the data collected during some experiments. The GUI asks the user to indicate the directory where the data to be analyzed is located:
class ExperimentAnalyzer(QtGui.QMainWindow):
## other stuff here
def loadExperiment(self):
directory = QtGui.QFileDialog.getExistingDirectory(self,
"Select Directory")
## load data from directory here
The GUI provides a play functionality, by means of which the user can see how experimental data changes over time. This is implemented through a QTimer:
def playOrPause(self):
## play
if self.appStatus.timer is None:
self.appStatus.timer = QtCore.QTimer(self)
self.appStatus.timer.connect(self.appStatus.timer,
QtCore.SIGNAL("timeout()"),
self.nextFrame)
self.appStatus.timer.start(40)
## pause
else:
self.appStatus.timer.stop()
self.appStatus.timer = None
If I play the temporal sequence of data, then pause, then try to change the directory to load data from a new experiment, I experience a segmentation fault.
Using some debug prints, I found out that the application crashes when I call
QtGui.QFileDialog.getExistingDirectory(self, "Select Directory")
in the loadExperiment method.
I'm pretty new to Qt and I think I'm not handling the timer properly.
I'm using PyQt 4.9, Python 2.7.3 on Ubuntu 10.04.
After Luke's answer, I went back to my code.
Here's the nextFrame method, invoked every time the timer emits the timeout signal. It updates a QGraphicsScene element contained in the GUI:
def nextFrame(self):
image = Image.open("<some jpg>")
w, h = image.size
imageQt = ImageQt.ImageQt(image)
pixMap = QtGui.QPixmap.fromImage(imageQt)
self.scene.clear()
self.scene.addPixmap(pixMap)
self.view.fitInView(QtCore.QRectF(0, 0, w, h),
QtCore.Qt.KeepAspectRatio)
where the self.scene and the self.view objects have been previously instantiated in the GUI constructor as
self.view = QtGui.QGraphicsView(self)
self.scene = QtGui.QGraphicsScene()
self.view.setScene(self.scene)
I found out that commenting this line:
# self.scene.addPixmap(pixMap)
and repeating the same operations sequence reported above, the segmentation fault does not occur anymore.
Running the application with gdb (with python-dbg), it turns out that the segmentation fault occurs after a call to QPainter::drawPixmap.
(gdb) bt
#0 0xb6861f1d in ?? () from /usr/lib/i386-linux-gnu/libQtGui.so.4
#1 0xb685d491 in ?? () from /usr/lib/i386-linux-gnu/libQtGui.so.4
#2 0xb693bcd3 in ?? () from /usr/lib/i386-linux-gnu/libQtGui.so.4
#3 0xb69390bc in ?? () from /usr/lib/i386-linux-gnu/libQtGui.so.4
#4 0xb6945c77 in ?? () from /usr/lib/i386-linux-gnu/libQtGui.so.4
#5 0xb68bd424 in QPainter::drawPixmap(QPointF const&, QPixmap const&) () from /usr/lib/i386-linux-gnu/libQtGui.so.4
Therefore, it's not a problem related to timer handling, as I believed in first instance.
The segmentation fault happens because I'm doing something wrong with the pixMap.
Ok, I eventually found out what the problem was.
I modified the nextFrame method in such a way to keep a reference to the ImageQt object, that is:
def nextFrame(self):
## load the image from file
self.appStatus.imageQt = ImageQt.ImageQt(image)
pixMap = QtGui.QPixmap.fromImage(self.appStatus.imageQt)
## update the QGraphicsView
and the segmentation fault is gone.
My explanation for this is the following: Qt mantains, internally, a pointer to the ImageQt object, which is used every time a paintEvent is triggered (and, hence, the pixMap has to be redrawn).
Not keeping a reference to the ImageQt object allows the Python's GC to collect it. As a result, Qt will try reading from an area of memory which has been freed, producing a segmentation fault.