原文鏈接Qt調用Python進階篇
在上一篇中介紹了 Qt 調用 Python 的入門操作,這一篇中我們來看看還有哪些更高級的用法
寫作原因
在實際使用中可能有保密的需求,畢竟誰也不想自己辛辛苦苦寫的 Python 代碼就這麼變成赤裸的小羔羊,在一篇Qt 調用 Python 並打包發佈中我們介紹了基本用法,迴避這個問題的方法是將 python 文件放到 Qt 的資源系統中去,然後讀取文本文件內容調用 python 的最簡單的執行函數PyRun_SimpleString,但這很明顯不能滿足需求,下面就如何解決這個問題記錄下.
方法
- 將文件放到資源中打包(避免源碼泄露)
- 編寫 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
- 遇到的坑
- 在 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)