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

📅 2026/7/26 7:38:36 👁️ 阅读次数 📝 编程学习
【Qt + OpenCASCADE】实现 SolidWorks 风格的装配树(附完整代码)

一、目的

实现像 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 项目集成

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