什么是避免PyQt崩溃/挂起的好方法?

时间:2012-08-14 02:56:36

标签: python pyqt pyqt4

我喜欢python和Qt,但对我来说很明显,Qt并不是用python设计的。有许多方法可以使PyQt / PySide应用程序崩溃,其中许多方法都非常难以调试,即使使用适当的工具也是如此。

我想知道:使用PyQt和PySide时,避免崩溃和锁定的好方法是什么?这些可以是从一般编程技巧和支持模块到高度特定的解决方法和要避免的错误。

4 个答案:

答案 0 :(得分:48)

一般编程实践

  • 如果必须使用多线程代码,永远不要从非GUI线程访问GUI。总是通过发出信号或其他一些线程安全机制向GUI线程发送消息。
  • 小心模型/查看任何内容。 TableView,TreeView等。它们很难正确编程,任何错误都会导致无法追踪的崩溃。使用Model Test来帮助确保您的模型在内部保持一致。
  • 了解Qt对象管理与Python对象管理交互的方式以及可能出错的情况。见http://python-camelot.s3.amazonaws.com/gpl/release/pyqt/doc/advanced/development.html
    • 没有父级的Qt对象由Python“拥有”;只有Python可以删除它们。
    • 父母的Qt对象由Qt“拥有”,如果父母被删除,将被Qt删除。
    • 示例:Core dump with PyQt4
  • QObject通常不应该引用其父级或其任何祖先(弱引用都可以)。这将导致内存泄漏最多,偶尔也会发生崩溃。
  • 请注意Qt自动删除对象的情况。如果没有通知python包装器已删除C ++对象,那么访问它将导致崩溃。由于PyQt和PySide在跟踪Qt对象方面存在困难,因此可能会以多种方式发生这种情况。

    • 复合小部件,例如QScrollArea及其滚动条,QSpinBox及其QLineEdit等(Pyside没有此问题)
    • 删除QObject将automatically delete all of its children(但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)
      

具体的解决方法/错误

  • 首先更改QGraphicsItems的边界而不先调用prepareGeometryChange()会导致崩溃。
  • 在QGraphicsItem.paint()中引发异常会导致崩溃。始终捕获paint()中的异常并显示消息,而不是让异常继续进行。
  • QGraphicsItems永远不应该保留对它们所在的QGraphicsView的引用。(weakrefs没问题。)
  • 反复使用QTimer.singleShot会导致锁定。
  • 避免将QGraphicsView与QGLWidget一起使用。

避免退出崩溃的做法

  • 不属于QGraphicsScene的QGraphicsItems可能会导致退出时崩溃。
  • 引用其父级或任何祖先的QObject可能导致退出崩溃。
  • 没有父母的QGraphicsScene会导致退出崩溃。
  • 避免退出崩溃的最简单方法是在python开始收集Qt对象之前调用os._exit()。但是,这可能很危险,因为程序的某些部分可能依赖于正确的退出处理来正常运行(例如,终止日志文件或正确关闭设备句柄)。在调用os._exit()之前,至少应该手动调用atexit回调。

答案 1 :(得分:4)

只是添加到这一点:

如果你必须在基于qt的程序中使用线程,你必须禁用自动垃圾收集器并在主线程上进行手动收集(如http://pydev.blogspot.com.br/2014/03/should-python-garbage-collector-be.html中所述) - 请注意你应该这样做如果你确定你的物体没有周期(有一个循环你基本上让你的物体存活直到蟒蛇循环垃圾收集器碰到,但有时如果你有一些例外,它可能是一个框架因此,在这种情况下,您的物体可能仍然比您预期的更长时间保持活着... ...在这种情况下,垃圾收集器可能会在次要线程中碰撞,这可能会导致qt to segfault(必须始终在主线程中收集qt小部件)。

答案 2 :(得分:4)

仅供参考,我将comment from the author of PyQt张贴在卢克的回答中:&#34;它是垃圾。&#34;

我认为这很重要,因为有人可以跳到这个帖子并且仍然对所有这些(不存在的)&#34;问题&#34;感到困惑。

答案 3 :(得分:0)

发射信号而不是同步UI控制是我在逻辑电路模拟器实现过程中避免出现问题的关键