Question

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.

Edit-1:

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.

Edit-2:

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.

Was it helpful?

Solution 2

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.

OTHER TIPS

Sorry, I've been unable to reproduce the segfaults you are seeing. Here's the full source code of the application I tried to reproduce your crash with (Qt 4.8.1, PyQt 4.9.1, Python 2.7.3, Kubuntu Precise):

#!/usr/bin/env python

import sys
from PyQt4 import QtCore, QtGui

class AppStatus(object):
    def __init__(self):
        self.timer = None

class ExperimentAnalyzer(QtGui.QMainWindow):
    def __init__(self, parent=None):
        super(ExperimentAnalyzer, self).__init__(parent)
        elayout = QtGui.QVBoxLayout()

        self.count = 0
        self.appStatus = AppStatus()

        everything = QtGui.QWidget(self)
        everything.setLayout(elayout)

        button = QtGui.QPushButton("Start/stop timer", self)
        self.connect(button, QtCore.SIGNAL("clicked()"), self.playOrPause)
        elayout.addWidget(button)

        button = QtGui.QPushButton("Load experiment", self)
        self.connect(button, QtCore.SIGNAL("clicked()"), self.loadExperiment)
        elayout.addWidget(button)

        self.setCentralWidget(everything)

    def loadExperiment(self):
        directory = QtGui.QFileDialog.getExistingDirectory(self, "Select Directory")

    def nextFrame(self):
        self.count += 1
        print self.count

    def playOrPause(self):
        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)

        else:
            self.appStatus.timer.stop()
            self.appStatus.timer = None

if __name__ == "__main__":
    app = QtGui.QApplication(sys.argv)
    mainwin = ExperimentAnalyzer(None)
    mainwin.show()
    app.exec_()

If this test application doesn't crash for you, then the problem is in code that you haven't shown us.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top