Qt/C++调用Python,以函数指针转PyObject*作回调使用

此内容为原创,转载请注明出处。

因为工作需要使用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);
}
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值