当QApplication退出时,QT引发分段错误(核心转储)

时间:2015-04-11 13:21:36

标签: python c++ c qt segmentation-fault

描述

我正在使用Python& QT做一些实验。
我使用Python通过调用QT的函数来创建GUI应用程序 但是,当我关闭一个简单的GUI应用程序时,它引发了一个分段错误(核心转储)

示例代码

C ++:

// This is python C++ extension
#ifndef QT_API
#define QT_API

#include "Python.h"
#include <QtWidgets/QApplication>
#include <QtWidgets/QPushButton>

#endif

static QSharedPointer<QApplication> pglobal_app;

// api: init
static PyObject* init_wrapper(PyObject* self, PyObject* args)
{
    int argc = 1;
    char *argv[] = {""};
    pglobal_app = QSharedPointer<QApplication>(new QApplication(argc, argv));
    Py_RETURN_NONE;
}

// api: launch
static PyObject* launch_wrapper(PyObject* self, PyObject* args)
{
    QPushButton button("Exit");
    QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
    button.show();
    pglobal_app->exec();
    Py_RETURN_NONE;
}

// methods table
static PyMethodDef qt_api_methods[] = {
    { "init", init_wrapper, METH_VARARGS, "init qt_api"},
    { "launch", launch_wrapper, METH_VARARGS, "launch qt_api"},
    { NULL, NULL, 0, NULL }
};

// init qt_api module
PyMODINIT_FUNC initqt_api(void)
{
    (void) Py_InitModule("qt_api", qt_api_methods);
}

的Python:

#!/usr/bin/env python
import sys, os
sys.path.append("build/lib.linux-x86_64-2.7")
import qt_api

qt_api.init()
qt_api.launch()
print "BYE"

症状

当我点击“退出”按钮时,程序会打印BYE然后引发分段错误。

GDB一瞥

我使用GDB来检查发生了什么。以下是输出。

#0  0x00007fd9872ed869 in QWindow::destroy() () from /usr/lib/x86_64-linux-gnu/libQt5Gui.so.5
#1  0x00007fd988b8e1ad in QWidgetPrivate::deleteTLSysExtra() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#2  0x00007fd988b6278d in QWidgetPrivate::deleteExtra() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#3  0x00007fd988b68cf8 in QWidgetPrivate::~QWidgetPrivate() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#4  0x00007fd988b68f39 in QWidgetPrivate::~QWidgetPrivate() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#5  0x00007fd9894be666 in QObject::~QObject() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#6  0x00007fd988b71c7a in QWidget::~QWidget() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#7  0x00007fd988ea5e0e in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#8  0x00007fd988b8d48d in ?? () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#9  0x00007fd9894be666 in QObject::~QObject() () from /usr/lib/x86_64-linux-gnu/libQt5Core.so.5
#10 0x00007fd988b71c7a in QWidget::~QWidget() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#11 0x00007fd988b8c299 in QDesktopWidget::~QDesktopWidget() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#12 0x00007fd988b38a5c in QApplication::~QApplication() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#13 0x00007fd988b38e69 in QApplication::~QApplication() () from /usr/lib/x86_64-linux-gnu/libQt5Widgets.so.5
#14 0x00007fd9898cf77e in destroy (this=0x233d220)
    at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:151
#15 deref (d=0x233d220) at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:469
#16 deref (this=<optimized out>) at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:464
#17 QSharedPointer<QApplication>::~QSharedPointer (this=<optimized out>, __in_chrg=<optimized out>)
    at /home/charles/tools/Qt5.4.0/5.4/gcc_64/include/QtCore/qsharedpointer_impl.h:305
#18 0x00007fd98ab15259 in __run_exit_handlers (status=0, listp=0x7fd98ae986c8 <__exit_funcs>, 
    run_list_atexit=run_list_atexit@entry=true) at exit.c:82
#19 0x00007fd98ab152a5 in __GI_exit (status=<optimized out>) at exit.c:104

问题

  1. 为什么会发生这种情况以及如何避免这个问题?
  2. 如果我想在另一个线程中从Python调用QApplication::quit(),我该怎么办?
  3. 非常感谢你。

1 个答案:

答案 0 :(得分:2)

让我们看看你的launch_wrapper

// api: launch
static PyObject* launch_wrapper(PyObject* self, PyObject* args)
{
    QPushButton button("Exit");
    QObject::connect(&button, &QPushButton::clicked, &QApplication::quit);
    button.show();
    pglobal_app->exec();
    Py_RETURN_NONE;
}

在这里创建一个QPushButton,连接它,显示它,然后输入主要的Qt事件循环。 但是你在堆栈上创建它,所以当你单击该按钮时,主事件循环返回,你的launch_wrapper返回,并且QPushButton在出路时被销毁。

现在,您的pglobal_app是静态的,因此在所有使用过的代码返回后,只有在QPushButton消失后很长时间内,它才会被销毁。

但内部Qt几乎为所有对象保留了父母和孩子的层次结构,父母拥有孩子。因此,当QApplication被销毁时,它也会摧毁其所有孩子,包括你的QPushButton

所以你有一个对象被摧毁了两次。这是一种双重自由和未定义的行为。因此崩溃。

要解决此问题,请在堆上创建所有GUI元素(即QPushButton* button = new QPushButton("Exit");,然后让Qt使用父/子层次结构清理它。