PySide6+Python C API:继承QRasterWindow调用metric触发EXC_BAD_ACCESS错误
环境信息
- Python版本:3.9.6 (default, May 7 2023, 23:32:45)
- PySide6版本:6.7.0
问题现象
- 直接创建
QRasterWindow实例并调用metric方法时运行正常:
Py_Initialize(); PyObject * GUI = PyImport_ImportModule("PySide6.QtGui"); if(!GUI) { printf("\nCould not import PySide6.QtGui module.\n"); return 0; } // QApplication PyObject* QGA = PyObject_GetAttrString(GUI, "QGuiApplication"); PyObject * argsqa = PyTuple_New (1); PyObject * listqa = PyList_New (1); PyList_SetItem (listqa, 0, PyUnicode_FromString ("")); PyTuple_SetItem (argsqa, 0, listqa); PyObject * res = PyObject_CallObject(QGA, argsqa); // QRasterWindow PyObject* QRW = PyObject_GetAttrString(GUI, "QRasterWindow"); if(QRW) { // Create instance PyObject * args = PyTuple_New (0); PyObject * QRW_inst = PyObject_CallObject(QRW, args); // Call metric PyObject * mm = PyObject_GetAttrString(QRW_inst, "metric"); PyObject * uu = PyObject_GetAttrString(GUI, "QPaintDevice"); PyObject * ww = PyObject_GetAttrString(uu, "PdmWidth"); PyObject * argsm = PyTuple_New (1); PyTuple_SetItem (argsm, 0, ww); PyObject * res = PyObject_CallObject(mm, argsm); int val = PyLong_AsLong(res); printf("\nQRasterWindow.metric value is %d \n", val); } else { printf("\nCould not get QRW class.\n"); }
- 但创建继承自
QRasterWindow的PCJ类,实例化后调用metric方法时触发段错误EXC_BAD_ACCESS:
// Create PCJ class inherited from QRasterWindow PyType_Slot slots[] = { { Py_tp_doc, "PGJ" }, { Py_tp_base, QRW }, { Py_tp_init, QRW_init }, { 0 }, }; PyType_Spec spec = { "PCJ", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }; PyTypeObject *PCJ = (PyTypeObject *)PyType_FromSpec(&spec); printf("\nPCJ value is %d \n", PCJ); // Create PCJ instance PyObject * argsj = PyTuple_New (0); PyObject * PCJ_inst = PyObject_CallObject((PyObject *) PCJ, argsj); printf("\nPCJ_inst value is %d \n", PCJ_inst); // Call metric PyObject * mm = PyObject_GetAttrString(PCJ_inst, "metric"); printf("\nmm value is %d \n", mm); PyObject * uu = PyObject_GetAttrString(GUI, "QPaintDevice"); printf("\nuu value is %d \n", uu); PyObject * ww = PyObject_GetAttrString(uu, "PdmWidth"); printf("\nww value is %d \n", ww); PyObject * argsm = PyTuple_New (1); PyTuple_SetItem (argsm, 0, ww); PyObject * resj = PyObject_CallObject(mm, argsm); // segmentation fault printf("\nresj value is %d \n", resj); int valj = PyLong_AsLong(resj); printf("\nPCJ.metric value is %d \n", valj);
问题原因及解决方法
问题核心在于你自定义的PCJ类没有正确完成Qt底层C++对象的初始化流程:
- 错误指定了
Py_tp_init:你设置的QRW_init函数如果没有调用父类QRasterWindow的初始化逻辑,会导致PCJ实例对应的Qt底层对象根本没被创建,调用metric方法时自然会访问无效内存触发段错误。 - 类型未完成初始化:创建自定义类型后没有调用
PyType_Ready,导致类型内部结构未完全就绪。
修复方案:
方案1:去掉自定义初始化逻辑(推荐,无特殊需求时用)
直接删除{ Py_tp_init, QRW_init }这个slot,让Python自动调用父类的初始化方法:
// Create PCJ class inherited from QRasterWindow PyType_Slot slots[] = { { Py_tp_doc, "PGJ" }, { Py_tp_base, QRW }, { 0 }, }; PyType_Spec spec = { "PCJ", 0, 0, Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE, slots }; PyTypeObject *PCJ = (PyTypeObject *)PyType_FromSpec(&spec); // 关键:确保类型准备就绪 if (PyType_Ready(PCJ) < 0) { printf("\nFailed to initialize PCJ type.\n"); return 0; } // 注意:打印指针要用%p,别用%d printf("\nPCJ value is %p \n", PCJ);
方案2:保留自定义初始化(需正确调用父类逻辑)
如果一定要自定义初始化函数,必须确保调用父类的__init__:
static int QRW_init(PyObject *self, PyObject *args, PyObject *kwds) { // 调用父类的初始化方法 PyTypeObject *base_type = (PyTypeObject*)Py_TYPE(self)->tp_base; if (base_type->tp_init != NULL) { int res = base_type->tp_init(self, args, kwds); if (res < 0) return res; } // 这里加你的自定义初始化逻辑 return 0; } // 后续创建类型的代码中保留Py_tp_init slot,同时必须调用PyType_Ready
另外,代码中用%d打印指针地址是错误的,统一换成%p才能正确显示指针值,避免输出混乱。
内容的提问来源于stack exchange,提问作者Blady
相关产品推荐
相关产品推荐

