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

Luk*_*uke 34 python pyqt pyqt4

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

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

Luk*_*uke 51

一般编程实践

  • 如果必须使用多线程代码,永远不要从非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自动删除对象的情况.如果没有通知python包装器已删除C++对象,那么访问它将导致崩溃.由于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)
      
      Run Code Online (Sandbox Code Playgroud)

具体的解决方法/错误

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

避免退出崩溃的做法

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

  • 您是正确的,这些问题中的许多是错误的程序设计而不是pyqt / pyside错误(我无意暗示)的结果。但是,我放入此列表中的每个问题都至少来自我在自己的应用程序中遇到的一个实际错误。他们中的许多人花费了数小时的调试才能解决,因此需要像这样的综合清单。我给出的示例是虚构的,是的,但这只是因为我对其进行了简化。不难想象会导致崩溃的实际情况。 (2认同)
  • 该示例并非旨在涉及对象所有权-删除Qt对象有很多合理的理由。关键是PyQt和PySide在跟踪其Qt对象方面并不完美。如果出现问题,将导致崩溃。更大的一点是,如果我们了解导致这些崩溃的原因,则可以通过更仔细的编程实践完全避免它们。 (2认同)