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)

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