PyQt:在创建pixMap后显示文件对话框会产生分段错误

时间:2013-02-22 10:23:22

标签: python qt pyqt pyqt4 qpixmap

我正在使用PyQt开发GUI,以对某些实验期间收集的数据进行可视化分析。 GUI要求用户指出要分析的数据所在的目录:

class ExperimentAnalyzer(QtGui.QMainWindow):
    ## other stuff here

    def loadExperiment(self):
        directory = QtGui.QFileDialog.getExistingDirectory(self,
                                                           "Select Directory")
        ## load data from directory here

GUI提供 播放 功能,用户可以通过该功能查看实验数据随时间的变化情况。这是通过 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

如果我 播放 数据的时间顺序,则 暂停 ,然后尝试将目录更改为加载来自新实验的数据,我遇到分段错误

使用一些调试打印,我发现当我调用

时应用程序崩溃了
    QtGui.QFileDialog.getExistingDirectory(self, "Select Directory")

loadExperiment 方法。

我对Qt很新,我认为我没有正确处理计时器 我在Ubuntu 10.04上使用PyQt 4.9,Python 2.7.3。

修改-1

在卢克的回答之后,我回到了我的代码 这是 nextFrame 方法,每次定时器发出 超时 信号时都会调用此方法。它更新GUI中包含的QGraphicsScene元素:

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)

其中self.scene和self.view对象先前已在GUI构造函数中实例化为

self.view = QtGui.QGraphicsView(self)
self.scene = QtGui.QGraphicsScene()
self.view.setScene(self.scene)

我发现评论这一行:

    # self.scene.addPixmap(pixMap)

并重复上面报告的相同操作序列,不再发生分段错误。

修改-2

使用gdb(使用python-dbg)运行应用程序,结果发现在调用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

因此,正如我在第一时间所认为的那样,这不是与计时器处理相关的问题 发生分段错误是因为我对pixMap做错了。

2 个答案:

答案 0 :(得分:0)

抱歉,我无法重现您所看到的段错误。这是我尝试使用(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_()

如果此测试应用程序没有为您崩溃,则问题出在您未向我们展示的代码中。

答案 1 :(得分:0)

好的,我最终发现了问题所在。
我修改了 nextFrame 方法,以便保留对 ImageQt 对象的引用,即:< / p>

def nextFrame(self):
    ## load the image from file

    self.appStatus.imageQt = ImageQt.ImageQt(image)
    pixMap = QtGui.QPixmap.fromImage(self.appStatus.imageQt)

    ## update the QGraphicsView

并且分段错误消失了。

我对此的解释如下: Qt mantains,内部,指向 ImageQt 对象的指针,每次使用时都会使用触发 paintEvent (因此,必须重新绘制pixMap)。
不保留对 ImageQt 对象的引用,允许Python的GC收集它。因此, Qt 将尝试从已释放的内存区域读取,从而产生分段错误。