logo头像

技术引领生活!

Qt调用Python进阶篇

本文于1760天之前发表,文中内容可能已经过时。

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

写作原因

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

方法

  1. 将文件放到资源中打包(避免源码泄露)
  2. 编写 Python 加载函数(参照了开源方案)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
#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 的形式
1
2
#引入Python模块
LIBS += -Lc:/python34/libs -lpython34
  • 在引入 python.h 之前添加 cmath,不然会报找不到::hypot 之类的错误
1
2
3
4
#undef slots
#include <cmath>
#include <Python.h>
#define slots Q_SLOTS

最终运行的结果如图

其他开源解决方案

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

PS

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209

#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)

支付宝打赏 微信打赏

您的支持是我前行的动力!