ARTICLE DETAIL

资讯详情

深耕商务建站与企业官网运营的一线实战洞察。

FreeCAD 视图提供者(View Provider)对象完全指南:GUI 显示层架构与开发实践

FreeCAD 视图提供者(View Provider)对象完全指南:GUI 显示层架构与开发实践 FreeCAD 视图提供者View Provider对象完全指南GUI 显示层架构与开发实践【免费下载链接】FreeCADOfficial source code of FreeCAD, a free and opensource multiplatform 3D parametric modeler.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeCAD本篇技术指南以 FreeCAD 官方 Sphinx 文档源 src/Doc/sphinx/ViewProvider.rst 为核心骨架系统讲解 FreeCAD GUI 架构中承载 3D 视图与树视图显示的ViewProvider视图提供者对象。读者将掌握 ViewProvider 在 App/Gui 分层架构中的定位、其基于 Coin3D 场景图的核心节点结构与显示模式机制、选择处理、树视图交互、编辑模式以及 Python 侧的编程接口并能结合仓库源码理解如何为自定义对象实现自己的视图提供者。一、什么是 ViewProviderGUI 层与数据层之间的桥FreeCAD 采用严格的数据App/界面Gui分离架构App层的 DocumentObject 只负责几何数据与文档结构本身不关心对象在屏幕上的样子而ViewProvider则是Gui层中所有可视化内容的通用接口。在 src/Gui/ViewProvider.h 的类注释中明确写道General interface for all visual stuff in FreeCAD. This class is used to generate and handle all around visualizing and presenting objects from the FreeCAD App layer to the user. This class and its descendents have to be implemented for any object type in order to show them in the 3DView and TreeView.即任何对象类型要想在 3D 视图3DView和树视图TreeView中显示都必须为它实现 ViewProvider 及其子类。文档 src/Doc/sphinx/ViewProvider.rst 通过 Sphinx 的automodule/autoclass指令将Gui::ViewProvider的完整成员自动生成为 Python API 参考文档是开发者查阅 ViewProvider 编程接口的官方入口该文档被挂接在 src/Doc/sphinx/index.rst 的 Python API 目录树中。ViewProvider 继承自App::TransactionalObject事务对象这意味着它与 App 层对象一样具备属性Property系统和事务记录能力。其核心职责可归纳为构建并维护该对象在 3D 视图中的 Coin3D 场景图子图响应 App 层属性变化同步刷新显示内容updateData处理鼠标拾取、选择高亮等交互向树视图提供图标、子对象分组、拖放支持管理显示模式线框/着色/隐藏线等与可见性进入/退出编辑模式时接管视图交互。二、核心场景图结构Root、ModeSwitch、Transform 与 AnnotationViewProvider 显示的核心载体是 Coin3DOpen Inventor场景图。在 src/Gui/ViewProvider.h 中可以看到它维护了四个关键节点指针成员类型作用pcRootSoSeparator*ViewProvider 场景图的根分离器被挂接到 3D 视图主场景pcTransformSoTransform*该视图对象的变换节点承载位置/旋转/缩放pcModeSwitchSoSwitch*显示模式开关节点所有不同显示模式的子图都收集于此pcAnnotationSoSeparator*注解根节点如尺寸标注、临时显示元素可为空对外访问接口分别为getRoot()、getModeSwitch()、getTransformNode()、getAnnotation()与getOrCreateAnnotation()。此外还有三个根级访问方法反映了场景图的组织层次getFrontRoot()前置根节点getChildRoot()收集子对象的根节点返回SoGroup*getBackRoot()后置根节点。canAddToSceneGraph()决定该 ViewProvider 是否应加入场景图isPartOfPhysicalObject()决定它是加入对象分组true还是仅加入场景图false。而claimChildren3D()则向 3D 视图交付应被分组到该对象场景图下的子对象直接影响到子对象的可见性与 3D 位置跟随行为。节点生命周期由仓库内自带的SoRefPtrT/CoinPtrT智能指针管理见 src/Gui/ViewProvider.h它们封装了 Coin3D 节点的ref()/unref()引用计数避免悬挂指针。显示模式切换的底层实现hide()与show()的实现见 src/Gui/ViewProvider.cpp直接操作pcModeSwitchvoid ViewProvider::hide() { // ... 通知扩展 if (pcModeSwitch-whichChild.getValue() 0) { pcModeSwitch-whichChild -1; // -1 表示不渲染任何子节点 // ... 通知扩展 modeSwitch 变化 } } void ViewProvider::show() { setModeSwitch(); // 恢复当前模式对应的子节点索引 // ... 通知扩展 show } bool ViewProvider::isShow() const { return pcModeSwitch-whichChild.getValue() ! -1; }可见可见性本质上是把SoSwitch的whichChild设为-1全部隐藏或恢复为实际模式索引。setVisible(bool)与isVisible()分别是这对操作的便捷封装。setModeSwitch()src/Gui/ViewProvider.cpp则负责把开关切到正确分支void ViewProvider::setModeSwitch() { if (viewOverrideMode -1) { pcModeSwitch-whichChild _iActualMode; // 正常模式 } else if (viewOverrideMode pcModeSwitch-getNumChildren()) { pcModeSwitch-whichChild viewOverrideMode; // 覆盖模式如高斯曲率着色 } // ... }这里体现了显示覆盖模式setOverrideMode与实际显示模式getActualMode的分离setOverrideMode(As Is)恢复原状否则在_sDisplayMaskModes映射中查找模式名对应的节点索引并临时切换见 src/Gui/ViewProvider.cpp。三、显示模式系统getDisplayModes / setDisplayMode / DisplayMaskModeViewProvider 提供两组模式机制容易混淆需区分显示模式Display Modes如线框、着色、隐藏线等通过getDisplayModes()返回可用模式名列表setDisplayMode(const char*)切换getActiveDisplayMode()/getDefaultDisplayMode()查询当前与默认模式。基类实现会向所有ViewProviderExtension扩展转发extensionSetDisplayMode/extensionGetDisplayModes见 src/Gui/ViewProvider.cpp子类通常需要重写以处理新模式。显示掩码模式Display Mask Modes主要控制SoSwitch选择不同的显示掩码分支。与显示模式数量不必一一对应——例如高斯曲率、平均曲率、灰度等多个显示模式可能共享同一个处理颜色的掩码分支。相关 API 为addDisplayMaskMode(SoNode*, const char*)、setDisplayMaskMode、getDisplayMaskMode、getDisplayMaskModes()见 src/Gui/ViewProvider.h。此外还有setRenderCacheMode(int)控制渲染缓存以及toggleVisibility()——默认切换自身可见性但可被重写以重定向到其他目标如容器对象由Std_ToggleVisibility命令调用。四、ViewProviderDocumentObject与文档对象绑定的基类实际中绝大多数功能对象的视图提供者继承自Gui::ViewProviderDocumentObject见 src/Gui/ViewProviderDocumentObject.h。它在 ViewProvider 基础上增加了一个指向 App 层对象的pcObject指针、所属 GUI 文档pcDocument并提供attach(App::DocumentObject*)将视图提供者绑定到文档对象首次创建时调用reattach重新绑定getObject()取回关联的 App 对象updateView()/forceUpdate()强制重绘部分视图提供者在隐藏时会跳过视觉更新需要强制刷新startRestoring()/finishRestoring()文档加载时从GuiDocument.xml恢复状态src/Gui/ViewProviderDocumentObject.cpp 中可以看到恢复时先hide()并同步 App 对象的Visibility属性getTreeRank()树视图中的排序层级。内置属性Display Options 与 Selection 分组构造函数src/Gui/ViewProviderDocumentObject.cpp注册了 5 个内置属性分为两组Display Options显示选项组属性类型说明DisplayModePropertyEnumeration显示模式对应视图提供者的模式列表VisibilityPropertyBool是否在 3D 视图中显示该对象ShowInTreePropertyBool是否在树视图中显示该对象Selection选择组属性类型枚举值说明SelectionStylePropertyEnumerationShape、BoundBox对象选择样式按形状拾取或按包围盒拾取OnTopWhenSelectedPropertyEnumerationDisabled、Enabled、Object、Element选中时是否置顶显示Object表示仅当整个对象被选中时置顶Element表示仅当对象某个子元素被选中时置顶这些属性即为属性视图中View标签页外观/显示的底层数据来源。getTaskViewContent()会向任务面板注入TaskAppearance外观编辑框见 src/Gui/ViewProviderDocumentObject.cpp。五、选择处理从拾取点到子元素名ViewProvider 负责把鼠标在 3D 视图中的拾取结果翻译为 FreeCAD 的子元素引用如Face1、Edge5这是选择、测量、约束等一切基于子元素功能的基础。相关方法集中在 src/Gui/ViewProvider.h 的 Selection handling 分组useNewSelectionModel()是否使用新的统一选择模型isSelectable()是否可被选择getElementPicked(const SoPickedPoint*, std::string subname)根据拾取点返回命中元素getElement(const SoDetail*)根据 Coin3D 细节对象返回子元素名getDetail(const char*)反向返回子元素对应的 Coin 节点细节getDetailPath(subname, pPath, append, det)返回指向子元素的 Coin 路径与细节。若该视图提供者链接了其他视图提供者实现还必须追加中间节点直到被链接视图提供者的模式开关getRelatedElements(subname, pickPoint)将一次拾取扩展为一组逻辑相关的子元素例如同一特征上相邻的面默认返回空向量subname如Face1subName为完整子元素引用如InternalFace1getModelPoints()拾取点对应的模型空间坐标getSelectionShape(Element)返回某元素或整个形状的高亮线partialRender(subelements, clear)局部渲染——只渲染指定的子元素集合需场景中存在至少一个SoFCSelectRoot节点cleartrue时移除局部渲染onSelectionChanged(const SelectionChanges)选择变化回调。包围盒查询getBoundingBox(subname, mat, transform, view, depth)无论对象当前是否可见都能工作depth参数用于防止无限递归链接对象场景内部实现为受保护的_getBoundingBox()子类可重写定制。六、树视图交互图标、子对象分组与拖放ViewProvider 同时驱动树视图组合视图中的模型树的表现对应 src/Gui/ViewProvider.h 的分组树表现getIcon()返回树中显示的图标mergeColorfulOverlayIcons()/mergeGreyableOverlayIcons()叠加彩色/可置灰的角标图标如状态标记claimChildren()/claimChildrenRecursive()返回应被分组到该对象标签下的子对象列表分组、装配、链接等对象的典型用法showInTree()是否出现在树中canToggleVisibility()是否允许切换可见性——ToggleVisibilityMode枚举CanToggleVisibility/NoToggleVisibility控制的特性对VarSet、Spreadsheet这类不渲染的对象返回 false见 src/Gui/ViewProvider.h。拖放Drag Drop拖放能力遵循先声明、后实现的约定canDragObjects()/canDropObjects()声明是否支持拖出/拖入canDragObject()/canDropObject()可按对象类型细粒度过滤实际动作由dragObject()/dropObject()完成。跨文档场景使用canDropObjectEx()/dropObjectEx()变体树视图优先调用它们传入完整限定名、父对象owner、子名引用subname与被选中的非对象子元素elements。默认实现在ViewProviderDocumentObject中禁止跨文档拖放重写它才能启用跨文档链接。replaceObject(oldObj, newObj)支持拖放替换返回 1 成功 / 0 未找到 / -1 不支持getDropPrefix()返回承接拖放对象的子对象引用前缀acceptReorderingObjects()决定是否接受拖放时重排子对象。七、编辑模式与任务面板当用户双击对象或执行编辑命令时ViewProvider 进入编辑模式接管视图交互。相关机制见 src/Gui/ViewProvider.h编辑模式EditMode枚举Default 0、Transform、Cutting、Color该枚举被反映到Application.h的userEditModes映射中用户可通过 GUI 选择默认编辑模式setEdit(int ModNum)受保护进入编辑unsetEdit(int ModNum)退出startEditing()/isEditing()/finishEditing()为公开入口setEditViewer()/unsetEditViewer()进入/离开编辑时调整视图设置keyPressed()、mouseMove()、mouseButtonPressed()、mouseWheelEvent()编辑期间的键盘/鼠标事件转发setupContextMenu()构造支持编辑模式的右键菜单doubleClicked()树中双击回调getTransactionText()返回 undo/redo 对话框中显示的事务名返回 null 则不开启事务selectAll()编辑期间响应全选命令返回 false 表示忽略。任务面板getTaskViewContent()返回与该对象关联的TaskView::TaskContent列表用于在任务面板Task panel中呈现参数编辑框。ViewProvider 还通过onDelete(subNames)在删除前征询意见返回 false 可阻止删除beforeDelete()保证在删除前被调用canDelete(App::DocumentObject*)询问其 outlist 中的对象能否被移除。八、Python 侧编程接口ViewProvider.pyiViewProvider 通过getPyObject()暴露给 PythonPython 封装类为ViewProviderPy声明为友元。类型存根 src/Gui/ViewProvider.pyi 完整列出了脚本开发者可用的成员与 C 接口一一对应场景图与外观vp.RootNode # pivy Separator本 ViewProvider 的根节点 vp.SwitchNode # pivy SoSwitch显示模式开关节点 vp.Annotation # pivy Separator用于追加自定义场景图 vp.IV # 整个 ViewProvider 的 Inventor 字符串表示 vp.DefaultMode # 以 coin 节点索引表示的默认显示模式可读写 vp.toString() # 返回 Inventor 节点字符串显示模式vp.addDisplayMode(obj, mode) # obj: coin.SoNodemode: 模式名 vp.listDisplayModes() # 列出全部显示模式 vp.show() / vp.hide() / vp.isVisible() vp.setTransformation(trans) # trans: Base.Placement 或 Base.Matrix选择与拾取vp.getElementPicked(pickPoint) # pickPoint: coin.SoPickedPoint返回拾取子元素 vp.getDetailPath(subelement, path, appendTrue) vp.partialRender(sub, clearFalse) # 局部渲染subNone 时重置 vp.getBoundingBox(subnameNone, transformTrue, viewNone, matNone, depth0)颜色与树vp.getElementColors(elementNameNone) # {elementName:(r,g,b,a)} vp.setElementColors(colors) vp.claimChildren() / claimChildrenRecursive() vp.canDragObject(obj) / dragObject(obj) vp.canDropObject(obj, *, owner, subname, elem) / dropObject(...) vp.replaceObject(oldObj, newObj) vp.doubleClicked() vp.signalChangeIcon() # 触发图标变更信号 vp.Icon / vp.CanRemoveChildrenFromRoot / vp.LinkVisibility vp.ToggleVisibility # ToggleVisibilityMode 枚举其中addProperty/removeProperty/supportedProperties继承自ExtensionContainer允许脚本为视图提供者动态添加属性。这些接口使 FreeCAD 的 Python 宏与工作台代码能够直接操控场景节点、显示模式与选择行为是开发自定义视图提供者脚本的基础。九、扩展机制ViewProviderExtension自基类起ViewProvider 就支持通过ViewProviderExtension见 src/Gui/ViewProviderExtension.h以扩展方式注入行为而无需修改继承链。从 src/Gui/ViewProvider.cpp 的多处实现可以看到这一模式贯穿始终setDisplayMode()遍历所有扩展并调用extensionSetDisplayMode()getDisplayModes()汇总各扩展的extensionGetDisplayModes()hide()/show()/setModeSwitch()中分别调用extensionHide()/extensionShow()/extensionModeSwitchChange()。这样工作台可以在不子类化的前提下通过扩展给现有 ViewProvider 增加显示模式、响应显隐事件实现行为的组合式复用。十、小结与开发建议ViewProvider 是 FreeCAD GUI 架构的枢纽它一头连接 App 层的数据对象另一头连接 Coin3D 场景图与 Qt 树视图并同时向 Python 脚本开放完整接口。官方 API 文档入口即 src/Doc/sphinx/ViewProvider.rst通过 Sphinxautoclass从 C 封装自动生成配合 src/Gui/ViewProvider.h、src/Gui/ViewProvider.cpp 与 src/Gui/ViewProviderDocumentObject.cpp 可以追查每个方法的真实实现。为自定义对象开发视图提供者时建议遵循以下路径继承Gui::ViewProviderDocumentObject在构造函数中调用ADD_PROPERTY_TYPE注册自己的外观属性实现attach()构造 Coin3D 场景子图并挂到getModeSwitch()下重写getDisplayModes()/setDisplayMode()/getDefaultDisplayMode()将模式名映射到SoSwitch子节点索引重写updateData()响应 App 层属性变化getElementPicked()/getDetailPath()提供子元素拾取支持需要特殊树结构时重写claimChildren()与getIcon()需要交互时实现编辑模式与拖放方法若功能可复用优先考虑写成ViewProviderExtension而非强制子类化。【免费下载链接】FreeCADOfficial source code of FreeCAD, a free and opensource multiplatform 3D parametric modeler.项目地址: https://gitcode.com/GitHub_Trending/fr/FreeCAD创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考
返回列表
PREV
查看更多资讯
NEXT
返回资讯列表