使用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:设置debug运行环境路径,为你项目中的py安装环境即可,我把py安装在项目根目录中的env/pyforcpp中(项目名:Template,环境:python38,官网下载即可):
  • Py_SetPythonHome(L"C:/Users/DELL/Desktop/Qt_Projects/CC_system/Template/env/pyforcpp");

  • Py_Initialize():c++中python配置的初始化,不能通过会报错;
  • Py_Initialize();
  • 设置py脚本所在的位置
  • 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_

    物联沃分享整理
    物联沃-IOTWORD物联网 » 使用Qt框架实现C++调用Python脚本的方法详解

    发表回复