开发笔记之:python集成Qt C++编写的扩展模块
一、问题列表
(1)C++定义的类(class)/结构体(struct)如何被python所“理解”
(2)如何了解扩展模块(.pyd)的依赖 以及 添加依赖目录
(3)指针的传递 和 管理
(4)字节数组的传递
(5)“no QGuiApplication instance and no application …”
(6)QGraphicsScene初始化问题
二、结论
(1)pybind11支持定义组合类型class/struct。
而且class/struct的项既可以是简单类型,也可以是组合类型。组合类型需要提前被定义。
(注:本项主要需改造C++端,python端对应即可)
(2.1)使用VS工具 dumpbin 来解析 .pyd 文件的依赖。(注:仅适用于windows平台)
(2.2)使用 os 的 add_dll_directory 方法来添加所依赖的 dll 所在的目录。例如添加 Qt的安装目录下的bin目录。(注:只在python端)
(3)暂时还没搞明白。
(注:使用场景:在python端发起创建句柄,调用方法中传入句柄)
(4)使用 py::buffer 类型接收,再转换成 uchar *。(主要在C++端)
(5)在模块入口构造 QGuiApplication 实例。(主要在C++端)
(6)将(5)中的 改成 QGuiApplication 改成 QApplication。
因为其属性font 依赖于 QApplication::font。(主要在C++端)
三、代码
(1)定义 & 绑定 结构体/实体类
a. 结构体定义(c++)
typedef struct foo_1 {int field1;bool field2;std::string fileld3;
} FOO_1;
b. 绑定结构体/类定义(c++, pybind11)
py::class_<FOO_1>(m, "FOO_1").def(py::init<>()).def_readwrite("field1", &FOO_1::field1).def_readwrite("field2", &FOO_1::field2).def_readwrite("fileld3", &FOO_1::fileld3);
c.引用结构体/类类型(python)
。。。
import module1 as md1
。。。
foo1 = md1.FOO_1()
foo1.field1 = 999
foo1.field2 = True
foo1.fileld3 = "hello, world"
。。。
(2.1)列出 pyd 文件的依赖(windows环境、VS开发环境、cmd)
"C:\Program Files (x86)\Microsoft Visual Studio\
2019\Community\VC\Tools\MSVC\14.29.30133\
bin\Hostx64\x64\dumpbin.exe" /DEPENDENTS module1.pyd
(2.2)添加 Qt 环境到 dll 目录(python)
import os
os.add_dll_directory("C:/Qt/5.15.10/msvc2019_64/bin")
(3)指针的使用
a.创建指针并传递(c++)
void *FooPinter Module1::createPointer(bool p1, const std::string &p2, int p3)
{std::unique_ptr<FooPinter> pt= FooPinter::createPointer(p1, p2, p3);return pt.release(); //这里用 release() 而不是 get()
}
b.接收并转换(c++)
const int Module1::checkPointer(void *pt)
{if (pt == NULL) {qCritical() << "传入的指针为空!";return -1;}auto pt2 = reinterpret_cast<FooPinter *>(pt); //类型转换qDebug() << pt2 ->p1() << pt2 ->p2();return 0;
}
c.释放(c++)
const int Module1::releasePointer(void *pt)
{if (pt == NULL) {qCritical() << "传入的指针为空!";return -1;}auto pt2 = reinterpret_cast<FooPinter *>(pt); //类型转换delete pt2; //释放指针空间return 0;
}
d.方法/参数绑定(c++, pybind11)
py::class_<Module1>(v, "Module1").def(py::init<>()).def("createPointer",&Module1::createPointer,py::arg("p1"),py::arg("p2"),py::arg("p3"),"Create the pointer.").def("checkPointer",&Module1::checkPointer,py::arg("pt"),"Check the pointer.").def("releasePointer",&Module1::releasePointer,py::arg("pt"),"Release the pointer.");
e.调用(python)
。。。
pt = md1.createPointer(p1, p2, p3)
。。。
md1.checkPointer(pt)
。。。
md1.releasePointer(pt)
(4)字节数组处理
a.读取并设置(python)
buff = Path('123.jpg').read_bytes() # bytes buffer
#
imageInfo1 = md1.ImageInfo()
。。。
imageInfo1.setBytes(buff)
。。。
b.方法/参数绑定(c++)
py::class_<ImageInfo>(v, "ImageInfo").def(py::init<>()).def("setBytes", &ImageInfo::setBytes, py::arg("bytes"), py::arg("len"), "setBytes").def("setBytes", [](ImageInfo &self, py::buffer buff) {py::buffer_info info = buff.request();const uchar *ptr = static_cast<const uchar *>(info.ptr);int len = info.size * info.itemsize;//self.setBytes(ptr, len);});
c.接收并设置(c++)
void ImageInfo::setBytes(const uchar *bytes, int len)
{m_image = QImage::fromData(bytes, len);
}
(5)构造 QApplication 实例(c++)
。。。
namespace py = pybind11;
static std::unique_ptr<QApplication> global_qt_app;
。。。
PYBIND11_MODULE(Module1, v)
{if (!QCoreApplication::instance()) {static int fake_argc = 0;global_qt_app = std::make_unique<QApplication>(fake_argc, nullptr);}v.doc() = "Object-oriented bindings for the Module1.";
。。。
【完】
