背景介绍
VS+Qt+CPP开发,需要处理docx文档。搜了一下C++的库,收费的用不起,免费的不好用。恰好最近在学python,发现python-docx模块很不错,于是写了脚本,在C++中调用。
第二次调用异常
一开始,脚本调用代码写在一个类成员方法中,如下所示:
#include "Python.h"
class Test{
public:
void WriteDoc(){
Py_Initialize();
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./Config/Scripts')"); //脚本所在目录
// do something
// ...
Py_Finalize();
}
}
int main(){
Test test;
test.WriteDoc();
test.WriteDoc();
return 0;
}
在main
函数中执行了两次WriteDoc
方法。第一次正常执行,第二次程序异常退出,并提示:(1)Fatal Python Error: Failed to import encoding module;(2)Python runtime state: core initiaized;(3)ModuleNotFoundError: No module named ‘encodings’。
有说法是Py_Initialize
和Py_Finalize
同一进程中只能调用一次。于是,将Py_Initialize
和Py_Finalize
分别放在了构造函数和析构函数中,问题解决。
#include "Python.h"
class Test{
public:
Test(){
Py_Initialize();
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./Config/Scripts')");
}
~Test(){
Py_Finalize();
}
void WriteDoc(){
// do something
// ...
}
}
int main(){
Test test;
test.WriteDoc();
test.WriteDoc();
return 0;
}
项目打包后程序闪退
将项目打包,并安装在本人开发所用的计算机上,没有任何问题。但是安装在没有python环境的计算机(如虚拟机)上运行,会闪退。通过打印日志发现,在执行构造函数中Py_Initialize
方法时就闪退。多方搜索资料后,说是缺少python运行时环境,要在Py_Initialize
之前调用Py_SetPythonHome
:
Py_SetPythonHome(L"./Config//env");
Py_Initialize();
PyRun_SimpleString("import sys");
PyRun_SimpleString("sys.path.append('./Config/Scripts')");
我将本机安装的Python目录复制到项目文件夹下并改名为env,然后将env的相对路径作为Py_SetPythonHome
参数,重新打包后,再次安装在虚拟机中运行,没有问题。
直接复制过来的Python目录下有很多无关内容,为了减少打包文件的大小,只保留DLLs和Lib这两个目录即可。这两个目录下的内容也有很多冗余,可以自行删除。
例如,我写的脚本中只用到了python-docx模块,于是在Lib目录下只保留了collections、encodings(Py_Initialize必须)、importlib、pathlib、re、zipfile文件夹以及所有的.py文件(.py文件也可以根据自行生成的__pycache__下的pyc文件对应保留);而在DLLS目录下只保留了所有的.pyd文件。
删除冗余文件的过程比较繁琐,每删除一部分,就运行一下程序,看相关功能是否能正常执行。最后保留下来的,就是python-docx模块以及Py_Initialize
所依赖的。