0x00、前提说明:

接手一个Qt项目,领导说需要调用Python算法。(不要吐槽这个方案,领导最大)
找了CSDN里面很多博客笔记,试验了很多都卡在了C++和Python之间互相传参上面。
无奈只能一边看python源码,一边调试摸索。
发个帖子记录一下

0x01、把大象放入冰箱一共分几步

计划:

1、首先嵌入一个Python解释,这个很好解决,CSDN自行搜索。
2、假设C++给Python方法传1000个结构体参数。
3、Python返还给C++1000个结构体参数。

0x02、Qt项目嵌入Python解释器

1、工程目录文件夹

qt qprogress调用py qt调用python函数_qt


具体细节看下面Pro文件的描述。

2、首先提供一下Pro文件的写法,此处我是把Python文件夹直接拷贝进工程目录。

说明:
1、unix 是在Linux下的配置
2、win32 是在windows下的配置

QT -= gui

CONFIG += c++11 console
CONFIG -= app_bundle

# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000    # disables all the APIs deprecated before Qt 6.0.0

DESTDIR = $$PWD/../bin

SOURCES += \
        main.cpp

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




unix: LIBS += -L$$PWD/../Python/python3/python3.10/config-3.10-x86_64-linux-gnu/ -lpython3.10
unix: INCLUDEPATH += $$PWD/../Python/python3/python3.10/include
unix: DEPENDPATH += $$PWD/../Python/python3/python3.10/include
unix: PRE_TARGETDEPS += $$PWD/../Python/python3/python3.10/config-3.10-x86_64-linux-gnu/libpython3.10.a


win32: LIBS += -L$$PWD/../Python/python310/libs/ -lpython310
win32: INCLUDEPATH += $$PWD/../Python/python310/include
win32: DEPENDPATH += $$PWD/../Python/python310/include
win32:!win32-g++: PRE_TARGETDEPS += $$PWD/../Python/python310/libs/python310.lib
#else:win32-g++: PRE_TARGETDEPS += $$PWD/../Python/python310/libs/libpython310.a

DISTFILES += \
    ../bin/py_script/Py_Module.py

0x03、C++和Python互相传参代码

1、Python代码

def process_data(*args):
    result = []
    for arg in args:
        strVal, intVal, floatVal = arg
        # process the data
        processed_strVal = strVal.upper()
        processed_intVal = intVal + 1
        processed_floatVal = floatVal ** 2
        # append the result to the list
        result.append((processed_strVal, processed_intVal, processed_floatVal))
    return tuple(result)

2、C++代码

#define PY_SSIZE_T_CLEAN
#include <Python.h>

#include <QCoreApplication>
#include <QDebug>
#include <QRandomGenerator>

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

    // 初始化Python解释器
    Py_Initialize();

    if(!Py_IsInitialized())
        qDebug()<<"[db:] Py_Initialize fail";
    else
        qDebug()<<"[db:] Py_Initialize success";

    // 导入sys模块设置模块地址
    PyRun_SimpleString("import sys");
    PyRun_SimpleString("import struct");
    PyRun_SimpleString("import ctypes");
    PyRun_SimpleString("sys.path.append('./py_script/')");

    //创建模块指针
    PyObject *pModule = PyImport_ImportModule("Py_Module");
    if(!pModule)
        qDebug()<<"[db:] pModule fail";
    else
        qDebug()<<"[db:] pModule success";

    //创建函数指针
    PyObject* pFunc= PyObject_GetAttrString(pModule,"process_data");
    if(!pFunc || !PyCallable_Check(pFunc))
        qDebug()<<"[db:] pFunc fail";
    else
        qDebug()<<"[db:] pFunc success";

	// 定义一个随机器
    QRandomGenerator generator;
    // 创建一个定长元组,用来存放传入参数
    PyObject* pyArgs = PyTuple_New(1000);
    // 填充元组 
    for (int i = 0; i < 1000; ++i) {
        PyObject* pyTuple = PyTuple_New(3);
        PyTuple_SetItem(pyTuple, 0, Py_BuildValue("s", "test string"));
        PyTuple_SetItem(pyTuple, 1, Py_BuildValue("i", generator.generate() % 100));
        PyTuple_SetItem(pyTuple, 2, Py_BuildValue("f", 3.14f));
        PyTuple_SetItem(pyArgs, i, pyTuple);
     }

     // 调用python函数
     PyObject* pyResult = PyObject_CallObject(pFunc, pyArgs);
     // 判单是否成功
     if (pyResult == NULL) {        PyErr_Print();    }
     else {
         // 解析返回值
         for (int i = 0; i < 1000; ++i) {
             PyObject* pyTuple = PyTuple_GetItem(pyResult, i);
             QString strVal = QString::fromUtf8(PyUnicode_AsUTF8(PyTuple_GetItem(pyTuple, 0)));
             int intVal = PyLong_AsLong(PyTuple_GetItem(pyTuple, 1));
             double floatVal = PyFloat_AsDouble(PyTuple_GetItem(pyTuple, 2));
             qDebug() << strVal << intVal << floatVal;	// 打印
         }
      }
      // 清理Python变量
      Py_DECREF(pyArgs);
      Py_DECREF(pFunc);
      Py_DECREF(pModule);
      Py_DECREF(pyResult);

      // 清理Python解释器
      Py_Finalize();

      return a.exec();
}

结果显示

qt qprogress调用py qt调用python函数_qt_02

此项目只是一个Demo代码,重点在于能拆能传能解。演示完毕。

0x04、闲情逸致,胡说八道

这个Python解释器嵌入这个问题是问题百出,我尝试过如下几个方法:

1、定义一个结构体,然后通过序列化转成char *的形式传给Python。Python能解,再使用struct.pack打包一个结构体传给C++后,C++解析乱码。
2、通过分析Python源码得知几乎所有的Python对象包括函数都是PyTypeObject类型,我尝试构造过解析函数让Python调用,报错。
3、同样是分析源码得知,传入参数会检查类型是否是元组,由此可知传入传出只能是元组格式。使用PyTuple_New叠加PyTuple_New方式,失败了好多次,终于成功了。

帖子有用请点赞。让我也有动力放出更多的帖子来。