初探OSG+OpenCascade(简称:OCC)在QT上的实践之模型装配体树
商业软件读取STP/STEP文件时候,一般会有一个树形栏显示这个文件的装配体,比如,使用开源的FreeCAD时,打开一个STP/STEP文件,会出现这样的一个装配体树:
可以清晰的看见这个文件模型的装配组合状况,于是,也想子读入STP/STEP文件时,也显示一个装配体树,这个在Opencascade中利用OCAF框架下的XDE能获取模型的装配结构、颜色、名称等信息。再结合QT的qtreewidget就能显示出来了。
不过有一些不同的是,使用XDE直接读取的装配结构与我们通常所见的装配结构是不一样,我下面这段代码是直接展示XDE的显示结构:
///
// XCAF 树形栏
class OCAFBrowser
{
public:
OCAFBrowser(Handle(TDocStd_Document) h)
: pDoc(h)
{
myGroupIcon = QApplication::style()->standardIcon(QStyle::SP_DirIcon);
TDataStd::IDList(myList);
myList.Append(TDataStd_TreeNode::GetDefaultTreeID());
myList.Append(TDataStd_Integer::GetID());
myList.Append(TDocStd_Owner::GetID());
myList.Append(TNaming_NamedShape::GetID());
myList.Append(TNaming_UsedShapes::GetID());
myList.Append(XCAFDoc_Color::GetID());
myList.Append(XCAFDoc_ColorTool::GetID());
myList.Append(XCAFDoc_LayerTool::GetID());
myList.Append(XCAFDoc_ShapeTool::GetID());
myList.Append(XCAFDoc_ShapeMapTool::GetID());
myList.Append(XCAFDoc_Location::GetID());
}
void load(QTreeWidget*);
private:
void load(const TDF_Label& label, QTreeWidgetItem* item, const QString&);
std::string toString(const TCollection_ExtendedString& extstr) const
{
char* str = new char[extstr.LengthOfCString() + 1];
extstr.ToUTF8CString(str);
std::string text(str);
delete[] str;
return text;
}
private:
QIcon myGroupIcon;
TDF_IDList myList;
Handle(TDocStd_Document) pDoc;
};
void OCAFBrowser::load(QTreeWidget* theTree)
{
theTree->clear();
QTreeWidgetItem* root = new QTreeWidgetItem();
root->setText(0, QLatin1String("0"));
root->setIcon(0, myGroupIcon);
theTree->addTopLevelItem(root);
load(pDoc->GetData()->Root(), root, QString::fromLatin1("0"));
}
void OCAFBrowser::load(const TDF_Label& label, QTreeWidgetItem* item, const QString& s)
{
Handle(TDataStd_Name) name;
if (label.FindAttribute(TDataStd_Name::GetID(), name)) {
QString text = QString::fromLatin1("%1 %2").arg(s).arg(QString::fromUtf8(toString(name->Get()).c_str()));
item->setText(0, text);
}
for (TDF_ListIteratorOfIDList it(myList); it.More(); it.Next()) {
Handle(TDF_Attribute) attr;
if (label.FindAttribute(it.Value(), attr)) {
QTreeWidgetItem* child = new QTreeWidgetItem();
if (it.Value() == TDataStd_Name::GetID()) {
QString text;
QTextStream str(&text);
str << attr->DynamicType()->Name();
str << " = " << toString(Handle(TDataStd_Name)::DownCast(attr)->Get()).c_str();
child->setText(0, text);
item->addChild(child);
}
else if (it.Value() == TDF_TagSource::GetID()) {
QString text;
QTextStream str(&text);
str << attr->DynamicType()->Name();
str << " = " << Handle(TDF_TagSource)::DownCast(attr)->Get();
child->setText(0, text);
item->addChild(child);
}
else if (it.Value() == TDataStd_Integer::GetID()) {
QString text;
QTextStream str(&text);
str << attr->DynamicType()->Name();
str << " = " << Handle(TDataStd_Integer)::DownCast(attr)->Get();
child->setText(0, text);
item->addChild(child);
}
else if (it.Value() == TNaming_NamedShape::GetID()) {
TopoDS_Shape shape = Handle(TNaming_NamedShape)::DownCast(attr)->Get();
QString text;
QTextStream str(&text);
str << attr->DynamicType()->Name() << " = ";
if (!shape.IsNull()) {
switch (shape.ShapeType()) {
case TopAbs_COMPOUND:
str << "COMPOUND PRIMITIVE";
break;
case TopAbs_COMPSOLID:
str << "COMPSOLID PRIMITIVE";
break;
case TopAbs_SOLID:
str << "SOLID PRIMITIVE";
break;
case TopAbs_SHELL:
str << "SHELL PRIMITIVE";
break;
case TopAbs_FACE:
str << "FACE PRIMITIVE";
break;
case TopAbs_WIRE:
str << "WIRE PRIMITIVE";
break;
case TopAbs_EDGE:
str << "EDGE PRIMITIVE";
break;
case TopAbs_VERTEX:
str << "VERTEX PRIMITIVE";
break;
case TopAbs_SHAPE:
str << "SHAPE PRIMITIVE";
break;
}
}
child->setText(0, text);
item->addChild(child);
}
else {
child->setText(0, QLatin1String(attr->DynamicType()->Name()));
item->addChild(child);
}
}
}
int i = 1;
for (TDF_ChildIterator it(label); it.More(); it.Next(), i++) {
QString text = QString::fromLatin1("%1:%2").arg(s).arg(i);
QTreeWidgetItem* child = new QTreeWidgetItem();
child->setText(0, text);
child->setIcon(0, myGroupIcon);
item->addChild(child);
load(it.Value(), child, text);
}
}
///
显示效果和eryar老师的这篇博客是一样的(图片直接借用eryar老师的这篇博客:http://www.cppblog.com/eryar/archive/2018/07/29/XDE.html):
很明显的是,XDE读取出来的装配体树和我们所见的不一样,集中表现在子节点不一定在父节点下,虽然可以通过前面的标号确认关系。因此,如果需要实现这个装配体树,需要做一些小修改,在遍历节点的时候需要像树的深度遍历方式那样递归查找直到没有孩子节点。下方这段代码是群友写的,很有帮助:
//
void Read_Assembly::ReadFile(QString aFilePath,Assemly_Tree & AssemlyTree)
{
STEPCAFControl_Reader reader;
reader.SetColorMode(true);
reader.SetNameMode(true);
IFSelect_ReturnStatus status = reader.ReadFile(aFilePath.toUtf8());
Handle(XCAFApp_Application) anApp = XCAFApp_Application::GetApplication();
Handle(TDocStd_Document) doc;
anApp->NewDocument("MDTV-XCAF", doc);
bool yes = reader.Transfer(doc);
if (yes)
{
TDF_Label mainLabel = doc->Main();
Handle(XCAFDoc_ShapeTool) ShapeTool = XCAFDoc_DocumentTool::ShapeTool(mainLabel);
Handle(XCAFDoc_ColorTool) ColorTool = XCAFDoc_DocumentTool::ColorTool(mainLabel);
{
TDF_LabelSequence tdfLabels;
ShapeTool->GetFreeShapes(tdfLabels); //获取装配体和组件对应名称
int Roots = tdfLabels.Length();
Assemly_Node * RootNode = AssemlyTree.GetRootNode();
for (int index = 1; index <= Roots; index++)
{
TDF_Label Label = tdfLabels.Value(index);
Assemly_Data AssemlyData = GetData(ShapeTool, ColorTool, Label, TopLoc_Location());
Assemly_Node * Node = AssemlyTree.AddNode(RootNode, AssemlyData);
MakeTree(ShapeTool, ColorTool, Label, TopLoc_Location(), Node, AssemlyTree);
}
}
}
}
oid Read_Assembly::MakeTree(const Handle(XCAFDoc_ShapeTool)& ShapeTool,
const Handle(XCAFDoc_ColorTool)& ColorTool,
const TDF_Label & Label,
TopLoc_Location Location,
Assemly_Node * ParentNode,
Assemly_Tree & Tree)
{
TDF_LabelSequence components;
if (ShapeTool->GetComponents(Label, components))
{
for (Standard_Integer compIndex = 1; compIndex <= components.Length(); ++compIndex)
{
TDF_Label ChildLabel = components.Value(compIndex);
if (ShapeTool->IsReference(ChildLabel))
{
TDF_Label ShapeLabel;
if (ShapeTool->GetReferredShape(ChildLabel, ShapeLabel))
{
TopLoc_Location LocalLocation = Location * ShapeTool->GetLocation(ChildLabel);
Assemly_Data AssemlyData = GetData(ShapeTool, ColorTool, ShapeLabel, LocalLocation);
Assemly_Node * Node = Tree.AddNode(ParentNode, AssemlyData);
MakeTree(ShapeTool, ColorTool, ShapeLabel, LocalLocation, Node, Tree);
}
}
}
}
}
///稍微改造下就能用qttreewidget显示出来了。我们直接看下效果,直接在我之前那个OCCAD程序下显示效果如下(左侧我的,右侧FreeCAD):
可见,二者显示的装配体结构是一致的。总体还是实现了装配体的正确显示。
如有任何问题,请您加我QQ:1623451686。恳请指正和交流。