Подтвердить что ты не робот

Каковы хорошие методы предотвращения сбоев/зависаний в PyQt?

Мне нравятся как python, так и Qt, но для меня довольно очевидно, что Qt не был разработан с учетом python. Существует множество способов свернуть приложение PyQt/PySide, многие из которых чрезвычайно трудно отлаживать, даже с помощью соответствующих инструментов.

Я хотел бы знать: каковы хорошие методы предотвращения сбоев и блокировок при использовании PyQt и PySide? Это может быть что угодно: от общих советов по программированию и модулей поддержки до высокоспециализированных обходных решений и ошибок, которых следует избегать.

4b9b3361

Ответ 1

Общие правила программирования

  • Если вы должны использовать многопоточный код, никогда не обращайтесь к графическому интерфейсу из потока, отличного от GUI. Всегда вместо этого отправляйте сообщение в поток GUI, передавая сигнал или какой-либо другой механизм, защищенный потоками.
  • Будьте осторожны с Model/View. 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 не была проинформирована о том, что объект С++ был удален, доступ к нему приведет к сбою. Это может произойти разными способами из-за сложности 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)
      

Конкретные обходные пути/ошибки

  • Изменение границ QGraphicsItems без вызова prepareGeometryChange() сначала может привести к сбою.
  • Исключение исключений внутри QGraphicsItem.paint() может привести к сбоям. Всегда перехватывайте исключения внутри paint() и выводите сообщение, а не исключаете исключение.
  • QGraphicsItems никогда не должны ссылаться на QGraphicsView, в котором они живут. (weakrefs в порядке).
  • Повторное использование QTimer.singleShot может вызвать блокировки.
  • Избегайте использования QGraphicsView с QGLWidget.

Практика предотвращения сбоев при выходе

  • QGraphicsItems, которые не являются частью QGraphicsScene, могут привести к сбою при выходе.
  • QObjects, ссылающиеся на своего родителя или любого предка, могут вызвать сбой выхода.
  • QGraphicsScene без родителя может вызвать сбой выхода.
  • Самый простой способ избежать сбоев выхода - вызвать os._exit(), прежде чем python начнет собирать объекты Qt. Однако это может быть опасно, потому что какая-то часть программы может полагаться на правильную обработку выхода для правильной работы (например, завершение файлов журнала или правильное закрытие дескрипторов устройств). Как минимум, нужно вызвать вызовы atexit перед вызовом os._exit().

Ответ 2

Просто добавив к точке:

Если вы должны использовать потоки в своей qt-программе, вам действительно нужно отключить автоматический сборщик мусора и сделать ручные коллекции в основном потоке (как описано в http://pydev.blogspot.com.br/2014/03/should-python-garbage-collector-be.html) - обратите внимание, что вы должны это сделать, даже если вы убедитесь, что у ваших объектов нет циклов (с циклом вы в основном делаете свои объекты живыми до тех пор, пока не столкнется циклический сборщик мусора python, но иногда, если у вас есть что-то в качестве исключения, возможно, что кадр сохранен в живых, поэтому в такой ситуации ваш объект может оставаться в живых дольше, чем вы ожидаете)... в этих случаях возможно, что сборщик мусора попадает в вторичный поток, который может вызвать qt для segfault (qt-виджеты должны всегда собираться в основном потоке).

Ответ 3

Только для справки я отправляю комментарий от автора PyQt на ответ Люка: "Это мусор".

Я думаю, что это важно, потому что кто-то может прыгнуть в этот пост и остался озадаченным всеми этими (несуществующими) "проблемами".