C++中调用Python的办法

1 背景

一直采用C++作为主语言开发,最近遇到一个项目需要解析PDF文件中的文本内容,直接采用C++来做显得不是很方便,但用python来做就显得很简单了。

难点在于如何C++语言环境下调用python来实现功能。

2 开发环境

Python版本为: 3.7.3(32 bit)版本。

 QT版本为:Qt 5.12.2(MSVC 2017, 32bit)

 QtCreator中项目的配置如下:

重要的事情(版本匹配): 

       请确保Python版本与QT版本的位数相一致,要么同为32bit,要么同为64bit。否则可能会出现代码能编译,但是debug的时候,会出现the cdb process terminated之类莫名其妙的错误。

        这里确实是一个坑,最开始本人安装的是64bit版本的python,程序也选的是64bit编译器和64bitdebugger。

        但由于QTcreator本身的版本(Based on Qt 5.12.2 (MSVC 2017, 32 bit))就是32bit的,就导致了莫名的错误。后来我都以QTcreator本身的版本为基准,全部换成32bit的,问题就解决了。

3 动手建立工程!

       方便起见,本文将python 3.7的安装目录中的几个必要文件夹(文件)直接拷贝到工程目录下,方便使用相对路径,也方便后续直接制作安装包。

当然,采用绝对路径也是香的,看个人喜好吧。

3.1 添加头文件包含目录、附加库目录、依赖库

在pro文件中:

添加

INCLUDEPATH += "./include"
LIBS += -L./libs -l_tkinter -lpython3 -lpython37
#消除中文乱码问题
msvc:QMAKE_CXXFLAGS += -execution-charset:utf-8
msvc:QMAKE_CXXFLAGS += -source-charset:utf-8

显然这里是使用了相对路径的方式。

3.2 在工程中引入头文件

#include "Python.h"

不出意外的话,这时候编译会出错!”error: C2238: 意外的标记位于“;”之前“.....

此外,还有 error: LNK1104: 无法打开文件“python36_d.lib”,甚至还有其他的...

莫慌,这里有一个很好的解决办法:

点这里错误解决,详细!

3.3 创建Python文件和工程内的C++调用

        创建一个python文件,放在当前工程exe文件的同级目录下(又是绝对和相对路径的小问题),只要让你的程序能找的到就行,其余随意!

parsepdf.py:

# -*- coding: utf-8 -*-

def readPDF(pdfpath):
	import pdfplumber
	import re
	pdf = pdfplumber.open(pdfpath)
	result=''
	for page in pdf.pages:
		result+= page.extract_text() + "\n\n"#页与页之间靠"\n\n"做间隔.
	
	pdf.close()
	return (result)

在C++中调用:

/*
*pdfpathName: 所要解析的PDF的绝对路径,同时也是传给python脚本的参数(注意如何传参)
*/
int MainWindow::PDFParserAndGenerate(QString pdfpathName)
{
    int ret = 0;
    do
    {
        //尝试加载python模块
        Py_SetPythonHome((const wchar_t *)(L"./Python37"));//发布时候加上这一句
        Py_Initialize();//初始化py模块
        if ( !Py_IsInitialized() )
        {
            ret = -1;
            break;
        }

        QString dirpath = QApplication::applicationDirPath();
        dirpath = QDir::toNativeSeparators(dirpath);

        PyRun_SimpleString("import sys");
        PyRun_SimpleString("sys.path.append('./')");//当前路径
        PyObject* pModule = PyImport_ImportModule("parsepdf");// parsepdf.py
        if (!pModule)
        {
            qDebug()<< "Cant open python file!\n";
            ret = -2;
            break;
        }
        PyObject* pFunparse= PyObject_GetAttrString(pModule,"readPDF");  // 这里的readPDF就是python文件定义的函数
        if(!pFunparse)
        {
             qDebug()<<"Get function readPDF failed";
             ret = -3;
             break;
        }
        // 创建元组设置参数
        QTextCodec *tc = QTextCodec ::codecForName("UTF-8");
        QByteArray ba = pdfpathName.toUtf8();
        char *path = ba.data();

        PyObject*  arg1 = PyUnicode_FromString(path);
        PyObject*  args = PyTuple_New(1);
        PyTuple_SetItem(args, 0, arg1);

        PyObject* fshowc = PyObject_CallObject(pFunparse,args);//调用main函数
        //PDFReportInfo reportInfo;
        if (!fshowc)
        {
            qDebug()<<"PyObject_CallObject error";
            ret = -4;
            break;
        }
        QString resultPDF = PyUnicode_AsUTF8(fshowc);/*整份PDF文档解释出来的内容 */
        QStringList  pages =resultPDF.split("\n\n");/*分割出每页内容 */

        QVector<QString>  lines;
        for (int i = 0; i < pages.size(); ++i)//遍历每一页
        {
            QStringList  listContent =pages.at(i).split("\n");/*分割单页内容 */
            for (QString s : listContent)
                lines.append(s);//每一行
        }
        // ToDo ....这里加上自己的业务代码....
    }
    while(0);

    //好同志总是在最后不忘释放资源!
    if ( Py_IsInitialized() )
    {
        Py_Finalize();
    }


    return ret;
}

4 工程打包

编译调试完毕之后,就要打包发布出去,搞到其他电脑上运行了。

注意发布包内的内容。Python37文件夹就是 “3 动手建立工程!”小节一上来就提到的部分。

保险起见,下图中的python3.dll    python37.dll  和 vcruntime140.dll是重复放进来了(Python37文件夹)中也有。可能不重复也可以,但没去尝试。

​​​​​​​

猜你喜欢

转载自blog.csdn.net/thequitesunshine007/article/details/119611831