Почему PyQt иногда вылетает при выходе? - PullRequest
3 голосов
/ 01 декабря 2019

В приведенном ниже коде отображается QMainWindow с 4 QGraphicsView для рисования с мышью в нем. Он работает как задумано, но при его закрытии в консоли появляется следующее сообщение об ошибке:

Process finished with exit code 139 (interrupted by signal 11: SIGSEGV)

Что не так в коде?


main.py

import sys

from PyQt5.QtWidgets import QApplication, QMainWindow, QGraphicsView, QGraphicsScene, QGraphicsPathItem
from PyQt5.QtGui import QPainterPath, QPen
from PyQt5.QtCore import Qt
from PyQt5.uic import loadUi

# Based on code from https://stackoverflow.com/a/44248794/7481773

class MainWindow(QMainWindow):
    def __init__(self):
        super().__init__()
        loadUi("mainwindow.ui", self)

        self.verticalLayout_top_left.addWidget(GraphicsView())
        self.verticalLayout_top_right.addWidget(GraphicsView())
        self.verticalLayout_bottom_left.addWidget(GraphicsView())
        self.verticalLayout_bottom_right.addWidget(GraphicsView())


class GraphicsView(QGraphicsView):
    def __init__(self):
        super().__init__()
        self.start = None
        self.end = None

        self.setScene(QGraphicsScene())
        self.path = QPainterPath()
        self.item = GraphicsPathItem()
        self.scene().addItem(self.item)

        self.contents_rect = self.contentsRect()
        self.setSceneRect(0, 0, self.contents_rect.width(), self.contents_rect.height())
        self.setHorizontalScrollBarPolicy(Qt.ScrollBarAlwaysOff)
        self.setVerticalScrollBarPolicy(Qt.ScrollBarAlwaysOff)

    def mousePressEvent(self, event):
        self.start = self.mapToScene(event.pos())
        self.path.moveTo(self.start)

    def mouseMoveEvent(self, event):
        self.end = self.mapToScene(event.pos())
        self.path.lineTo(self.end)
        self.start = self.end
        self.item.setPath(self.path)


class GraphicsPathItem(QGraphicsPathItem):
    def __init__(self):
        super().__init__()
        pen = QPen()
        pen.setColor(Qt.black)
        pen.setWidth(5)
        self.setPen(pen)


def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    app.exec_()


if __name__ == "__main__":
    main()

mainwindow.ui

<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>MainWindow</class>
 <widget class="QMainWindow" name="MainWindow">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>800</width>
    <height>600</height>
   </rect>
  </property>
  <property name="windowTitle">
   <string>MainWindow</string>
  </property>
  <widget class="QWidget" name="centralwidget">
   <layout class="QGridLayout" name="gridLayout">
    <item row="1" column="0">
     <widget class="QPushButton" name="pushButton_left">
      <property name="text">
       <string>PushButton</string>
      </property>
     </widget>
    </item>
    <item row="0" column="0">
     <layout class="QVBoxLayout" name="verticalLayout_top_left"/>
    </item>
    <item row="0" column="1">
     <layout class="QVBoxLayout" name="verticalLayout_top_right"/>
    </item>
    <item row="1" column="1">
     <widget class="QPushButton" name="pushButton_right">
      <property name="text">
       <string>PushButton</string>
      </property>
     </widget>
    </item>
    <item row="2" column="0">
     <layout class="QVBoxLayout" name="verticalLayout_bottom_left"/>
    </item>
    <item row="2" column="1">
     <layout class="QVBoxLayout" name="verticalLayout_bottom_right"/>
    </item>
   </layout>
  </widget>
  <widget class="QMenuBar" name="menubar">
   <property name="geometry">
    <rect>
     <x>0</x>
     <y>0</y>
     <width>800</width>
     <height>24</height>
    </rect>
   </property>
  </widget>
  <widget class="QStatusBar" name="statusbar"/>
 </widget>
 <resources/>
 <connections/>
</ui>

Ответы [ 2 ]

3 голосов
/ 01 декабря 2019

Это вызвано давней проблемой, которая должна быть исправлена ​​в следующем выпуске (возможно, PyQt-5.14). Если вы используете PyQt-5.13.1, вы можете протестировать исправление с помощью следующего временного API:

from PyQt5.QtCore import pyqt5_enable_new_onexit_scheme

pyqt5_enable_new_onexit_scheme(True)

Если проблем с этим нет, то в конечном итоге это станет поведением по умолчанию (поэтомуне будет необходимости включать его явно). Основная проблема, которая вызывает проблему, задокументирована здесь:

По сути, схема сбора мусора в Python удаляетобъекты в непредсказуемом порядке, и это иногда может привести к тому, что Qt попытается удалить объекты, которые больше не существуют (что приведет к сбою). Таким образом, пример в вопросе также может быть исправлен следующим образом:

def main():
    app = QApplication(sys.argv)
    main_window = MainWindow()
    main_window.show()
    app.exec_()
    # ensure correct deletion order
    del main_window, app

Предстоящие изменения, отмеченные выше, будут означать, что такого рода код очистки больше не понадобится.

2 голосов
/ 01 декабря 2019

Проблема вызвана тем, что даже когда вы завершаете запуск app.exec_ (), приложение по-прежнему освобождает ресурсы, которым требуется доступ к экземпляру QApplication, но, как и в вашем случае, «app» удаляется перед этим, выполняя внутренние функцииQt Доступ к незарезервированной памяти.

Учитывая вышеизложенное, возможное решение состоит в том, чтобы расширить область действия "приложения", чтобы оно не удалялось после выполнения основной функции путем создания глобальной переменной.

# ...
<b>app = None</b>


def main():
    <b>global app</b>
    app = QApplication(sys.argv)
    # ...
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...