39

私は Python と Qt の両方が大好きですが、Qt が Python を念頭に置いて設計されていないことは明らかです。PyQt / PySide アプリケーションをクラッシュさせる方法は数多くありますが、その多くは適切なツールを使用してもデバッグが非常に困難です。

知りたいのですが、PyQt と PySide を使用するときにクラッシュやロックアップを回避するための良い方法は何ですか? これらは、一般的なプログラミングのヒントやサポート モジュールから、回避すべき非常に具体的な回避策やバグに至るまで、あらゆるものです。

4

4 に答える 4

57

一般的なプログラミングの実践

  • マルチスレッド コードを使用する必要がある場合は、決して非 GUI スレッドから GUI にアクセスしないでください。代わりに、常にシグナルまたはその他のスレッドセーフなメカニズムを発行して、GUI スレッドにメッセージを送信してください。
  • モデル/ビューには注意してください。TableView、TreeView など。正しくプログラムするのは難しく、間違いがあると追跡不能なクラッシュにつながります。モデル テストを使用して、モデルが内部的に一貫していることを確認します。
  • Qt オブジェクト管理が Python オブジェクト管理と相互作用する方法と、これがうまくいかないケースを理解します。http://python-camelot.s3.amazonaws.com/gpl/release/pyqt/doc/advanced/development.htmlを参照してください。
    • 親を持たない Qt オブジェクトは Python によって「所有」されます。それらを削除できるのは Python だけです。
    • 親を持つ Qt オブジェクトは Qt によって「所有」され、親が削除されると Qt によって削除されます。
    • 例: PyQt4 でのコア ダンプ
  • QObject は通常、その親またはその先祖への参照を持つべきではありません (弱い参照は問題ありません)。これにより、せいぜいメモリ リークが発生し、時折クラッシュすることもあります。
  • Qt がオブジェクトを自動削除する状況に注意してください。C++ オブジェクトが削除されたことが Python ラッパーに通知されていない場合、それにアクセスするとクラッシュが発生します。これは、PyQt と PySide が Qt オブジェクトを追跡するのが難しいため、さまざまな方法で発生する可能性があります。

    • QScrollArea とそのスクロール バー、QSpinBox とその QLineEdit などの複合ウィジェット (Pyside にはこの問題はありません)
    • QObject を削除すると、そのすべての子が自動的に削除されます(ただし、PyQt は通常これを正しく処理します)。
    • QTreeWidget からアイテムを削除すると、関連するウィジェット (QTreeWidget.setItemWidget で設定) が削除されます。

      # Example:
      from PyQt4 import QtGui, QtCore
      app = QtGui.QApplication([])
      
      # Create a QScrollArea, get a reference to one of its scroll bars.
      w = QtGui.QWidget()
      sa = QtGui.QScrollArea(w)
      sb = sa.horizontalScrollBar()
      
      # Later on, we delete the top-level widget because it was removed from the 
      # GUI and is no longer needed
      del w
      
      # At this point, Qt has automatically deleted all three widgets.
      # PyQt knows that the QScrollArea is gone and will raise an exception if
      # you try to access it:
      sa.parent()
      Traceback (most recent call last):
        File "<stdin>", line 1, in <module>
      RuntimeError: underlying C/C++ object has been deleted
      
      # However, PyQt does not know that the scroll bar has also been deleted.
      # Since any attempt to access the deleted object will probably cause a 
      # crash, this object is 'toxic'; remove all references to it to avoid 
      # any accidents
      sb.parent()
      # Segmentation fault (core dumped)
      

特定の回避策/バグ

  • 最初に prepareGeometryChange() を呼び出さずに QGraphicsItems の境界を変更すると、クラッシュする可能性があります。
  • QGraphicsItem.paint() 内で例外を発生させると、クラッシュが発生する可能性があります。例外がキャッチされずに処理されるのではなく、常に paint() 内で例外をキャッチし、メッセージを表示します。
  • QGraphicsItems は、それらが存在する QGraphicsView への参照を保持するべきではありません (weakrefs は問題ありません)。
  • QTimer.singleShot を繰り返し使用すると、ロックアップが発生する可能性があります。
  • QGLWidget で QGraphicsView を使用しないでください。

終了クラッシュを回避するためのプラクティス

  • QGraphicsScene の一部ではない QGraphicsItems は、終了時にクラッシュを引き起こす可能性があります。
  • 親または祖先を参照する QObject は、終了クラッシュを引き起こす可能性があります。
  • 親のない QGraphicsScene は、終了クラッシュを引き起こす可能性があります。
  • exit クラッシュを回避する最も簡単な方法は、Python が Qt オブジェクトの収集を開始する前に os._exit() を呼び出すことです。ただし、プログラムの一部が正しく機能するために適切な終了処理に依存している可能性があるため (たとえば、ログ ファイルを終了したり、デバイス ハンドルを適切に閉じたりするなど)、これは危険な場合があります。少なくとも、os._exit() を呼び出す前に atexit コールバックを手動で呼び出す必要があります。
于 2012-08-14T02:56:36.447 に答える
4

ポイントに追加するだけです:

qt ベースのプログラムでスレッドを使用する必要がある場合は、自動ガベージ コレクターを無効にして、メイン スレッドで手動コレクションを実行する必要があります ( http://pydev.blogspot.com.br/2014/03/shouldで説明されているように)。 python-garbage-collector-be.html ) -- オブジェクトにサイクルがないことを確認した場合でも、そうする必要があることに注意してください (サイクルを使用すると、Python の循環ガベージ コレクターが衝突するまで、基本的にオブジェクトを有効にできますが、例外として何かがある場合、フレームが生き続けている可能性があるため、そのような状況では、オブジェクトが予想よりも長く生き続けている可能性があります)...そのような場合、ガベージコレクターがぶつかる可能性がありますセカンダリ スレッドでは、qt がセグメンテーション違反を起こす可能性があります (qt ウィジェットは常にメイン スレッドで収集する必要があります)。

于 2014-03-15T11:22:18.180 に答える
4

参考までに、Luke の回答に対するPyQt の作成者からのコメントを投稿します。

誰かがこの投稿に飛び込んで、これらすべての(存在しない)「問題」に当惑したままになる可能性があるため、これは重要だと思います.

于 2015-03-20T16:47:53.533 に答える
0

シンクロUIコントロールではなくシグナルを出すことが、論理回路シミュレーターの実装時に問題を回避するための鍵でした

于 2021-03-01T07:41:50.027 に答える