尧图网站建设 尧图网络
  • 首页
  • 关于我们
  • 服务项目
  • 案例展示
  • 建站流程
  • 资讯中心
  • 联系我们
首页/资讯中心/详情

【Qt + OpenCASCADE】实现 SolidWorks 风格的装配树(附完整代码)

【Qt + OpenCASCADE】实现 SolidWorks 风格的装配树(附完整代码)
📅 发布时间:2026/7/26 7:38:36

一、目的

实现像 SolidWorks 那样:

  • 左边是结构树,右边是 3D 视图,
  • 选中树节点 → 对应 3D 实体变为亮色(高亮);
  • 勾选/取消勾选树节点 → 对应 3D 实体在视图中显示/消失

二、效果

三、实现

  • QTreeView+QAbstractItemMode:树形结构展示、选中/勾选交互
  • OpenCASCADE (OCCT):STEP 文件读取,STEP 装配结构解析、BRep/装配模型管理、3D 渲染

四、重点代码讲解

a、step文件导入
boolStepPartTreeView::loadStepFile(constQString&filePath,constocc::handle<TDocStd_Document>&doc){clearTree();if(doc.IsNull()){QMessageBox::warning(this,QStringLiteral("错误"),QStringLiteral("XCAF 文档未初始化"));returnfalse;}STEPCAFControl_Reader reader;reader.SetColorMode(true);reader.SetNameMode(true);reader.SetLayerMode(true);IFSelect_ReturnStatus status=reader.ReadFile(filePath.toUtf8().constData());if(status!=IFSelect_RetDone){QMessageBox::warning(this,QStringLiteral("错误"),QStringLiteral("无法读取 STEP 文件"));returnfalse;}if(!reader.Transfer(doc)){QMessageBox::warning(this,QStringLiteral("错误"),QStringLiteral("STEP 数据转换失败"));returnfalse;}occ::handle<XCAFDoc_ShapeTool>shapeTool=XCAFDoc_DocumentTool::ShapeTool(doc->Main());if(!shapeTool.IsNull()){shapeTool->UpdateAssemblies();}m_model->loadDocument(doc);expandToDepth(1);if(!m_context.IsNull()&&!m_model->graph().IsNull()){constQList<int>visibleIds=m_model->visibleGraphNodeIds();for(intid:visibleIds){occ::handle<AIS_InteractiveObject>ais=ensureAIS(id);if(!ais.IsNull()){m_context->Display(ais,AIS_Shaded,-1,false);}}if(!m_view.IsNull()){m_view->FitAll();if(m_occtViewer)m_occtViewer->fill();}m_context->UpdateCurrentViewer();}returntrue;}voidStepPartTreeModel::loadDocument(constocc::handle<TDocStd_Document>&doc){beginResetModel();m_nodes.clear();m_graphIdToNodeIndex.clear();m_rootNodeIndex=-1;m_doc=doc;if(!m_doc.IsNull()){m_graph=newXCAFDoc_AssemblyGraph(m_doc);constTColStd_PackedMapOfInteger&roots=m_graph->GetRoots();QString rootName=QStringLiteral("装配体");for(TColStd_PackedMapOfInteger::Iteratorit(roots);it.More();it.Next()){QString n=getShapeName(m_graph->GetNode(it.Key()));if(!n.isEmpty()){rootName=n;break;}}m_rootNodeIndex=appendNode(rootName,FeatNodeKind::RootDoc,-1,-1);for(TColStd_PackedMapOfInteger::Iteratorit(roots);it.More();it.Next()){buildAssemblyBranch(it.Key(),m_rootNodeIndex);}}else{m_graph.Nullify();}endResetModel();}
b、选中高亮
voidStepPartTreeView::onSelectionChanged(constQItemSelection&,constQItemSelection&){clearHighlight();constQModelIndexList sel=selectionModel()->selectedRows();QList<int>ids;for(constQModelIndex&idx:sel){intnodeIdx=static_cast<int>(idx.internalId());QList<int>subIds;m_model->collectShapeGraphIds(nodeIdx,subIds);constQList<int>&subIdsConst=subIds;for(intid:subIdsConst){if(!ids.contains(id))ids.append(id);}if(subIds.isEmpty()){inteffId=m_model->effectiveGraphNodeId(nodeIdx);if(effId>0&&!ids.contains(effId))ids.append(effId);}}if(ids.isEmpty()){if(!m_context.IsNull())m_context->UpdateCurrentViewer();return;}constQList<int>&idsConst=ids;for(intid:idsConst){highlightNode(id);}intfirstId=ids.first();TDF_Label label=m_model->labelFromGraphId(firstId);if(!m_context.IsNull())m_context->UpdateCurrentViewer();emitnodeSelected(firstId,label);}
c、勾选显隐
voidStepPartTreeModel::setNodeVisibleRecursive(intnodeIndex,boolvisible){if(nodeIndex<0||nodeIndex>=m_nodes.size())return;NodeInfo&info=m_nodes[nodeIndex];if(info.visible!=visible){info.visible=visible;QModelIndex idx=createIndex(info.rowInParent,0,quintptr(nodeIndex));emitdataChanged(idx,idx,{Qt::CheckStateRole,IsVisibleRole});}constQVector<int>&children=info.childrenIndex;for(intchild:children)setNodeVisibleRecursive(child,visible);}voidStepPartTreeView::onModelDataChanged(constQModelIndex&topLeft,constQModelIndex&,constQList<int>&roles){if(!roles.contains(Qt::CheckStateRole))return;if(!topLeft.isValid())return;QList<int>ids;intnodeIdx=static_cast<int>(topLeft.internalId());m_model->collectShapeGraphIds(nodeIdx,ids);if(ids.isEmpty()){inteffId=m_model->effectiveGraphNodeId(nodeIdx);if(effId>0)ids.append(effId);}boolvisible=topLeft.data(IsVisibleRole).toBool();if(m_context.IsNull())return;boolchanged=false;constQList<int>&idsConst=ids;for(intid:idsConst){if(m_highlightedNodeIds.contains(id)){if(!visible){autoit=m_aisObjects.find(id);if(it!=m_aisObjects.end()&&!it.value().IsNull()){restoreOriginalColor(it.value());if(m_context->IsDisplayed(it.value())){m_context->Erase(it.value(),false);changed=true;}}m_highlightedNodeIds.remove(id);}continue;}occ::handle<AIS_InteractiveObject>ais;autoit=m_aisObjects.find(id);if(it!=m_aisObjects.end()){ais=it.value();}else{ais=ensureAIS(id);}if(ais.IsNull())continue;if(visible){if(!m_context->IsDisplayed(ais)){m_context->Display(ais,AIS_Shaded,-1,false);changed=true;}}else{if(m_context->IsDisplayed(ais)){m_context->Erase(ais,false);changed=true;}}}if(changed)m_context->UpdateCurrentViewer();emitupdateViewRequired();}
d、AIS 对象的"懒惰初始化 + 缓存"
occ::handle<AIS_InteractiveObject>StepPartTreeView::ensureAIS(intgraphNodeId){autoit=m_aisObjects.find(graphNodeId);if(it!=m_aisObjects.end())returnit.value();if(m_model->graph().IsNull()||m_context.IsNull())returnocc::handle<AIS_InteractiveObject>();TDF_Label label=m_model->graph()->GetNode(graphNodeId);if(label.IsNull())returnocc::handle<AIS_InteractiveObject>();occ::handle<AIS_InteractiveObject>ais;TopoDS_Shape shape;try{if(XCAFDoc_ShapeTool::GetShape(label,shape)&&!shape.IsNull()){}else{shape=getShapeRecursive(label);}}catch(...){shape=getShapeRecursive(label);}if(!shape.IsNull()){try{occ::handle<AIS_Shape>aisShape=newAIS_Shape(shape);aisShape->SetDisplayMode(AIS_Shaded);m_context->Display(aisShape,AIS_Shaded,-1,false);if(m_context->IsDisplayed(aisShape)){ais=aisShape;m_context->Erase(ais,false);}else{m_context->Remove(aisShape,false);}}catch(...){}}if(ais.IsNull()){try{occ::handle<XCAFPrs_AISObject>xcafAis=newXCAFPrs_AISObject(label);xcafAis->SetDisplayMode(AIS_Shaded);m_context->Display(xcafAis,AIS_Shaded,-1,false);if(m_context->IsDisplayed(xcafAis)){ais=xcafAis;m_context->Erase(ais,false);}else{m_context->Remove(xcafAis,false);}}catch(...){}}if(!ais.IsNull()){m_aisObjects[graphNodeId]=ais;}returnais;}
e、视图初始化
voidOcctViewerWidget::showEvent(QShowEvent*event){// 延迟初始化if(!m_isCreated){initOcctViewer();}}voidOcctViewerWidget::initOcctEnv(){occ::handle<OpenGl_GraphicDriverFactory>factory=newOpenGl_GraphicDriverFactory();Graphic3d_GraphicDriverFactory::RegisterFactory(factory,true);occ::handle<Graphic3d_GraphicDriverFactory>driverFactory=Graphic3d_GraphicDriverFactory::DefaultDriverFactory();if(driverFactory.IsNull())return;m_displayConnection=newAspect_DisplayConnection();m_driver=driverFactory->CreateDriver(m_displayConnection);if(m_driver.IsNull())return;// 创建视图管理器m_viewer=newV3d_Viewer(m_driver);m_viewer->SetDefaultViewSize(1000.0);m_viewer->SetDefaultViewProj(V3d_XposYnegZpos);m_viewer->SetComputedMode(true);m_viewer->SetDefaultComputedMode(true);m_viewer->SetDefaultLights();m_viewer->SetLightOn();// 创建交互上下文m_context=newAIS_InteractiveContext(m_viewer);m_context->SetPixelTolerance(10);// 设置选择像素差// 创建(内部)视图m_view=m_context->CurrentViewer()->CreateView();}voidOcctViewerWidget::initOcctViewer(){// m_occtWindow = new OcctWindow(this);WId window_handle=(WId)winId();m_occtWindow=newWNT_Window((Aspect_Handle)window_handle);m_view->SetWindow(m_occtWindow);if(!m_occtWindow->IsMapped()){m_occtWindow->Map();}m_view->SetBackgroundColor(Quantity_NOC_BLACK);m_view->MustBeResized();m_view->Camera()->SetProjectionType(Graphic3d_Camera::Projection_Orthographic);showViewCube();m_isCreated=true;isometric();fill();}

项目源码

Windows 环境 OCCT 8.0 编译构建及与 Qt6 项目集成

对你有用就点个赞👍,以后需要用到就收藏⭐

相关新闻

  • TI AM62L WKUP_PLL0时钟系统配置详解与实战
  • 【毕业设计】基于 Django 的二手电子产品发布交易系统 轻量化二手电子设备交易与信息展示平台(源码+文档+远程调试,全bao定制等)
  • 保定水电改造哪家施工规范 - 中媒介

最新新闻

  • Android V4L2 `devm_kmalloc` 功能解释
  • C++与OpenCV实战:从零构建视频运动检测系统
  • 2026年无锡系统门窗厂家怎么选?5家本地工厂实测对比,一站式门窗定制配套更适配家装工装 - 海棠依旧大
  • CC115L射频设计实战:晶体振荡器与PCB布局的精准设计与避坑指南
  • C++里氏替换原则:面向对象设计的基石与实战指南
  • FastAPI+Docker构建AI微服务:金融问答机器人实战架构

日新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

周新闻

  • 大连理工大学与东京大学联手打造的“主动型AI助手“
  • 170.2026年国家级科研瓶颈:超精密单点金刚石切削(SPDT)光学表面生成
  • SongBloom:革命性歌曲生成框架深度解析——如何通过交织自回归与扩散模型创作完整音乐

月新闻

  • 2026年6月公司网站搭建最新热门渠道测评:四大低成本/零代码平台对比+避坑
  • 【Linux】Linux arm 编译QT程序,出现expected “}“报错
  • 【MATLAB例程】四基站二维AOA定位与距离辅助增强对比仿真。基于角度观测和测距修正的固定目标平面定位精度分析

关于尧图

  • 公司简介
  • 团队介绍
  • 企业文化
  • 荣誉资质

服务项目

  • 定制开发
  • 电商建站
  • UI 设计
  • 运维服务

快速链接

  • 案例展示
  • 建站流程
  • 常见问题
  • 资讯中心

联系方式

  • 📍北京市朝阳区互联网产业园 A 座 10 层
  • 📞400-888-8888
  • ✉️contact@rkmt.cn
  • 🕐周一至周日 9:00-21:00

© 2024 北京尧图网络科技有限公司 版权所有 | 京 ICP 备 XXXXXXXX 号