项目树是切换模型场景的索引,根据创建的项目树,用户可选择性显示某一部分内容,也可叠加显示一个大的场景,通过UI界面上的组织来控制数据层面的显示。所以项目树的创建与展示,直接影响着视图层面的显示组织,创建项目树也显的尤为重要。
BIMBase平台项目树分UI层面及数据层面的树,UI层面主要负责面板上树的展示,数据层面主要负责相应树及节点数据储存。UI层面树与数据层面树是相结合使用,控制面板树列表显示及视图中内容显示。UI的相关接口类在BPProjectUITreeManager类中,提供了树节点相应操作接口,数据层面接口类集中在BPTree、BPTreeNode、BPTreeManager中。文件中主要的接口如表8-2所示。
表8-2 UI显示项目树接口列表
方法 | 描述 |
---|---|
HandleTreeItem append(string,string, handleTreeItem ) | 增加一个节点 |
bool remove(handleTreeItem ) | 删除一个节点 |
bool remove(wstring) | 删除一个节点 |
void extend(HandleTreeItem) | 展开树节点 |
void extend(wstring) | 展开树节点 |
void collapse(wstring) | 折叠起树节点 |
void collapse(HandleTreeItem) | 折叠起树节点 |
HandleTreeItem findByID(const long long) | 根据节点ID找到节点 |
HandleTreeItem findByName(const wchar_t*) | 找到某一名字的节点 |
void clear() | 清除项目树 |
HandleTreeItem getTreeRootItem() | 获取树根节点Item |
void registerMsgReactionFunction (ProjectUIMsgFunType) | 回调函数,通过注册获取鼠标点击事件 |
bool reName(HandleTreeItem , wstring) | 重命名某一节点 |
void select(wstring) | 选中某一节点 |
void setImage(int, int, wstring) | 给树节点设置图片 |
表8-3 数据层项目树接口列表
方法 | 描述 | |
---|---|---|
BPTreeManager | BPTreeOperateStatus removeTreeByTreeId(const long long) | 根据指定树Id在当前工程文件中删除对应的树 |
BPTreeP getTreeByTreeId(const long long) | 根据ID获取树 | |
BPTreeOperateStatus loadBimBaseTreeFromFile(); | 加载当前工程文件中的BPTree数据并生成缓存 | |
BPTreeP createTree(BPTreeNodeR) | 以指定节点作为根节点创建树 | |
BPTreeNodeP transformNode (long long,long long,long long,long long) | 将某树中的某个节点移动到目标节点,使目标节点成为被移动节点的父节点 | |
BPTree | BPTreeNodeP getRootNode() | 获取根节点 |
BPTreeNodeP insertNode(BPTreeNodeCR, const long long) | 在BPTree中以指定节点为父节点插入节点 | |
BPTreeOperateStatus deleteNodeById(const long long) | 在BPTree中删除指定Id对应节点 | |
BPTreeNodeP getNodeById(long long) | 根据ID获取节点 | |
long long getTreeId() | 获取当前树ID | |
std::wstring getName() | 获取当前树的名称 | |
BPTreeNode | long long getNodeId() | 获取当前节点ID |
long long getTreeId() | 获取当前树ID | |
long long getParentNodeId() | 获取父节点ID | |
BPTreeNodeP getParentNode() | 获取父节点 | |
BPTreeOperateStatus setName(std::wstring | 设置节点名称 | |
std::wstring getName() | 获取节点名称 | |
BPTreeOperateStatus attachResource(std::wstring) | 节点附加资源,将用户自定义字符串作为资源附加到节点,并支持持久化存储 | |
BPTreeOperateStatus detachResource(std::wstring) | 节点去除资源 | |
BPTreeOperateStatus clearResourceVec() | 节点清空资源 | |
std::vector |
获取节点资源集合 | |
BPTreeOperateStatus setReserverdData(const byte* , size_t) | 设置节点预留数据 | |
byte* getReservedData() | 获取节点预留数据 | |
BPTreeNodePtr deepClone(BPTreeNodeCR) | 深拷贝一个节点,拷贝内容只限于节点类型、资源等数据,不包括节点Id、深度、父子节点信息;新节点与源节点数据独立 | |
BPTreeNodePtr shallowCloneWithId(BPTreeNodeCR) | 浅拷贝一个节点,并使用相同Id,拷贝内容只限于节点类型、资源等数据,不包括深度、父子节点信息;新节点与源节点Id相同,数据共享 | |
BPTreeNodePtr createNode() | 创建一个空节点 | |
BPTreeOperateStatus updateInProject(::BIMBase::Core::BPProjectR) | 将对从工程文件中取出的节点的修改更新到工程文件中 | |
size_t getDepth() | 获取当前节点深度 |
代码片段8-2展示了调用所列接口创建项目树的过程。
//项目树事件行为的定义
#define UI_PROJECT_TREE_MSG_LEFTCLICK L"LeftClick"
#define UI_PROJECT_TREE_MSG_RIGHTCLICK L"RightClick"
#define UI_PROJECT_TREE_MSG_DBLCLICK L"DblClick"
#define IDM_CREATE_NODE 12001
//当前项目树的ID
static long long s_CurrentTreeIdDemo;
static int g_TreeNodeCount = 1;
void ProjectTreeDemo::traverseTreeNodeAndAdd(const BPTreeNodeP& tTree, int depth, BIMBase::FrameWork::HandleTreeItem& ssptri)
{
if (depth < 1)
return;
for (auto node : BPTreeNodeCollection(*tTree, depth))
{
if (node->getParentNodeId() != tTree->getNodeId())
continue;
g_TreeNodeCount++;
BIMBase::FrameWork::HandleTreeItem ptrSub = BIMBase::FrameWork::BPProjectUITreeManager::append(node->getName().c_str(), node->getName().c_str(), ssptri, node->getNodeId());
traverseTreeNodeAndAdd(node, node->getDepth(), ptrSub);
}
}
void ProjectTreeDemo::projectTreeRefresh()
{
BIMBase::FrameWork::BPProjectUITreeManager::clear();
//注册后,后续可以获取左击、右击、双击响应消息
BIMBase::FrameWork::BPProjectUITreeManager::registerMsgReactionFunction(ProjectTreeDemo::onProjectManagerMsg);
//项目树名称
std::wstring sTreeName = _T("DemoTree");
bool bHaveProjectTree = false;
BIMBase::Core::IBPProjectManagerP pProjectManager = BIMBase::Core::BPApplication::getInstance().getProjectManager();
if (pProjectManager == nullptr)
return;
BPProjectP project = pProjectManager->getMainProject();
if (project == nullptr)
return;
//若是若有同名树则选择第一个树
for (auto tree : BPTreeCollection(*project))//遍历树
{
std::vector<std::wstring> wsVecResource = tree->getRootNode()->getResourceVec();
for (auto it : wsVecResource)
{
CString csTemp = it.c_str();
if (csTemp.Find(sTreeName.c_str()) >= 0)
{
BPTreeP mainTree = tree;
if (mainTree == nullptr)
continue;
bHaveProjectTree = true;
s_CurrentTreeIdDemo = mainTree->getTreeId();
break;
}
}
}
if (!bHaveProjectTree)//创建默认项目树
{
vector<std::wstring> resource;
resource.push_back(sTreeName);
BPTreeNodePtr pNode = BPTreeNode::createNode();
if (pNode.isNull())
return;
pNode->setName(_T("工程项目"));
pNode->setNoteDataType(BIMBase::Data::BPTreeNodeType::enRootNode);
for (auto res : resource)
{
pNode->attachResource(res);
}
BPTreeP pTree = BPTreeManager::getInstance(project).createTree(*pNode);
if (pTree == nullptr)
return;
s_CurrentTreeIdDemo = pTree->getTreeId();
}
//填充树
BPTreeP curTree = BPTreeManager::getInstance(project).getTreeByTreeId(s_CurrentTreeIdDemo);
if (curTree == nullptr)
return;
BIMBase::FrameWork::HandleTreeItem ptrMain = BIMBase::FrameWork::BPProjectUITreeManager::append(curTree->getName().c_str(), curTree->getName().c_str(),BIMBase::FrameWork::BPProjectUITreeManager::getTreeRootItem(),curTree->getRootNode()->getNodeId());
traverseTreeNodeAndAdd(curTree->getRootNode(),curTree->getRootNode()->getDepth(), ptrMain);
BIMBase::FrameWork::BPProjectUITreeManager::extend(ptrMain);
BIMBase::FrameWork::BPProjectUITreeManager::setImageList(nullptr);
}
代码8-2:接口创建项目树
在该代码创建项目树片段中,projectTreeRefresh
函数是刷新项目树,在刷新的时候会判断是否存在项目树。如果已经存在,则需要获取已有的项目树,并且展示出来,如果没有项目树,则创建一个项目树。在创建项目树的过程中,先是在数据层面创建一个根节点,根据根节点创建一个树,然后再到UI层面去显示创建的树。在显示的过程中,采用了递归的方式,即traverseTreeNodeAndAdd
函数,循环把所有的节点都显示到面板项目树中。在递归循环的过程中,循环是先往深度进行,再往下进行,例如工程项目下面有场景1及场景2,场景1下有场景3,场景2下有场景4,在进行树节点迭代的时候,是先把场景1下所有的子节点遍历完,再到场景2下遍历。
在项目树初始化另一个比较重要的点,就是需要注册回调函数,此注册在projectTreeRefresh
函数实现,用来响应左键、右键、双击等事件,这样一来就可以在界面上响应鼠标事件完成相应的逻辑了。上述事例中调用接口registerMsgReactionFunction
即是注册自己定义的事件响应函数,传入的参数是回调函数的名称。
上面的代码片段,我们增加了一个项目树,并且注册了鼠标响应事件。代码片段8-3展示了鼠标事件具体响应后的逻辑,平台提供的鼠标响应事件关键字为“RightClick
”、“DbClick
”、“LeftClick
”,分别代表右键点击、双击、左键点击等事件。根据不同的事件我们可以完成不同的逻辑,范例中示例了右键及双击响应的用法。在右键响应的时候,弹出右键菜单,里面填充上创建节点。在双击节点的时候,完成切换不同的model的功能。
void ProjectTreeDemo::onProjectManagerMsg(wchar_t const* msgType, wchar_t const* text, wchar_t const* name, BIMBase::FrameWork::HandleTreeItem hdl)
{
BPProjectP project = BPProject::getActiveProject();
if (project == nullptr)
return;
int nNodeId = BIMBase::FrameWork::BPProjectUITreeManager::getItemData(hdl);
BPTreeP curTree = BPTreeManager::getInstance(project).getTreeByTreeId(s_CurrentTreeIdDemo);
if (curTree == nullptr)
return;
BPTreeNodeP pCurrentSelectNode = curTree->getNodeById(nNodeId);
if (pCurrentSelectNode == nullptr)
return;
if (0 == wcscmp(UI_PROJECT_TREE_MSG_RIGHTCLICK, msgType))
{
createRightKeyMenu(text, pCurrentSelectNode, curTree);
}
else if (0 == wcscmp(UI_PROJECT_TREE_MSG_DBLCLICK, msgType))
{
onDbClickFunction(pCurrentSelectNode);
}
return;
}
void ProjectTreeDemo::createRightKeyMenu(wchar_t const* text, BPTreeNodeP& pSelectNode, BPTreeP& pTree)
{
if (pSelectNode == nullptr)
return;
if (pTree == nullptr)
return;
BIMBase::Core::IBPProjectManagerP pProjectManager = BIMBase::Core::BPApplication::getInstance().getProjectManager();
if (pProjectManager == nullptr)
return;
BPProjectPtr project = pProjectManager->getMainProject();
if (project.isNull())
return;
CPoint cp;
GetCursorPos(&cp);
CMenu pMenu;
VERIFY(pMenu.CreatePopupMenu());
pMenu.AppendMenuW(MF_STRING, IDM_CREATE_NODE, _T("创建节点"));
CWnd* cwnd = AfxGetMainWnd();
int retID = pMenu.TrackPopupMenu(TPM_LEFTALIGN | TPM_RETURNCMD | TPM_NONOTIFY, cp.x, cp.y, cwnd);
if (retID == IDM_CREATE_NODE)
{
BPTreeNodePtr pNode = BPTreeNode::createNode();
if (!pNode)
return;
std::wstring strName = std::to_wstring(g_TreeNodeCount);
strName = _T("场景") + strName;
pNode->setName(strName);
pNode->setNoteDataType(BIMBase::Data::
BPTreeNodeType::enScene);
BPTreeNodeP newNodeP = pTree->insertNode(*pNode, pSelectNode->getNodeId());
if (newNodeP == nullptr)
return;
BIMBase::FrameWork::HandleTreeItem itemFather = BIMBase::FrameWork::BPProjectUITreeManager::findByName(text);
BIMBase::FrameWork::HandleTreeItem ptrSub = BIMBase::FrameWork::BPProjectUITreeManager::
append(newNodeP->getName().c_str(), newNodeP->getName().c_str(), itemFather,newNodeP->getNodeId());
BIMBase::FrameWork::BPProjectUITreeManager::extend(itemFather);
//给相应节点创建Model
P3DStatus status;
p3d::Utf8String modelName = "";
modelName.sprintf("newModel%d", g_TreeNodeCount);
g_TreeNodeCount++;
p3d::platform::P3DModelType modelType = p3d::platform::P3DModelType::enPhysical;
BPModelPtr newModel = project->createNewModel(status, modelName, modelType, true);
if (newModel.isNull())
return;
Int32 nModelId = newModel->getModelId().m_id;
std::wstring strId = std::to_wstring(nModelId);
vector<std::wstring> resource;
resource.push_back(strId);
for (auto res : resource)
{
pNode->attachResource(res);
}
newNodeP->updateInProject(*project);
BPViewManagerR mg = BPViewManager::getInstance();
UInt32 nIndex = mg.getActiveIndex();
//显示创建的model
BPViewManager::getInstance().displayModelOnViewPort(nModelId, nIndex);
}
}
void ProjectTreeDemo::onDbClickFunction(BPTreeNodeP& pSelectNode)
{
if (pSelectNode == nullptr)
return;
std::vector<std::wstring> strVct = pSelectNode->getResourceVec();
if (strVct.size() == 0)
return;
Int32 Id = _wtoi(strVct[0].c_str());
BPViewManagerR mg = BPViewManager::getInstance();
UInt32 nIndex = mg.getActiveIndex();
BPViewManager::getInstance().displayModelOnViewPort(Id, nIndex);
}
代码8-3:项目树鼠标响应
上述代码中,onProjectManagerMsg
是我们注册的回调函数,当项目树点击左键、右键、双击等,会响应到此函数。在此函数中,我们根据传过来的鼠标消息,分别创建了右键菜单及双击切换项目树节点功能。创建右键菜单(createRightKeyMenu
)中的创建节点功能,数据层面会先创建一个节点,然后增加到当前项目树中,同时会创建一个model,model可以理解为放置构件的容器,该modelID存于节点附加资源,用于model与项目树节点绑定,这样切换到不同的节点时,将会展示对应model上不同的模型。最后再根据新创建的数据节点在UI面板上进行展示,新创建的节点会变为当前选中节点。双击响应函数(onDbClickFunction
)中,传入的是当前双击的节点,根据双击的节点,可以获取到节点中储存的modelID,把此model显示到视图中,即完成了节点双击切换的功能。
最后在程序中得到项目树的效果如下图所示

图8-7 项目树效果