I am trying to create a C++/Qt5 program that uses an embedded Python3 interpreter for scripting and customization purposes.
This is my approach: I create a QApplication instance in C++, create a QMainwindow instance, and add a QVBoxLayout and a QPushbutton.
I then initialize the Python interpreter, import a python module containing some very basic PyQt code for adding a second QPushButton to the QVBoxLayout, and execute that code.
Then, back in C++, I call 'show()' on the QMainWindow instance and execute the QApplication.
After QApplication::exec() returnenter code here
s, I try to clean up references to PyObjects and try to finalize the Python interpreter.
The call of Py_Finalize()
causes a segmentation fault.
Here is the code:
#include <QApplication>
#include <QWidget>
#include <QVBoxLayout>
#include <QPushButton>
#include <QMainWindow>
#pragma push_macro("slots")
#undef slots
#include "Python.h"
#pragma pop_macro("slots")
int main(int argc, char *argv[])
{
PyObject *pName, *pModule, *pDict, *pFunc;
// Initialize QApplication and setup basic widgets.
QApplication app (argc, argv);
QMainWindow mainWindow (nullptr);
QPushButton button ("test", &mainWindow);
QWidget * widget = new QWidget(&mainWindow);
mainWindow.setCentralWidget(widget);
auto l = new QVBoxLayout ();
widget->setLayout(l);
l->addWidget(&button);
// Initialize the Python Interpreter
Py_Initialize();
// Build the name object
pName = PyUnicode_FromString("runPyQt");
// Load the module object
pModule = PyImport_Import(pName);
if (!pModule){
Py_Finalize();
return 1;
}
pDict = PyModule_GetDict(pModule);
pFunc = PyDict_GetItemString(pDict, "main");
if (PyCallable_Check(pFunc))
{
PyObject_CallObject(pFunc, nullptr);
} else
{
PyErr_Print();
}
mainWindow.show();
int r = 0;
r = QApplication::exec();
// Clean up
Py_DECREF(pModule);
Py_DECREF(pName);
// Finish the Python Interpreter
Py_Finalize();
return r;
}
And the Python module:
import sys
if sys.platform.startswith( 'linux' ) :
from OpenGL import GL
from PyQt5.QtWidgets import QApplication, QPushButton, QMainWindow
def main():
app = QApplication.instance()
widgets = app.topLevelWidgets()
for widget in widgets:
if type(widget) is QMainWindow:
break
widget = widget.centralWidget()
widget.layout().addWidget(QPushButton('Hello from PyQt'))
I assumed that it had something to do with not holding the GIL when finalizing the Interpreter. I tried calling PyEval_AcquireLock()
right before finalizing, which causes the program to hang at that point.
Maybe PyQt somewhere acquires the lock and doesn't release it before terminating, but that is just a guess.
Up until the call of Py_Finalize()
the program works exactly as I expect. Even using PyQt to add some non-trivial widgets works fine.
My question is: How do I correctly finalize the Python interpreter in this situation?