FreeCAD中对象属性的Python访问机制
2021/5/21 22:24:58
本文主要是介绍FreeCAD中对象属性的Python访问机制,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!
FreeCAD中对象属性的Python访问机制
济南友泉软件有限公司
在Part模块一节中,对Part模块的主要组件以及原理进行了较为详细的介绍。但有一个遗留问题,即Python脚本中是如何实现对Part::Feature及其子类的属性访问的呢?Part::Face继承自Part::Feature,因此,可以通过Part::Face为例来说明Python中Part::Face相关属性的访问过程。
一、Python C扩展的原理
在剖析Part::Face代码实现实现之前,需要先要了解一下Python C扩展的原理,具体来说是使用CPython API来扩展Python类型。本节以Part::PartFeaturePy为例先说明Python扩展类型的注册方法。
使用CPython扩展Python类型,实质是通过CPython API向CPython注册类型相关的信息,这是PyTypeObject这个结构体来描述的,
/// Type structure of PartFeaturePy PyTypeObject PartFeaturePy::Type = { PyVarObject_HEAD_INIT(&PyType_Type,0) "Part.Feature", /*tp_name*/ sizeof(PartFeaturePy), /*tp_basicsize*/ 0, /*tp_itemsize*/ /* methods */ PyDestructor, /*tp_dealloc*/ 0, /*tp_print*/ 0, /*tp_getattr*/ 0, /*tp_setattr*/ 0, /*tp_compare*/ __repr, /*tp_repr*/ 0, /*tp_as_number*/ 0, /*tp_as_sequence*/ 0, /*tp_as_mapping*/ 0, /*tp_hash*/ 0, /*tp_call */ 0, /*tp_str */ __getattro, /*tp_getattro*/ __setattro, /*tp_setattro*/ /* --- Functions to access object as input/output buffer ---------*/ 0, /* tp_as_buffer */ /* --- Flags to define presence of optional/expanded features */ #if PY_MAJOR_VERSION >= 3 Py_TPFLAGS_BASETYPE|Py_TPFLAGS_DEFAULT, /*tp_flags */ #else Py_TPFLAGS_DEFAULT, /*tp_flags */ #endif "This is the father of all shape object classes", /*tp_doc */ 0, /*tp_traverse */ 0, /*tp_clear */ 0, /*tp_richcompare */ 0, /*tp_weaklistoffset */ 0, /*tp_iter */ 0, /*tp_iternext */ Part::PartFeaturePy::Methods, /*tp_methods */ 0, /*tp_members */ Part::PartFeaturePy::GetterSetter, /*tp_getset */ &App::GeoFeaturePy::Type, /*tp_base */ 0, /*tp_dict */ 0, /*tp_descr_get */ 0, /*tp_descr_set */ 0, /*tp_dictoffset */ __PyInit, /*tp_init */ 0, /*tp_alloc */ Part::PartFeaturePy::PyMake,/*tp_new */ 0, /*tp_free Low-level free-memory routine */ 0, /*tp_is_gc For PyObject_IS_GC */ 0, /*tp_bases */ 0, /*tp_mro method resolution order */ 0, /*tp_cache */ 0, /*tp_subclasses */ 0, /*tp_weaklist */ 0, /*tp_del */ 0 /*tp_version_tag */ #if PY_MAJOR_VERSION >= 3 ,0 /*tp_finalize */ #endif };
从上面的代码可以看出,PartFeaturePy注册了Base::PyObjectBase::__getattro()与Base::PyObjectBase::__setattro()来获取与设置属性。实际上,当在Python脚本中通过”.”运算符访问Python扩展类型时,调用的就是注册的这两个函数。
二、Part::Face
2.1 获取PartFeaturePy
当在Python脚本中创建Part::Feature对象时,
face1 = App.ActiveDocument.addObject('Part::Face', 'Face1')
由于App.ActiveDocument.addObject()实际关联到DocumentPy::addObject(),而DocumentPy::addObject()则是通过调用getPyObject()返回关联的Python扩展类型。
在FreeCAD中,虽然没有提供与Part::Face直接对应的Python扩展类型,但是通过继承自Part::Feature,Part::Face关联到了Part::PartFeaturePy这个Python扩展类型。
namespace App { /// @cond DOXERR PROPERTY_SOURCE_TEMPLATE(Part::FeaturePython, Part::Feature) template<> const char* Part::FeaturePython::getViewProviderName(void) const { return "PartGui::ViewProviderPython"; } template<> PyObject* Part::FeaturePython::getPyObject(void) { if (PythonObject.is(Py::_None())) { // ref counter is set to 1 PythonObject = Py::Object(new FeaturePythonPyT<Part::PartFeaturePy>(this),true); } return Py::new_reference_to(PythonObject); } /// @endcond // explicit template instantiation template class PartExport FeaturePythonT<Part::Feature>; }
2.2 访问属性
从前面的分析可知,如果访问Part::Feature的Sources属性,则会出发调用Base::PyObjectBase::__getattro()函数,最终会调用PropertyContainerPy::getCustomAttributes()这一非虚函数,
PyObject *PropertyContainerPy::getCustomAttributes(const char* attr) const { // search in PropertyList if(FC_LOG_INSTANCE.level()>FC_LOGLEVEL_TRACE) { FC_TRACE("Get property " << attr); } Property *prop = getPropertyContainerPtr()->getPropertyByName(attr); if (prop) { PyObject* pyobj = prop->getPyObject(); if (!pyobj && PyErr_Occurred()) { // the Python exception is already set throw Py::Exception(); } return pyobj; } else if (Base::streq(attr, "__dict__")) { // get the properties to the C++ PropertyContainer class std::map<std::string,App::Property*> Map; getPropertyContainerPtr()->getPropertyMap(Map); PyObject *dict = PyDict_New(); if (dict) { for ( std::map<std::string,App::Property*>::iterator it = Map.begin(); it != Map.end(); ++it ) #if PY_MAJOR_VERSION >= 3 PyDict_SetItem(dict, PyUnicode_FromString(it->first.c_str()), PyUnicode_FromString("")); #else PyDict_SetItem(dict, PyString_FromString(it->first.c_str()), PyString_FromString("")); #endif if (PyErr_Occurred()) { Py_DECREF(dict); dict = NULL; } } return dict; } else if(Base::streq(attr,"Shape") && getPropertyContainerPtr()->isDerivedFrom(App::DocumentObject::getClassTypeId())) { // Special treatment of Shape property static PyObject *_getShape = 0; if(!_getShape) { _getShape = Py_None; PyObject *mod = PyImport_ImportModule("Part"); if(!mod) { PyErr_Clear(); } else { Py::Object pyMod = Py::asObject(mod); if(pyMod.hasAttr("getShape")) _getShape = Py::new_reference_to(pyMod.getAttr("getShape")); } } if(_getShape != Py_None) { Py::Tuple args(1); args.setItem(0,Py::Object(const_cast<PropertyContainerPy*>(this))); auto res = PyObject_CallObject(_getShape, args.ptr()); if(!res) PyErr_Clear(); else { Py::Object pyres(res,true); if(pyres.hasAttr("isNull")) { Py::Callable func(pyres.getAttr("isNull")); if(!func.apply().isTrue()) return Py::new_reference_to(res); } } } } return 0; }
从上面的代码可以看出,正是在这个函数完成根据属性名映射到了属性扩展类型。
参考资料
- PyCXX
- PyCXX Python3
- FreeCADWeb
这篇关于FreeCAD中对象属性的Python访问机制的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!
- 2024-11-24Python编程基础详解
- 2024-11-21Python编程基础教程
- 2024-11-20Python编程基础与实践
- 2024-11-20Python编程基础与高级应用
- 2024-11-19Python 基础编程教程
- 2024-11-19Python基础入门教程
- 2024-11-17在FastAPI项目中添加一个生产级别的数据库——本地环境搭建指南
- 2024-11-16`PyMuPDF4LLM`:提取PDF数据的神器
- 2024-11-16四种数据科学Web界面框架快速对比:Rio、Reflex、Streamlit和Plotly Dash
- 2024-11-14获取参数学习:Python编程入门教程