1、背景
简单说一下需求,Qt开发的上位机界面程序,需要调用Python编写的算法跑一个结果返回到界面上显示。
2、度娘出一篇博客,按照步骤进行环境搭建和简单的代码测试
环境搭建请参照如下博客地址:
博客:① https://blog.csdn.net/cholenmine/article/details/82854301
② https://blog.csdn.net/yinyuchen1/article/details/77775851
#include "Python.h"
void MainWindow::test()
{
//进行初始化
Py_Initialize();
//如果初始化失败,返回
if(!Py_IsInitialized())
{
qDebug()<<"11111111111111111111";
return ;
}
//加载模块,模块名称为myModule,就是myModule.py文件
PyObject *pModule = PyImport_ImportModule("myModule");
//如果加载失败,则返回
if(!pModule)
{
qDebug()<<"2222222222222222";
return;
}
//加载函数greatFunc
PyObject * pFuncHello = PyObject_GetAttrString(pModule, "greatFunc");
//如果失败则返回
if(!pFuncHello)
{
qDebug()<<"3333333333333333333333";
return ;
}
Py_Finalize();
}
3、根据目前的具体需求,我需要在开启一个线程来调用Python脚本,于是用qt内部的信号槽来使用线程调用,调用方法还是用的上面的示例代码。
.h文件
void Widget::handleLoadGCode(QString str)
{
m_loadGCodeClick = true;
pQwait->SetShowText(u8"提示", u8"加载G代码中,请稍后");
pQwait->show();
if (!m_isInitPy)
m_consuming->PythonInit();
else
qDebug() << "python环境已经初始化了";
m_isInitPy = true;
QThread *thread = new QThread;
connect(thread, &QThread::started, [=]() {
m_consuming->LoadGCode(str);
});
connect(m_consuming, SIGNAL(successed()), this, SLOT(handleTimeConsumingEnd()));
connect(m_consuming, SIGNAL(successed()), thread, SLOT(quit()));
connect(m_consuming, SIGNAL(failed()), this, SLOT(handleTimeConsumingFailed()));
connect(m_consuming, SIGNAL(failed()), thread, SLOT(quit()));
connect(thread, SIGNAL(finished()), thread, SLOT(deleteLater()));
m_consuming->moveToThread(thread);
thread->start();
}
线程类一定要是继承自qobject的,就行了。
现在问题来了:
① 第一次调用python脚本,能够正常调用并且得到结果。
② 不关闭主界面,接着进行第二次调用,软件直接崩溃,崩溃的行数是PyImport_ImportModule()函数。
最开始分析的原因:① 出现了空指针
② 第二次调用时,第一次的资源没有释放,占用python脚本,导致PyImport_ImportModule()函数不能将模块导入
4、最后差资料发现,因为我这里使用的是线程,C++多线调用python时必须要控制GIL
参照如下博客的方法才得以解决这个问题,对于小白初次线程中调用Python,鬼知道要控制什么GIL,虽然问题解决了,到现在都没去看GIL是个什么鬼
https://blog.csdn.net/qq_42938320/article/details/101770269
解决方法 :
1:先新建一个.h文件:如下
//将全局解释器锁和线程的相关操作用类封装
#ifndef PYTHREADSTATELOCK_H
#define PYTHREADSTATELOCK_H
#include "Python.h"
class PyThreadStateLock
{
public:
PyThreadStateLock(void)
{
_save = nullptr;
nStatus = 0;
nStatus = PyGILState_Check(); //检测当前线程是否拥有GIL
PyGILState_STATE gstate;
if (!nStatus)
{
gstate = PyGILState_Ensure(); //如果没有GIL,则申请获取GIL
nStatus = 1;
}
_save = PyEval_SaveThread();
PyEval_RestoreThread(_save);
}
~PyThreadStateLock(void)
{
_save = PyEval_SaveThread();
PyEval_RestoreThread(_save);
if (nStatus)
{
PyGILState_Release(gstate); //释放当前线程的GIL
}
}
private:
PyGILState_STATE gstate;
PyThreadState *_save;
int nStatus;
};
#endif // PYTHREADSTATELOCK_H
2:然后写个PythonInit函数,这个函数只调一次就行了。
void Robot_time_consuming::PythonInit()
{
if (!Py_IsInitialized())
{
//1.初始化Python解释器,这是调用操作的第一步
Py_Initialize();
if (!Py_IsInitialized()) {
qDebug("初始化Python解释器失败");
emit failed();
}
else {
//执行单句Python语句,用于给出调用模块的路径,否则将无法找到相应的调用模块
PyRun_SimpleString("import sys");
QString setSysPath = QString("sys.path.append('%1')").arg(QCoreApplication::applicationDirPath());
PyRun_SimpleString(setSysPath.toStdString().c_str());
// 初始化线程支持
PyEval_InitThreads();
// 启动子线程前执行,为了释放PyEval_InitThreads获得的全局锁,否则子线程可能无法获取到全局锁。
PyEval_ReleaseThread(PyThreadState_Get());
qDebug("初始化Python解释器成功");
}
}
}
3:然后再你调用python函数的地方这样写,记住,之前的
Py_Finalize(),要删除;
void Robot_time_consuming::LoadGCode(QString GName) { class PyThreadStateLock PyThreadLock;//获取全局锁 //导入TransferKRL.py模块 PyObject* pModule = PyImport_ImportModule("TransferKRL"); if (!pModule) { QString infoData = "Can not open python file!"; qDebug() << infoData; emit failed(); return; } PyObject *pyClass = PyObject_GetAttrString(pModule, "GcodeToTrack"); PyObject *pConstruct = PyInstanceMethod_New(pyClass); PyObject* pParams = PyTuple_New(2); PyTuple_SetItem(pParams, 0, Py_BuildValue("s", GName.toStdString().c_str())); PyTuple_SetItem(pParams, 1, Py_BuildValue("s", "trackFile01.csv")); PyObject* pIns = PyObject_CallObject(pConstruct, pParams); int res = 0; // PyArg_Parse(FuncTwoBack,"i",&res);//转换返回类型 qDebug() << "res:" << res; emit successed(); }
标签:consuming,调用,QT,python,c++,Python,线程,GIL,PyEval
From: https://www.cnblogs.com/lidabo/p/17076768.html