1

いくつかの実験中に収集されたデータの視覚的分析を実行するために、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 で何か間違ったことをしているため、セグメンテーション違反が発生します。

4

2 に答える 2

0

さて、私は最終的に問題が何であるかを知りました。ImageQtオブジェクトへの参照を保持するようにnextFrame
メソッド を変更しました。つまり、次のようになります。

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は、内部で、paintEventがトリガーされるたびに使用されるImageQtオブジェクトへのポインターを保持します(したがって、pixMapを再描画する必要があります)。ImageQtオブジェクト への参照を保持しないと、PythonのGCがそれを収集できます。その結果、Qtは解放されたメモリ領域からの読み取りを試み、セグメンテーション違反を引き起こします。

于 2013-02-23T11:30:41.737 に答える
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_()

このテスト アプリケーションがクラッシュしない場合、問題は示されていないコードにあります。

于 2013-02-22T10:59:07.780 に答える