此内容为原创,转载请注明出处。
因为工作需要使用Qt调用Python,但有耗时操作时Python需要上传数据到Qt,特地研究了一下回调的使用,记录一下。
Python2可以直接使用Py_InitModule生成一个PyObject*,我这里是用的Python3,需要使用到PyModule_Create。
前提
Qt已经添加了Python库,并且能够正常调用Python的API,可以成功调用Python的代码块,这部分可以参考我另外一篇博客:
https://blog.csdn.net/qq_23604781/article/details/117564135
第一步 首先创建一个traincallback.h 和 traincallback.cpp文件。
将traincallback.h 中引入一下头文件 #include <Python.h>
- 再来看traincallback.cpp,先设计一个回调上来的处理函数。
#include "traincallback.h"
int getVaule(int a){
qDebug() << "a=" << a;
return a;
}
- 再将这个回调函数处理成Python能识别的PyObject*模式,注意这里的结果一定不能ruturn NULL,我有试过return NULL,结果回调函数不能执行。
static PyObject* callBack(PyObject *self, PyObject *args)
{
int n;
if (!PyArg_ParseTuple(args,"i",&n))
return NULL;
return Py_BuildValue("i", getVaule(n));
}
- 将回调函数包装一下。
static PyMethodDef module_methods[] = {
{"getVaule", callBack, METH_VARARGS},
{NULL, NULL}
};
注意:如果有多个回调函数的话,可以在这里添加多行代码,而不是添加多个[]。例如:
static PyMethodDef module_methods[] = {
{“getVaule”, callBack, METH_VARARGS},
{“getVaule2”,callBack2,METH_VARARGS},
{“getVaule3”,callBack3,METH_VARARGS},
……
{NULL, NULL}
};
- 将包装好的方法装进结构体cModPyDem 中待用。
static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"callBack", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
module_methods
};
- 利用PyModule_Create生成PyObject*。
PyMODINIT_FUNC PyInit_cModPyDem(void)
{
PyObject *m = PyModule_Create(&cModPyDem);
if(m == NULL){
qDebug("PyInit_cModPyDem failed!");
return NULL;
}
return m;
}
注意:这里的函数名一定要以PyInit_为开头。
- 我们再打开traincallback.h文件,将刚刚生成PyObject*的函数提取出来,以供调用Python函数代码块时使用。
#include <QObject>
#include <QDebug>
#include <Python.h>
PyMODINIT_FUNC PyInit_cModPyDem(void);
第二步 创建一个python代码,用来回调上传数据。
创建一个demo.py文件,写入代码:
import time
def initCallback(cc):
print("")
cb = cc
for index in range(100):
cb.getVaule(index)
time.sleep(1)
cc 为传输进来的c++函数,我们在一个for循环中,每隔一秒就给Qt上传一次数据。
第三步 Qt调用Python函数,将回调传给Python。
先讲一下流程,再上代码。
引用一下头文件 #include <traincallback.h> #include <Python.h>
- 先使用PyImport_ImportModule加载demo.py;
- 用PyObject_GetAttrString从demo.py中加载函数initCallback;
- 用PyTuple_SetItem设计参数;
- PyObject_CallObject调用Python函数initCallback,并开通回调。
上代码:
void TrainThread::demoCallback()
{
PyObject *demo = PyImport_ImportModule("demo");
if(demo == NULL){
emit showTrainLog("import demo.py failed!");
return;
}
PyObject *initCallback = PyObject_GetAttrString(demo,"initCallback");
if(initCallback == NULL){
emit handle->showTrainLog("init setTrainData failed!");
return;
}
PyObject* pReturnValue;
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, PyInit_cModPyDem());
pReturnValue = PyObject_CallObject(initCallback, args);
}
运行结果
完整代码块。
- Python部分
import time
def initCallback(cc):
print("")
cb = cc
for index in range(100):
cb.getVaule(index)
time.sleep(1)
- Qt生成回调函数部分
traincallback.h
#include <QObject>
#include <QDebug>
#include <Python.h>
PyMODINIT_FUNC PyInit_cModPyDem(void);
traincallback.cpp
#include "traincallback.h"
int getVaule(int a){
qDebug() << "a=" << a;
return a;
}
static PyObject* callBack(PyObject *self, PyObject *args)
{
int n;
if (!PyArg_ParseTuple(args,"i",&n))
return NULL;
return Py_BuildValue("i", getVaule(n));
}
static PyMethodDef module_methods[] = {
{"getVaule", callBack, METH_VARARGS},
{NULL, NULL}
};
static struct PyModuleDef cModPyDem =
{
PyModuleDef_HEAD_INIT,
"callBack", /* name of module */
"", /* module documentation, may be NULL */
-1, /* size of per-interpreter state of the module, or -1 if the module keeps state in global variables. */
module_methods
};
PyMODINIT_FUNC PyInit_cModPyDem(void)
{
PyObject *m = PyModule_Create(&cModPyDem);
if(m == NULL){
qDebug("PyInit_cModPyDem failed!");
return NULL;
}
return m;
}
- Qt调用Python代码,并传入回调部分。
#include "traincallback.h"
void TrainThread::demoCallback()
{
PyObject *demo = PyImport_ImportModule("demo");
if(demo == NULL){
emit showTrainLog("import demo.py failed!");
return;
}
PyObject *initCallback = PyObject_GetAttrString(demo,"initCallback");
if(initCallback == NULL){
emit handle->showTrainLog("init setTrainData failed!");
return;
}
PyObject* pReturnValue;
PyObject* args = PyTuple_New(1);
PyTuple_SetItem(args, 0, PyInit_cModPyDem());
pReturnValue = PyObject_CallObject(initCallback, args);
}