如何实现在c++QT中调用py脚本呢?方法如下:
方法一:通过官方python.h进行调用
该方法参考了Ubuntu18.04下 Qt调用conda下的python_猫生鱼的博客-CSDN博客
感谢大佬的分享~~
1.在.pro文件中加入
##加入python库(根据实际位置调整路径)
LIBS += -L/usr/local/python3.7/lib -lpython3.7m -lcrypt -lpthread -ldl -lutil -lm -lpython3
DEPENDPATH += /usr/local/python3.7/include/python3.7m
INCLUDEPATH += /usr/local/python3.7/include/python3.7m -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O3 -Wall
INCLUDEPATH += /usr/local/python3.7/lib/python3.7/site-packages/numpy/core/include
2.在需要调用py脚本的文件中加入
#undef slots
#include
#define slots Q_SLOTS
tip:为什么添加#undef slots #define slots Q_SLOTS?原因是python中的slots与qt中的slots 定义冲突
3、在需要调用py脚本的.cpp文件中加入以下代码(可以在函数中加入)
//以下为使用python.h调用py的代码
Py_Initialize();// 解释器初始化
if(!Py_IsInitialized()){
PyErr_Print();
qDebug() << "Can't Initialize python!n" ;
return ;
}
PyRun_SimpleString("import sys");//令python能找到已经安装的库,不加此句会出现无法import的问题
PyRun_SimpleString("sys.argv = ['python.py']");
PyRun_SimpleString("sys.path.append('./')");//把工作目录改为py文件所在目录,这里为当前目录
//导入.py文件模块,这里的project为文件名
PyObject* pModule = Pyimport_importModule("project");
if(!pModule){
PyErr_Print();
qDebug() << "Can't open python file!n" ;
return;
}
//导入模块中的方法字典
PyObject* pDict = PyModule_GetDict(pModule);
if(!pDict){
PyErr_Print();
qDebug() << "Get Dict failed!n" ;
return;
}
//导入已导入模块中的方法或类,这里的main为方法(函数)名
PyObject* pFuc = PyDict_GetItemString(pDict, "main");
if(!pFuc){
PyErr_Print();
qDebug() << "Get function failed!n" ;
return;
}
//启用导入的方法或类
PyObject_CallFunction(pFuc,NULL);
//内存释放
Py_CLEAR(pModule);
Py_CLEAR(pDict);
Py_CLEAR(pFuc);
//释放Python解释器所占用的资源
Py_Finalize();
知识点补充:Python嵌入C/C++释放资源
Python使用引用计数机制对内存进行管理,实现自动垃圾回收。在C/C++中使用Python对象时,应正确地处理引用计数,否则容易导致内存泄漏。在Python/C API中提供了Py_CLEAR()、Py_DECREF()等宏来对引用计数进行操作。
当使用Python/C API中的函数创建列表、元组、字典等后,就在内存中生成了这些对象的引用计数。在对其完成操作后应该使用Py_CLEAR()、Py_DECREF()等宏来销毁这些对象。其原型分别如下所示。
void Py_CLEAR( PyObject *o)
void Py_DECREF( PyObject *o)
其参数含义如下。
*o:要进行操作的对象。
对于Py_CLEAR()其参数可以为NULL指针,此时,Py_CLEAR()不进行任何操作。而对于Py_DECREF()其参数不能为NULL指针,否则将导致错误。
方法二:通过多线程调用
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include
#include
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
ui->setupUi(this);
process1 = new QProcess(this);
//方式一:首先调用控制台,再通过控制台启动程序。(控制台:mac/linux下调用bash,windows下是cmd.exe)
process1->start("bash");//调用控制台程序
process1->waitForStarted();//等待程序确实启动再往下走
process1->write("/usr/bin/python3.7 ./handpose.py n");
//方式二:直接启动python,并要求他启动指定脚本。
process1->start("/usr/bin/python3.7 /AR/ar/handpose.py");//直接启动python,并要求他启动指定脚本。
//信号槽连接,获取到程序输出就调用OnReadData函数
connect(process1,SIGNAL(readyReadStandardOutput()),this,SLOT(onReadData()));
}
void MainWindow::onReadData(){
//打印输出
QString strResult = QString::fromLocal8Bit(process1->readAllStandardOutput().data());
qDebug() << strResult;
}
MainWindow::~MainWindow()
{
//杀死调用的进程
process1->kill();
process1->close();
delete process1;
delete ui;
}
输出问题:
值得指出的是,这里输出的内容,是当前py脚本运行结束后才会触发。比如脚本中有个循环10次每秒输出print,这里就不会触发10次分别打出print,而是会等10次运行结束后,统一打出文本。
解决办法,如果要想收到控制台的每次输出,可以再py脚本中将输出保存到内存中,或是剪切板,或者是指定的log文件,然后再通过qt读取,获取每次输出。