使用Qt框架实现C++调用Python脚本的方法详解
Debug版本
QtCreator、Vs 配置python出现的问题可参考这两位位老哥的贴子
VS:
http://t.csdnimg.cn/BoIyXhttp://t.csdnimg.cn/BoIyX
QtCreator:
http://t.csdnimg.cn/rwqpthttp://t.csdnimg.cn/rwqpt
一看他们就是没少踩坑的,里面出现的所有bug我都遇到过了,很感谢他们!
首先针对debug版本的环境问题,如果你的电脑已经配置了anaconda或者pycharm环境或者环境变量,此时想在项目中安装单独的py版本,参照网上链接后,可能会出现环境变量始终无法改的问题,想要调试到debug,环境变量得设置为你安装的py版本位置,解决不了就只能卸载conda然后重新安装单独的py。
现在默认你的环境变量已经设置好,开始初始化:
#ifdef QT_DEBUG
Py_SetPythonHome(L"C:/Users/DELL/Desktop/Qt_Projects/CC_system/Template/env/pyforcpp");
Py_Initialize();
if (!Py_IsInitialized())
{
qDebug() << "无法初始化python解释器.";
return false;
}
QString pythonPathAppend = "sys.path.append('" + QDir::currentPath() + "/lib/py_code" + "')";
PyRun_SimpleString("import sys");
PyRun_SimpleString(pythonPathAppend.toLocal8Bit().data());
PyRun_SimpleString("sys.path.append('C:/Users/DELL/Desktop/Qt_Projects/CC_system/Template/env/pyforcpp/Lib/site-packages')");
#endif
Py_SetPythonHome(L"C:/Users/DELL/Desktop/Qt_Projects/CC_system/Template/env/pyforcpp");
Py_Initialize();
QString pythonPathAppend = "sys.path.append('" + QDir::currentPath() + "/lib/py_code" + "')";
PyRun_SimpleString("import sys");
PyRun_SimpleString(pythonPathAppend.toLocal8Bit().data());
PyRun_SimpleString("sys.path.append('C:/Users/DELL/Desktop/Qt_Projects/CC_system/Template/env/pyforcpp/Lib/site-packages')");
Release版本
和debug版本差不多,关键就在打包问题。Py_SetPythonHome设置你打包版本的环境,这个非常重要!!
#ifndef QT_DEBUG
Py_SetPythonHome((wchar_t*)(L"./python_env"));
Py_Initialize();
if (!Py_IsInitialized())
{
qDebug() << "py初始化失败!";
}
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append(\"./\")");//这一步很重要,修改Python路径
#endif
qt打包
首先不管是qt还是vs,找到release之后生成exe文件,先用windeployqt,把目标exe程序的qt库打包下来,假设目标文件夹为Template_Release;
python 脚本打包
建议使用pyinstaller(使用方法自己百度),会生成build和dist文件:
butter_c是我py脚本的名字,你只用含有找到很多.pyd和.dll目录即可,不同版本的pyinstaller生成的文件可能不同,将其下所有内容拷贝置Template_Release目录下(必须和qt生成目标exe在同一目录级),此时Template_Release目录下就有qt+py打包的文件;
python 环境打包
在Template_Release目录下创建文件python_env,将env/pyforcpp中的如下文件拷贝至python_env中,因为代码里设置了Py_SetPythonHome((wchar_t*)(L"./python_env"));
其他需要注意的
在Template_Release目录中还需要将你写好的所有py脚本放置进去
运行exe,如果还缺啥dll就往里面补啥,至此,Template_Release下的exe可以在未安装python环境的电脑运行。
完整代码
在c++中调用py脚本,自己创建了一个单例工厂,有需要就copy,将指针传到你的项目中即可
#pragma once
#include <Python.h>
#include <QDebug>
class Python_Interpreter {
public:
static Python_Interpreter& getInstance()
{
static Python_Interpreter instance;
return instance;
}
bool initialize()
{
if (!Py_IsInitialized())
{
//debug
#ifdef QT_DEBUG
Py_SetPythonHome(L"C:/Users/DELL/Desktop/Qt_Projects/CC_system/Template/env/pyforcpp");
Py_Initialize();
if (!Py_IsInitialized())
{
qDebug() << "无法初始化python解释器.";
return false;
}
QString pythonPathAppend = "sys.path.append('" + QDir::currentPath() + "/lib/py_code" + "')";
PyRun_SimpleString("import sys");
PyRun_SimpleString(pythonPathAppend.toLocal8Bit().data());
PyRun_SimpleString("sys.path.append('C:/Users/DELL/Desktop/Qt_Projects/CC_system/Template/env/pyforcpp/Lib/site-packages')");
#endif
#ifndef QT_DEBUG
// 设置 Python 环境Template
Py_SetPythonHome((wchar_t*)(L"./python_env"));
Py_Initialize();
if (!Py_IsInitialized())
{
qDebug() << "py初始化失败!";
}
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append(\"./\")");//这一步很重要,修改Python路径
#endif
}
return true;
}
void finalize()
{
if (Py_IsInitialized())
{
qDebug() << "无法正常终止python.";
Py_Finalize();
}
}
PyObject* importModule(const QString& moduleName)
{
return PyImport_ImportModule(moduleName.toStdString().c_str());
}
PyObject* getFunction(PyObject* module, const QString& functionName)
{
return PyObject_GetAttrString(module, functionName.toStdString().c_str());
}
//以下是自定义模块
// 将Python列表转换为QVector<double>的辅助函数
QVector<double> PyListToQVector(PyObject* pList);
// 将QVector<double>转换为Python列表的辅助函数
PyObject* PyList_FromVector(const QVector<double>& vector);
//巴特沃斯滤波器
QVector<double> butter(QVector<double>input, QString filter, int N, double col_fre, double Wn1=0.0, double Wn2 = 0.0);
//自相关,互相关的误码率计算
double getErrorRate(QVector<double>input1, QVector<double>input2, int L, int fos);
private:
Python_Interpreter() {}
Python_Interpreter(const Python_Interpreter&) = delete;
Python_Interpreter& operator=(const Python_Interpreter&) = delete;
};
程序中的自定义模块在对应cpp中实现即可,这边就展示一个py的有参调用
#include "Python_Interpreter.h"
double Python_Interpreter::getErrorRate(QVector<double>input1, QVector<double>input2, int L, int fos)
{
double error = -1.0;
if (!getInstance().initialize())
{
PyErr_Print();
return error;
}
PyObject* pModule = getInstance().importModule("correlate_c");
if (!pModule)
{
qDebug() << "无法加载 Python 模块。";
PyErr_Print();
return error;
}
PyObject* pFunc = getInstance().getFunction(pModule, "main_function");
if (!pFunc || !PyCallable_Check(pFunc))
{
qDebug() << "无法获取 Python 函数。";
Py_DECREF(pModule);
PyErr_Print();
return error;
}
// 在这里正确定义和初始化 pArgs 和 pResult
PyObject* pArgs = PyTuple_New(4);
PyTuple_SetItem(pArgs, 0, PyList_FromVector(input1)); // 将QVector转换为PyObject(Python列表)
PyTuple_SetItem(pArgs, 1, PyList_FromVector(input2)); // 将QVector转换为PyObject(Python列表)
PyTuple_SetItem(pArgs, 2, Py_BuildValue("i", L));//码元数目
PyTuple_SetItem(pArgs, 3, Py_BuildValue("i", fos));//采样率
PyObject* pResult = PyObject_CallObject(pFunc, pArgs);
if (!pResult) {
qDebug() << "无法调用python代码中的函数.";
Py_DECREF(pArgs);
Py_DECREF(pFunc);
Py_DECREF(pModule);
PyErr_Print();
return error;
}
double output = PyFloat_AsDouble(pResult); // 直接解析返回值为double
Py_DECREF(pArgs);
Py_DECREF(pResult);
Py_DECREF(pFunc);
Py_DECREF(pModule);
return output;
}
测试
运行一下exe,看看butter_c.py中调用 numpy库 和 scipy库 的巴特沃斯滤波器的效果:
运行速度很快很稳定,动态图像正确,完毕!
作者:_Seikou_