Qt调用Python进阶篇

原文链接Qt调用Python进阶篇

在上一篇中介绍了 Qt 调用 Python 的入门操作,这一篇中我们来看看还有哪些更高级的用法

写作原因

在实际使用中可能有保密的需求,毕竟谁也不想自己辛辛苦苦写的 Python 代码就这么变成赤裸的小羔羊,在一篇Qt 调用 Python 并打包发布中我们介绍了基本用法,回避这个问题的方法是将 python 文件放到 Qt 的资源系统中去,然后读取文本文件内容调用 python 的最简单的执行函数PyRun_SimpleString,但这很明显不能满足需求,下面就如何解决这个问题记录下.

方法

  1. 将文件放到资源中打包(避免源码泄露)
  2. 编写 Python 加载函数(参照了开源方案)
#DemoPython.py
def PrintHello(name):
    print("Hello  %s" % name);

def add(a, b):
    return a + b

def mul(a, b):
    return a * b;

#if __name__ == '__main__':
#    PrintHello("Spygg");
#    add(3, 4)
    pass
  1. 遇到的坑
  • 在 windows 中运行 mingw32 版 Qt 时一定要使用 -lpython34 的形式
#引入Python模块
LIBS += -Lc:/python34/libs -lpython34
  • 在引入 python.h 之前添加 cmath,不然会报找不到::hypot 之类的错误
#undef slots
#include <cmath>
#include <Python.h>
#define slots Q_SLOTS

最终运行的结果[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-0ruNrsv1-1583667705459)(/photos/Qt调用Python进阶篇/运行示意图.webp)]

其他开源解决方案

经过一阵搜索找到了一个开源模块叫PythonQt一般来用有点笨重,有兴趣的同学自己可以研究下

PS

核心代码如下,主要参考了 PythonQt 项目


#include "callpython.h"

#include <QFile>
#include <QDebug>

CallPython::CallPython():
    m_bInitOk(false)
{
    m_object = NULL;
    Py_Initialize();   //初始化

    m_bInitOk = Py_IsInitialized();
}

CallPython::~CallPython()
{
    Py_Finalize();
}

PyObject *CallPython::getMainModule()
{
    PyObject *dict = PyImport_GetModuleDict();
    return PyDict_GetItemString(dict, "__main__");
}

PyObject *CallPython::lookupObject(PyObject* module, const QString& name)
{
    QStringList l = name.split('.');
    PyObject* p = module;

    QByteArray b;
    for (QStringList::ConstIterator i = l.begin(); i!=l.end() && p; ++i) {
        b = QStringToPythonEncoding(*i);
        if (PyDict_Check(p)) {
            p = PyDict_GetItemString(p, b.data());
        }
        else {
            p = PyObject_GetAttrString(p, b.data());
        }
    }
    PyErr_Clear();
    return p;
}


PyObject* CallPython::lookupCallable(PyObject* module, const QString& name)
{
    PyObject* p = lookupObject(module, name);
    if (p) {
        if (PyCallable_Check(p)) {
            return p;
        }
    }
    PyErr_Clear();
    return NULL;
}

int CallPython::execSimpleString(QString ps)
{
    return PyRun_SimpleString(ps.toUtf8().constData());
}

QVariant CallPython::callMethod(const char *method, const QVariantList& args)
{
    if(!m_object){
        qDebug() << "请先加载文件";
        return -1;
    }

    PyObject* methodobj = PyObject_GetAttrString(m_object, method);

    if(methodobj){
        PyObject* r = callAndReturnPyObject(methodobj, args);

        if(r){
            return PyConvert::PyObjToQVariant(r);
        }
        else{
            qDebug() << QString("调用函数 %1 时发生了异常!").arg(method);
//            handleError();
        }
    }
    else{
        qDebug() << QString("没有找到函数: %1 ").arg(method);
    }

    PyErr_Clear();
    return QVariant();
}

PyObject* CallPython::callAndReturnPyObject(PyObject* callable, const QVariantList& args)
{
    PyObject* result = NULL;

    if (callable) {
        bool err = false;
        PyObject* pargs;
        int count = args.size();
        if (count > 0 ) { // create empty tuple if kwargs are given
            pargs = PyTuple_New(count);

            // transform QVariant arguments to Python
            for (int i = 0; i < count; i++) {
                PyObject* arg = PyConvert::QVariantToPyObject(args.at(i));
                if (arg) {
                    // steals reference, no unref
                    PyTuple_SetItem(pargs, i,arg);
                }
                else {
                    err = true;
                    break;
                }
            }
        }
        if (!err) {
            // do a direct call if we have no keyword arguments
            PyErr_Clear();
            result = PyObject_CallObject(callable, pargs);
        }
    }

    return result;
}

int CallPython::loadPythonFile(QString fileName)
{
    QFile file(fileName);
    if(file.open(QIODevice::ReadOnly)){
        QByteArray data = file.readAll();
        if (m_object){
            Py_DECREF(m_object);
        }

        m_object = getMainModule();
        PyObject *code = compileSource(fileName, data);
        if (code) {
            evalCode(m_object, code);
        }
    }

    return 0;
}


QVariant CallPython::evalCode(PyObject* object, PyObject* pycode)
{
    QVariant result;

    if (pycode) {
        PyObject* dict = NULL;
        PyObject* globals = NULL;

        if (PyModule_Check(object)) {
            dict = PyModule_GetDict(object);
            globals = dict;
        } else if (PyDict_Check(object)) {
            dict = object;
            globals = dict;
        }
        else{
            dict = PyObject_GetAttrString(object, "__dict__");
            globals = PyObject_GetAttrString(PyImport_ImportModule(PyString_AS_STRING(PyObject_GetAttrString(object, "__module__"))),"__dict__");
        }

        PyObject* r = NULL;

        if (dict) {
#ifdef PY3K
            r = PyEval_EvalCode(pycode, globals, dict);
#else
            r = PyEval_EvalCode((PyCodeObject*)pycode, globals, dict);
#endif
        }
        if (r) {
            Py_DECREF(r);
        }
        else {
            handleError();
        }
    }
    else {
        handleError();
    }
    return result;
}

void CallPython::handleError()
{
    PyErr_Print();
}


PyObject *CallPython::compileSource(const QString& path, const QByteArray& data)
{
    PyObject *code;

#ifdef PY3K
    PyObject* filename = PyConvert::QStringToPyObject(path);
    code = Py_CompileStringObject(data.data(), filename,
                                  Py_file_input, NULL, -1);
    Py_DECREF(filename);
#else
    code = Py_CompileString(data.data(), QStringToPythonConstCharPointer(path),
                            Py_file_input);
#endif
    return code;
}

PPS

由于打包的问题还没搞定就不放完整工程了,有需要的小伙伴可以联系我
完整工程(fake)

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章