原文链接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)