0x00、前提说明:
接手一个Qt项目,领导说需要调用Python算法。(不要吐槽这个方案,领导最大)
找了CSDN里面很多博客笔记,试验了很多都卡在了C++和Python之间互相传参上面。
无奈只能一边看python源码,一边调试摸索。
发个帖子记录一下
0x01、把大象放入冰箱一共分几步
计划:
1、首先嵌入一个Python解释,这个很好解决,CSDN自行搜索。
2、假设C++给Python方法传1000个结构体参数。
3、Python返还给C++1000个结构体参数。
0x02、Qt项目嵌入Python解释器
1、工程目录文件夹
具体细节看下面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();
}
结果显示
此项目只是一个Demo代码,重点在于能拆能传能解。演示完毕。
0x04、闲情逸致,胡说八道
这个Python解释器嵌入这个问题是问题百出,我尝试过如下几个方法:
1、定义一个结构体,然后通过序列化转成char *的形式传给Python。Python能解,再使用struct.pack打包一个结构体传给C++后,C++解析乱码。
2、通过分析Python源码得知几乎所有的Python对象包括函数都是PyTypeObject
类型,我尝试构造过解析函数让Python调用,报错。
3、同样是分析源码得知,传入参数会检查类型是否是元组,由此可知传入传出只能是元组格式。使用PyTuple_New
叠加PyTuple_New
方式,失败了好多次,终于成功了。
帖子有用请点赞。让我也有动力放出更多的帖子来。