Qt C++ Python 混合编程测试使用文档
Qt C++ Python 混合编程测试文档
[TOC]
环境版本
- Qt:5.9.0 (MSVC 2017 64bit)
- Python: 3.10.2 (64 bit)
开发步骤
将 Python 集成到 Qt 中
安装Python环境(略)
Qt 配置 Python 解释器
选项——环境——外部工具
添加工具:Python3
构建执行档:python.exe路径(我这里是 D:\python\python.exe)
参数:%{CurrentDocument:FilePath}
工作目录:%{CurrentDocument:Path}

添加 Python 脚本文件
打开项目文件,选择添加 Python File 文件。

本文将用下面的两个函数作为测试函数,分别接受一个 Python 列表和一个 numpy.darray, 将他们排序后并返回。具体如下所示:
注意:这里不能命名文件为 test.py。 会和python定义的test.py重复,运行会报错找不到.py文件。所以不要将.py文件命名为test.py。
1 | # list_test.py |
添加 Python 程序文件后,可用测试程序,按照下面的方式运行,检测是否添加文件成功。

Qt 调用Python脚本
pro 导入Python环境
1 | INCLUDEPATH += -I D:\python\include |
注意:
+= -LD:\python
中的 LD 不可分开-lpython310
根据自己python版本决定,比如python 3.9 为-lpython39
- 我的python路径如下所示:
![]()
C++ 调用 Python 函数接口 API介绍
本文以上文两个函数为例,分别测试参数和返回值为 list 类型和 numpy.darray 类型的函数调用。
从操作步骤上看,C++调用 Python 低层接口可以分为几个阶段
- 初始化Python解释器
- 从C++到Python转换数据
- 用转换后的数据做参数调用Python函数
- 把函数返回值转换为C++数据结构
初始化Python解释器
1 | #include <Python.h> |
初始化Python后,可以通过
int PyRun_SimpleString(const char *command)
函数令解释器执行任意 python 代码。这种叫做高层接口。高层接口虽然方便,但很难与C/C++交换数据。所以对于复杂需求,应该使用低层接口。虽然需要多写很多C代码,但可以灵活的实现很多复杂功能。
C++ 数据转化为 PyObject
PyObject 为 C++ 中 Python 的数据类型,传入 Python 函数的参数和从 Python 函数得到的返回值,必须以 PyObject 的形式存在。
基本数据类型转换
- 可以采用下面的库函数进行基本数据的转换:
1 | PyObject* PyLong_FromLong(long v) |
- 可以采用 Py_BuildValue() 函数进行转换
PyObject *Py_BuildValue(const char **format*, ...)
下面给出几个例子
1 | Py_BuildValue("") None |
- 列表、元组类型转换 PyList、PyTuple
List API 简单介绍
1 | // 判断是否是一个Python List(列表) |
具体使用如下所示:
1 | // 将C++ 数组 array_1 转化为 PyList |
Tuple API 简单介绍
1 | // 判断是否是一个元组对象 |
用法与 PyList 基本一致。
numpy.darray API 简单介绍
在使用此类型之前,必须进行一些配置:
- 在Pro文件中,加入下面语句,使得配置环境包括numpy/arrayobject.h:
1 INCLUDEPATH += -I D:\python\Lib\site-packages\numpy\core\include
- 添加头文件
#include <numpy/arrayobject.h>
- 添加函数
init()
并在Py_Initialize();
后调用函数,注意,这里可能出错,需要调整使用 Release 编译而不是 Debug 编译。
1
2
3
4
5
6
7
8
9
...
int init_numpy() {
import_array();
}
...
Py_Initialize();
init_numpy();
做完初始化后,我们就可以使用 PyArrayObject 对象。先对PyArrayObject 对象做一个简单的介绍。PyArrayObject 实际上是一个结构体,结构体内包含四个元素,用来访问 Numpy Array 中的数据:
- int nd:Numpy Array数组的维度。
- int *dimensions :Numpy Array 数组每一维度数据的个数。
- int *strides:Numpy Array 数组每一维度的步长。
- char *data: Numpy Array 中指向数据的头指针。
所以当我们要访问 PyArrayObject 对象中的数据时,有:
1 | //对于一维 Numpy Array 数组,我们访问[i]位置处的元素的值 |
我们以创建一维数组为例:
1 | npy_intp Dims[1] = {colCnt}; //给定维度信息 |
调用 Python 函数
我们首先需要指定 模块和函数:
1 | PyObject* PyModule = PyImport_ImportModule("list_test"); // 指定模块 list_test.py |
所有的参数通过一个元组数据结构进行传递,具体如下所示:
1 | //定义一个Tuple对象,Tuple对象的长度与Python函数参数个数一致 |
采用下面的方式调用函数并且获得返回值:
1 | PyObject *PyResult = (PyArrayObject *)PyObject_CallObject(PyFun, PyArg); |
返回值转化为 C++ 数据结构
基本数据结构
- 可以使用基本库函数
1 | // 使用一系列库函数转换基本变量 |
- 可以使用 PyArg_Parse() 函数
1 | int PyArg_Parse(PyObject *args, const char *format, ...) |
具体使用如下所示:
1 | int num; |
- PyList、PyTurpe
只能通过上例,单独访问转换获得每一个元素,进而组成 C++ 数组形式。
- numpy.darray
只能通过下例,单独访问转换获得每一个元素,进而组成 C++ 数组形式。
1 | int num; // 临时数据存储 |
在接受 numpy.darray 返回值时,需要通过下面的方式:
1 PyArrayObject *PyResult = (PyArrayObject *)PyObject_CallObject(PyFun, PyArg);
常见问题总结
**坑1、**首先是.py程序的名称,别命名为test.py,会和python定义的重复的,随便都想,就是别用这个名字,否则不管你里面写什么函数一律找不到,以为它压根读到的就不是这个文件。
**坑3、**怎么导入静态库: Pro文件下右键–添加库–然后外部库–然后看图–
**坑4、**如果提示你找不到 python37_d.lib 怎么办
那就把libs文件夹下的python37.lib文件,重新拷一份回来并且重命名为python37_d.lib就行了
**坑5、**提示打不开python.h文件
首先你导入库正确了,就像第三条说的那样做就行,接下来是这里的问题: 你只需要更改下构建的路径就行了,或者更简单的直接将对号去掉构建在当前工程同一个文件夹下。
**坑6、**当你做完第五步,你会发现尼玛还有问题,提示的error:error: expected unqualified-id before ‘;’ token
将error展开说是在python中的object.h文件中的slots冲突,天哪,发生了什么?解决吧
原因:由于QT中定义了slots作为关键了,而python3中有使用slot作为变量,所以有冲突
坑7、 你还要将你的.py文件放在和QT的EXE文件在同一目录下,否则还会持续报错
坑8、 QT Creator 使用 design 修改 ui界面编译后界面未更新问题的解决。项目设置文件.pro内增加 UI_DIR=./UI,同时删除掉源代码目录中ui_*.h,clear all,->qmake->rebuilt all
坑9、 fatal error: numpy/arrayobject.h: No such file or directory:在Pro文件添加路径,详情请看上文有提到。
坑10、 Qt Creator mainwindow.obj-1: error: LNK2019
- 请先把已经存在的debug和release文件夹删除,重新编译,看会不会再出现这个问题;
- 如果还在出现,那绝对说明你的一些成员函数只有声明没有实现,或者两者不一致
官方文档与参考博文
- 官方文档
- 参考博文
Python + C/C++ 嵌入式编程(1):多维数组Numpy.Array()在Python和C/C++文件间的传递问题