本意目的是通过qt窗口调用python脚本,使用nltk提取文章关键字,配置了一天,记录如下。
1.#include <Python.h>
首先需要明确,qt的位数(msvc的位数)一定要和python位数相同,若不相同会提示:
·“x86与计算机当前模块x64不符”
·或明明选了libs文件夹还报错“未能解析的外部符号”LINK2019这种错误,也是位数不同导致的
其次vs2019里选择对应python解释器的include和libs目录,为头文件和链接库提供路径。
然后按网上大家说的,修改python文件夹下include/object.h中400行左右的地方(搜索slot即可),添加这两句
接着进入python文件夹下的libs文件夹,把python37.lib复制一份重命名python37_d.lib
此时在qt头文件里打上 #include <Python.h>,f5运行可行
2.第三方库的调用
有传参示例如下:
#include "trytrypython.h"
trytrypython::trytrypython(QWidget* parent)
: QMainWindow(parent)
{
ui.setupUi(this);
//进行初始化
Py_Initialize();
//如果初始化失败,返回
if (!Py_IsInitialized())
{
qDebug() << "IsInitialized: error";
}
//导入trydata.py文件,放在exe同文件夹
PyObject* pModule = PyImport_ImportModule("trydata");
if (!pModule)
{
qDebug() << "ImportModule: error";
}
//调用其中的add函数求和
PyObject* pFuncHello = PyObject_GetAttrString(pModule, "add");
//如果失败则返回
if (!pFuncHello)
{
qDebug() << "GetAttrString: error";
}
else {
PyObject* args = PyTuple_New(2);
PyObject* arg1 = PyLong_FromLong(11111);
PyObject* arg2 = PyLong_FromLong(22222);
PyTuple_SetItem(args, 0, arg1);
PyTuple_SetItem(args, 1, arg2);
PyObject* result = PyObject_CallObject(pFuncHello, args);
if (result)
{
long a = PyLong_AsLong(result);
qDebug() << "a:" << a << endl;
}
}
//调用函数
//PyObject_CallFunction(pFuncHello, NULL);
//退出
Py_Finalize();
}
我是没有进行任何操作,直接在代码中import nltk以及其他包,便可使用分词筛词等功能
第二天后记:
1.传参时使用“类型”来满足多种类型的传参
PyObject *pArgs = PyTuple_New(3);
PyObject* arg1 = Py_BuildValue("i",220); //整数参数
PyObject* arg2 = Py_BuildValue("f", 3.14); //浮点数参数
PyObject* arg3 = Py_BuildValue("s", "hello"); //字符串参数
PyEval_CallObject(pFunc, pArgs); //调用函数,pArgs元素个数与被调函数参数个数一致
其他形式传入参数,或用Pyxx_fromyy(与as相对应)变换,如下
2.python返回值时不知道为什么无法使用PyList_Check(retvalue)来判断返回值的类型,但这个值仍然可以使用其类型的对应操作如list用PyList_GetItem()等,故传字符串时不知道对面过来的究竟是什么东西,最后python里字符串是unicode形式,需要先转为utf8,也就是python的bytes形式,再用PyBytes_AsString()来读取为char[ ],(该函数是char * PyString_AsString(PyObject*)的更新),或者在python端就把str转为bytes再return,两种方法都可以正常收回返回值字符串。方法一如下:
/*nltk.freq()的数据结构是这样的:
[0:('appl', 12)
1:('banana', 7)
2:('orang', 6)
3:('paint', 5)
4:('call', 2)]
list里套tuple套str和int,我只想要str*/
PyObject* result=PyObject_CallObject(pFuncGetKeyword, args);
int SizeOfList = PyList_Size(result);//List对象的大小
for (int i = 0; i < SizeOfList; i++)
{
PyObject* ListItem = PyList_GetItem(result, i);//获取List对象中的每一个元素
printf("%s", PyBytes_AsString(PyUnicode_AsUTF8String(PyTuple_GetItem(ListItem, 0))));
//Py_DECREF(ListItem); //释放空间
}
最后可以成功用了下面这些包,返回了一个多层嵌套的list到qt主线程作为char[ ]打印
读了挺久的python文档,上面有c++调用python用例,下面有具体的交互函数
网上有说要把对应的site-package里的包复制到exe同文件夹下的,不行的可以试试
复制sitepackage:QT(msvc2015_64)调用Python3脚本过程记录
3.其他小插曲
电脑更改过用户名,原来C盘下默认路径的python内路径一片狼藉,删除后(控制面板方法)重新下载(找到了原来的下载包),CMD内:python -m pip install nltk 提示我已经安装了nltk,而import nltk报错:
No module named 'regex._regex
python -m pip install nltk重新安装依然无果
删除regex包重新安装,成功,之前在anaconda里下载过nltk数据包(本次这个python是单独的32位),其nltk_data文件夹路径在c盘是共享的,尝试stopwords等数据,成功分词并提取词干。
若没下载过nltk的,import nltk后
nltk.download()
从可视窗口下载即可
*第三天后记:
出现了第一个问题是重复import_module一个py文件,nltk不允许重复import会报错,不知道其他库怎么样,故调整为在qt界面开始就打开py解释器,打开py文件,后续需要调用函数时再去单独调用函数,避免了重复import nltk
第二个问题是无法多次(6次以上)使用python函数进行nltk分析,后发现原因是
PY_DECREF这玩意不仅p用没有还不让我大量调用,没时间研究具体原理是啥,全部注掉,心情愉悦~