Search code examples
pythonqtcpythonpython-c-api

call a python function


I need to call python from qt c++ (qmake) and share data between them. So I wrote this code but the error is logical, I mean no error but it stops unexpectedly.

I tried this with python2.7 too but the same error. I want to pass a to someFunction function and return it back to the main.cpp

code:

someFunction.py: (I put this in "Otherfile" section of the project)

def someFunction(a):
    return a

main.cpp:

#include <Python.h>
#undef B0
#include "mainwindow.h"
#include <QApplication>
#include <QDebug>

int main(int argc, char *argv[])
{
        Py_Initialize();

        PyRun_SimpleString("import sys");
        PyRun_SimpleString("sys.path.append(\".\")");

        PyObject* myModuleString = PyUnicode_FromString((char*)"arbName");
        PyObject* myModule = PyImport_Import(myModuleString);
        PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"someFunction");

        PyObject* args = Py_BuildValue("(d)", 2.0);
        PyObject *myResult = PyObject_CallObject(myFunction, args);
        qDebug() << "Hello World";

        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
}

qmake:

QT       += core gui
INCLUDEPATH = /usr/include/python3.8

LIBS += -lpython3.8

CLANG_INSTALL_DIR += usr/lib/clang/10/lib/linux

greaterThan(QT_MAJOR_VERSION, 4): QT += widgets

CONFIG += c++11
CONFIG += no_keywords


SOURCES += \
    main.cpp \
    mainwindow.cpp

HEADERS += \
    mainwindow.h

FORMS += \
    mainwindow.ui

# Default rules for deployment.
qnx: target.path = /tmp/$${TARGET}/bin
else: unix:!android: target.path = /opt/$${TARGET}/bin
!isEmpty(target.path): INSTALLS += target

DISTFILES += \
    someFunction.py


Solution

  • You have 2 problems:

    • You use someFunction but the module is called arbName.

    • The "." It indicates the directory where the application is executed, in the case that you use QtCreator it is the build directory and not the source code folder. On the other hand it is better to avoid using relative paths but rather to construct absolute paths. For this you must use QCoreApplication::applicationDirPath(). In addition to copying the file and to do it automatically then you can use this answer.

    QT       += core gui
    
    greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
    
    CONFIG += c++11
    
    CONFIG += no_keywords
    
    INCLUDEPATH = /usr/include/python3.8
    LIBS += -lpython3.8
    
    CONFIG += file_copies
    
    COPIES += python
    python.files = someFunction.py
    python.path = $$OUT_PWD
    
    
    SOURCES += \
        main.cpp \
        mainwindow.cpp
    
    HEADERS += \
        mainwindow.h
    
    FORMS += \
        mainwindow.ui
    
    DISTFILES += \
        someFunction.py
    

    main.cpp

    #include "mainwindow.h"
    
    #include <Python.h>
    #include <QDebug>
    #include <QApplication>
    
    int main(int argc, char *argv[])
    {
        Py_Initialize();
    
        QString sys_path_append_command = QString(R"(sys.path.append("%1"))").arg(QCoreApplication::applicationDirPath());
    
        PyRun_SimpleString("import sys");
        PyRun_SimpleString(sys_path_append_command.toStdString().c_str());
    
        PyObject* myModuleString = PyUnicode_FromString((char*)"someFunction");
        PyObject* myModule = PyImport_Import(myModuleString);
        PyObject* myFunction = PyObject_GetAttrString(myModule,(char*)"someFunction");
    
        PyObject* args = Py_BuildValue("(f)", 2.0);
        PyObject *myResult = PyObject_CallObject(myFunction, args);
    
        qDebug() << "Hello World" << PyFloat_AsDouble(myResult);
    
        QApplication a(argc, argv);
        MainWindow w;
        w.show();
        return a.exec();
    }