写点什么

Qt 下异步使用 C++ 调用 Python 文件

作者:Geek_163f36
  • 2022 年 8 月 08 日
  • 本文字数:2316 字

    阅读完需:约 8 分钟

Qt 项目中使用到了 C++异步调用 Python,这里记录一下。

环境

C++ 14,Python 2.7 ,Qt5.4.2 用 CMake 构建,Win10 64 位


CMakeLists.txt:Python 部分


# Python环境配置find_package(Python2.7 COMPONENTS Interpreter Development REQUIRED)include_directories(${PYTHON_INCLUDE_DIR})
复制代码


C++ 体系复杂,且构建依赖环境,一定要注意各个环节版本是否一致。特别是系统 64/32 位,两种环境不能互通。

代码

C++头文件


class MyWindow : public QMainWindow{Q_OBJECTpublic:    explicit MyWindow(QWidget *parent = 0);    //销毁资源    virtual ~MyWindow();    //异步调用方法需设置为static    static QString callPython(QString selectedValue);public slots:    void onValueChanged();    void onComboChanged(int idx);private:    //任务监听    QFutureWatcher<QString> *myWatcher;    QComboBox *myCombo;    QString myValue;}
复制代码


  • QFutureWatcher<QString>:为任务监听,QString 为异步任务返回值,全局初始化是为了方便获取执行结果及销毁资源,不全局申明可在 onValueChanged 方法中用下面的代码获取任务监听


QFutureWatcher\<QString\> *myWatcher = dynamic_cast<QFutureWatcher\<QString\> *\>(this->sender());
复制代码


C++ CXX 文件:


#include <Python.h>
MyWindow::MyWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MyWindow), m_Model(NULL) { ui->setupUi(this); myCombo = new QComboBox(this); //...初始化combobox //绑定comboBoxchange事件 connect(myCombo, SIGNAL(currentIndexChanged(int)), SLOT(onComboChanged(int))); myWatcher = new QFutureWatcher<QString>(this); //监听任务结束事件 connect(myWatcher, SIGNAL(finished()), this, SLOT(onValueChanged())); ... } MyWindow::~MyWindow() { //销毁资源 myWatcher->cancel(); myWatcher->waitForFinished(); delete myWatcher; } void MyWindow::onComboChanged(int idx) { QString selectedValue = myCombo->currentData().value<QString>(); QFuture<QString> myFuture = QtConcurrent::run(MyWindow::callPython, selectedValue); cacWatcher->setFuture(myFuture); }
QString MyWindow::callPython(QString selectedValue){ QString result = ""; if(Py_IsInitialized() == 0){ Py_Initialize(); } QString filename = "py文件路径"; QFileInfo filepath = QFileInfo(filename); QString path = filepath.absolutePath(); PyObject *sys = PyImport_ImportModule("sys"); PyObject *syspath = PyObject_GetAttrString(sys, "path"); PyList_Insert(syspath, 0, PyString_FromFormat(path.toStdString().c_str())); filename = filepath.fileName().split(".")[0]; PyObject *pName = PyString_FromString(filename.toStdString().c_str()); PyObject *pModule = PyImport_Import(pName); if (pModule != NULL) { PyObject *pDict = PyModule_GetDict(pModule); PyObject *pFunc = PyDict_GetItem(pDict, PyString_FromString("函数名称")); if (pFunc != NULL) { PyObject *pyParams = PyTuple_New(2); PyTuple_SetItem(pyParams, 0, Py_BuildValue("s", selectedValue.toStdString().c_str()));
PyObject *pythonResult = PyObject_CallObject(pFunc, pyParams); result = QString(PyString_AsString(pythonResult).c_str());
} else { msgBox.setText("can't find function"); msgBox.exec(); } } else { msgBox.setText("can't find dir"); msgBox.exec(); } } catch (std::exception &err) { msgBox.setText(err.what()); msgBox.exec(); } return result;}
void MyWindow::onValueChanged() { this->myValue = myWatcher->result(); // ...业务代码 }
复制代码


  • QFutureWatcher:为任务监听

  • QFuture:为异步任务,这里使用 QtConcurrent 来创建异步任务,也有其它模式,具体请参见官方文档。

  • Py_Initialize 为初始化 python 环境,但在 2.7 时重复初始环境会导致系统崩溃,可使用 Py_IsInitialized()判断是否已初始化过环境。

  • Py_DECREF:看说明是释放资源,但是调用函数第二次运行时也会崩溃,所以实际代码中没有使用。

  • Py_Finalize():同上述原因实际没有使用。

  • PyObject_CallObject:第二个参数为 Python 函数参数,可有多个,使用 PyTuple 包裹。


Python 源码:


import os
def call_python(param): helloStr = "Hello in Python "+param; return helloStr;
复制代码


代码运行过程中每第二次调用 python 程序就会崩溃,一度以为是 QFutureWatcher 的异步监听没有释放资源导致,修改为同步任务调试才发现是由于 Py_Initialize 二次初始化及 Py_DECREF 释放资源导致。去掉相关资源释放后反而正常了。还是要以实际效果为准

参考资料

https://zhuanlan.zhihu.com/p/149887203


https://blog.csdn.net/iamqianrenzhan/article/details/86516440

用户头像

Geek_163f36

关注

还未添加个人签名 2020.06.10 加入

还未添加个人简介

评论

发布
暂无评论
Qt下异步使用C++调用Python文件_Geek_163f36_InfoQ写作社区