tutorial riverbankcomputing descargar python pyqt pyqt4

python - riverbankcomputing - ¿Cuáles son las buenas prácticas para evitar bloqueos/cuelgues en PyQt?



qt designer (3)

Me encantan tanto Python como Qt, pero es bastante obvio para mí que Qt no se diseñó pensando en Python. Existen numerosas formas de bloquear una aplicación PyQt / PySide, muchas de las cuales son extraordinariamente difíciles de depurar, incluso con las herramientas adecuadas.

Me gustaría saber: ¿cuáles son las buenas prácticas para evitar bloqueos y bloqueos al usar PyQt y PySide? Estos pueden ser desde consejos de programación generales y módulos de soporte hasta soluciones muy específicas y errores que se deben evitar.


Prácticas generales de programación

  • Si debe utilizar un código de subprocesos múltiples, nunca acceda a la GUI desde un hilo que no sea GUI. En su lugar, siempre envíe un mensaje al subproceso de la GUI emitiendo una señal o algún otro mecanismo seguro para subprocesos.
  • Tenga cuidado con Model / View anything. TableView, TreeView, etc. Son difíciles de programar correctamente, y cualquier error conduce a bloqueos imposibles de rastrear. Use la prueba de modelo para ayudar a garantizar que su modelo sea internamente consistente.
  • Comprender la forma en que la gestión de objetos de Qt interactúa con la gestión de objetos de Python y los casos en que esto puede ir mal. Ver http://python-camelot.s3.amazonaws.com/gpl/release/pyqt/doc/advanced/development.html
    • Los objetos Qt sin padre son "propiedad" de Python; solo Python puede eliminarlos.
    • Los objetos Qt con un padre son "propiedad" de Qt y Qt los eliminará si se eliminan sus padres.
    • Ejemplo: volcado de núcleo con PyQt4
  • Un QObject generalmente no debería tener una referencia a su padre o alguno de sus antepasados ​​(las referencias débiles están bien). Esto causará fugas de memoria en el mejor de los casos y también fallas ocasionales.
  • Tenga en cuenta las situaciones en las que Qt elimina automáticamente objetos. Si no se ha informado al contenedor de Python que se eliminó el objeto C ++, acceder a él provocará un bloqueo. Esto puede suceder de muchas maneras diferentes debido a la dificultad que tienen PyQt y PySide en el seguimiento de objetos Qt.

    • Widgets compuestos como QScrollArea y sus barras de desplazamiento, QSpinBox y su QLineEdit, etc. (Pyside no tiene este problema)
    • Al eliminar un QObject, se eliminarán automáticamente todos sus elementos secundarios (sin embargo, PyQt normalmente lo maneja correctamente).
    • La eliminación de elementos de QTreeWidget hará que se eliminen los widgets asociados (establecidos con 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)

Soluciones específicas / Bugs

  • Cambiar los límites de QGraphicsItems sin llamar a prepareGeometryChange () primero puede causar bloqueo.
  • La generación de excepciones dentro de QGraphicsItem.paint () puede provocar bloqueos. Siempre capture las excepciones dentro de paint () y muestre un mensaje en lugar de dejar que la excepción proceda sin ser detectada.
  • QGraphicsItems nunca debe mantener una referencia al QGraphicsView en el que viven (los archivos weakrefs son correctos).
  • Usar QTimer.singleShot repetidamente puede causar bloqueos.
  • Evite usar QGraphicsView con QGLWidget.

Prácticas para evitar bloqueos de salida

  • QGraphicsItems que no forman parte de un QGraphicsScene pueden provocar un bloqueo en la salida.
  • QObjects que hacen referencia a su padre o cualquier antecesor puede causar un bloqueo de salida.
  • QGraphicsScene sin padre puede provocar un bloqueo de salida.
  • La forma más fácil de evitar bloqueos de salida es llamar a os._exit () antes de que Python comience a recopilar objetos Qt. Sin embargo, esto puede ser peligroso porque alguna parte del programa puede depender del manejo correcto de la salida para funcionar correctamente (por ejemplo, terminar archivos de registro o cerrar correctamente los identificadores de dispositivos). Como mínimo, uno debe invocar manualmente las devoluciones de llamada atexit antes de llamar a os._exit ().

Solo añadiendo al punto:

Si debe usar subprocesos en su programa basado en qt, realmente debe deshabilitar el colector de basura automático y hacer recolecciones manuales en el hilo principal (como se describe en http://pydev.blogspot.com.br/2014/03/should-python-garbage-collector-be.html ) - tenga en cuenta que debe hacer eso incluso si se asegura de que sus objetos no tengan ciclos (con un ciclo básicamente hace que sus objetos vivan hasta que el recolector de basura cíclico python se tropiece, pero a veces, si tienes algo como una excepción, es posible que un cuadro se mantenga con vida, por lo tanto, en tal situación tu objeto puede mantenerse vivo más de lo que anticipas) ... en esos casos, es posible que el recolector de basura se encuentre con en un subproceso secundario, lo que puede hacer que qt segfault (los widgets qt siempre deben recopilarse en el subproceso principal).


Solo como referencia, publico el comentario del autor de PyQt sobre la respuesta de Luke: "Es basura".

Creo que es importante porque alguien puede saltar a este post y quedó perplejo con todos estos (inexistentes) "problemas".