FreeCAD中数据显示流程
济南友泉软件有限公司
目录
Open Inventor(以下简称OIV)是SGI公司使用C++编写的基于OpenGL的面向对象三维图形软件开发包。使用OIV开发包,程序员可以快速、简洁地开发出各种类型的交互式三维图形软件。OIV具有平台无关性,它可以在Microsoft Windows、Unix、Linux等多种操作系统中使用。OIV允许使用C、C++、Java、DotNet多种编程语言进行程序开发。经过多年的发展,OIV已经基本上成为面向对象的3D图形开发“事实上”的工业标准。广泛地应用于机械工程设计与仿真、医学和科学图像、地理科学、石油钻探、虚拟现实、科学数据可视化等领域。
OIV目前主要由三家公司负责维护,相应的有三个主要的版本。SGI最早提出并开发OIV的UNIX版本;TGS公司最早将OIV由Unix系统移植到Microsoft Windows下的公司;SIM公司开发的Coin3D OIV可以同时在UNIX和Microsoft Windows下使用,Coin3D OIV免费版本的使用协议采用的是GPL协议,但如果要在商业软件使用Coin3D,商业用户需要每年支付一笔很少的开发费用。
目前,FreeCAD使用的是Coin3D OIV这个版本。
由于OIV采用C++面向对象的编程思想进行设计,因此相对于直接调用OpenGL C API来说,使用OIV开发图形图像软件将会更加简单而且开发效率更高。
使用OIV编写应用程序比较简单,其要点是根据需要把景物节点组装成景物(Scene),并设置景物节点的事件及其响应。
- 景物与节点
OIV使用层次化的树状结构来存储模型数据,其中的节点称之为景物节点(SoNode及其子类);而景物(Scene)则是由若干景物节点组成的;景物图(Scene)则指场景中景物的集合。
SoCube |
立方体 |
|
|
|
|
- 行为
行为(SoAction及其子类)主要用来渲染景物。
SoGLRenderAction |
应用GL图形库对景物图进行渲染 |
SoGetBoundingBoxAction |
在景物途中计算一个对象的三维包围盒 |
SoWriteAction |
将景物图写入文件 |
SoHandleEventAction |
处理事件 |
SoRayPickAction |
沿直线选择对象 |
SoCallbackAction |
调用回调函数 |
- 事件
事件(SoEvent及其子类)用来表示键盘、鼠标等外部设备对景物的操作。
SoButtonEvent |
|
SoLocation2Event |
|
SoMotion3Event |
|
- 传感器
传感器(SoSensor及其子类)用于检测各类事件,当有事件发生时便会调用注册的对应回调函数。
- 引擎
引擎(SoEngine及其子类)用于景物运动模拟,适合于物体运动、机械结构关节点等应用场景。
虚基类SoBase不仅提供了类型管理功能。而且实现了基于引用计数的内存管理。每个对象都有一个引用计数的变量,只要它被使用过一次,引用计数就加1,不再使用时,引用计数就减1,如果引用计数变成0,那么这个对象就自动被删除。
通过下面的代码可以直观地了解OIV程序开发的流程,
#include <Inventor/Qt/SoQt.h>#include <Inventor/Qt/viewers/SoQtExaminerViewer.h>#include <Inventor/nodes/SoSeparator.h>#include <Inventor/nodes/SoCube.h>#include <Inventor/nodes/SoMaterial.h>int main(int, char **argv){//初始化InventorQWidget *myWindow = SoQt::init(argv[0]);//创建观察器SoQtExaminerViewer *myViewer = new SoQtExaminerViewer(myWindow);//创建场景SoSeparator *root = new SoSeparator;SoMaterial *myMaterial = new SoMaterial;myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);//红色root->addChild(myMaterial);root->addChild(new SoCube);//增加上一个立方体//观察器和场景相关联myViewer->setSceneGraph(root);//显示主窗口myViewer->show();SoQt::show(myWindow);// 主循环SoQt::mainLoop();return 0;}#include <Inventor/Qt/SoQt.h> #include <Inventor/Qt/viewers/SoQtExaminerViewer.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoCube.h> #include <Inventor/nodes/SoMaterial.h> int main(int, char **argv) { //初始化Inventor QWidget *myWindow = SoQt::init(argv[0]); //创建观察器 SoQtExaminerViewer *myViewer = new SoQtExaminerViewer(myWindow); //创建场景 SoSeparator *root = new SoSeparator; SoMaterial *myMaterial = new SoMaterial; myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);//红色 root->addChild(myMaterial); root->addChild(new SoCube);//增加上一个立方体 //观察器和场景相关联 myViewer->setSceneGraph(root); //显示主窗口 myViewer->show(); SoQt::show(myWindow); // 主循环 SoQt::mainLoop(); return 0; }#include <Inventor/Qt/SoQt.h> #include <Inventor/Qt/viewers/SoQtExaminerViewer.h> #include <Inventor/nodes/SoSeparator.h> #include <Inventor/nodes/SoCube.h> #include <Inventor/nodes/SoMaterial.h> int main(int, char **argv) { //初始化Inventor QWidget *myWindow = SoQt::init(argv[0]); //创建观察器 SoQtExaminerViewer *myViewer = new SoQtExaminerViewer(myWindow); //创建场景 SoSeparator *root = new SoSeparator; SoMaterial *myMaterial = new SoMaterial; myMaterial->diffuseColor.setValue(1.0, 0.0, 0.0);//红色 root->addChild(myMaterial); root->addChild(new SoCube);//增加上一个立方体 //观察器和场景相关联 myViewer->setSceneGraph(root); //显示主窗口 myViewer->show(); SoQt::show(myWindow); // 主循环 SoQt::mainLoop(); return 0; }
程序运行结果如下:
App::Document采用层次化的树状结构存储文档数据,每个文档节点称之为文档对象(App::DocumentObjectObject)。
App::DocumentObjectObject包含描述对象的数据,提供了对象数据持久化的功能,同时采用Boost库中信号-槽机制用于管理数据更新操作的相应。
ViewProvider定义了各种数据在View3Dinventor(派生于MDIView)、TreeView中显示的接口。对于View3Dinventor,主要完成模型数据转换成OIV渲染数据结构的功能;对于TreeView,主要定义了节点图标、双击响应等方面的接口。
View3DInventor通过其内部View3DInventorViewer来完成在Qt窗口内使用OIV渲染几何文档数据。
- 设置景物图
为了显示模型,仅需要调用setSceneGraph()指定需要显示的所有景物的根节点。
void View3DInventorViewer::setSceneGraph(SoNode *root);
本节以Part模块中Part::Box类型文档对象来分析文档对象的创建、显示过程。
当在点击菜单[Part/Primitives/Cube]或者直接在工具栏上点击Cube图标,便会调用CmdPartBox::activated()函数,
void CmdPartBox::activated(int iMsg){Q_UNUSED(iMsg);QString cmd;cmd = qApp->translate("CmdPartBox","Cube");openCommand((const char*)cmd.toUtf8());runCommand(Doc,"App.ActiveDocument.addObject(\\"Part::Box\\",\\"Box\\")");cmd = QString::fromLatin1("App.ActiveDocument.ActiveObject.Label = \\"%1\\"").arg(qApp->translate("CmdPartBox","Cube"));runCommand(Doc,cmd.toUtf8());commitCommand();updateActive();runCommand(Gui, "Gui.SendMsgToActiveView(\\"ViewFit\\")");}void CmdPartBox::activated(int iMsg) { Q_UNUSED(iMsg); QString cmd; cmd = qApp->translate("CmdPartBox","Cube"); openCommand((const char*)cmd.toUtf8()); runCommand(Doc,"App.ActiveDocument.addObject(\\"Part::Box\\",\\"Box\\")"); cmd = QString::fromLatin1("App.ActiveDocument.ActiveObject.Label = \\"%1\\"") .arg(qApp->translate("CmdPartBox","Cube")); runCommand(Doc,cmd.toUtf8()); commitCommand(); updateActive(); runCommand(Gui, "Gui.SendMsgToActiveView(\\"ViewFit\\")"); }void CmdPartBox::activated(int iMsg) { Q_UNUSED(iMsg); QString cmd; cmd = qApp->translate("CmdPartBox","Cube"); openCommand((const char*)cmd.toUtf8()); runCommand(Doc,"App.ActiveDocument.addObject(\\"Part::Box\\",\\"Box\\")"); cmd = QString::fromLatin1("App.ActiveDocument.ActiveObject.Label = \\"%1\\"") .arg(qApp->translate("CmdPartBox","Cube")); runCommand(Doc,cmd.toUtf8()); commitCommand(); updateActive(); runCommand(Gui, "Gui.SendMsgToActiveView(\\"ViewFit\\")"); }
在这个函数中,会调用App::Document::addObject()来创建Part::Box类型的文档对象。
在App::Document::addObject()中,会根据传入的对象文档类型创建对象文件,然后分别触发signalNewObject、signalActivatedObject等信号。
boost::signals2::signal<void(const App::DocumentObject&)> App::Document::signalNewObject;boost::signals2::signal<void(const App::DocumentObject&)> App::Document:: signalActivatedObject;DocumentObject * Document::addObject(const char* sType, const char* pObjectName, bool isNew){Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true));string ObjectName;if (!base)return 0;if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) {delete base;std::stringstream str;str << "'" << sType << "' is not a document object type";throw Base::TypeError(str.str());}App::DocumentObject* pcObject = static_cast<App::DocumentObject*>(base);pcObject->setDocument(this);// do no transactions if we do a rollback!if (!d->rollback) {// Undo stuffif (d->activeUndoTransaction)d->activeUndoTransaction->addObjectDel(pcObject);}// get Unique nameif (pObjectName && pObjectName[0] != '\\0')ObjectName = getUniqueObjectName(pObjectName);elseObjectName = getUniqueObjectName(sType);d->activeObject = pcObject;// insert in the name mapd->objectMap[ObjectName] = pcObject;// cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument())pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first);// insert in the vectord->objectArray.push_back(pcObject);// insert in the adjacence list and reference through the ConectionMap//_DepConMap[pcObject] = add_vertex(_DepList);// If we are restoring, don't set the Label object now; it will be restored later. This is to avoid potential duplicate// label conflicts later.if (!d->StatusBits.test(Restoring))pcObject->Label.setValue( ObjectName );// Call the object-specific initializationif (!d->undoing && !d->rollback && isNew) {pcObject->setupObject ();}// mark the object as new (i.e. set status bit 2) and send the signalpcObject->setStatus(ObjectStatus::New, true);signalNewObject(*pcObject);// do no transactions if we do a rollback!if (!d->rollback && d->activeUndoTransaction) {signalTransactionAppend(*pcObject, d->activeUndoTransaction);}signalActivatedObject(*pcObject);// return the Objectreturn pcObject;}boost::signals2::signal<void(const App::DocumentObject&)> App::Document::signalNewObject; boost::signals2::signal<void(const App::DocumentObject&)> App::Document:: signalActivatedObject; DocumentObject * Document::addObject(const char* sType, const char* pObjectName, bool isNew) { Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true)); string ObjectName; if (!base) return 0; if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) { delete base; std::stringstream str; str << "'" << sType << "' is not a document object type"; throw Base::TypeError(str.str()); } App::DocumentObject* pcObject = static_cast<App::DocumentObject*>(base); pcObject->setDocument(this); // do no transactions if we do a rollback! if (!d->rollback) { // Undo stuff if (d->activeUndoTransaction) d->activeUndoTransaction->addObjectDel(pcObject); } // get Unique name if (pObjectName && pObjectName[0] != '\\0') ObjectName = getUniqueObjectName(pObjectName); else ObjectName = getUniqueObjectName(sType); d->activeObject = pcObject; // insert in the name map d->objectMap[ObjectName] = pcObject; // cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument()) pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first); // insert in the vector d->objectArray.push_back(pcObject); // insert in the adjacence list and reference through the ConectionMap //_DepConMap[pcObject] = add_vertex(_DepList); // If we are restoring, don't set the Label object now; it will be restored later. This is to avoid potential duplicate // label conflicts later. if (!d->StatusBits.test(Restoring)) pcObject->Label.setValue( ObjectName ); // Call the object-specific initialization if (!d->undoing && !d->rollback && isNew) { pcObject->setupObject (); } // mark the object as new (i.e. set status bit 2) and send the signal pcObject->setStatus(ObjectStatus::New, true); signalNewObject(*pcObject); // do no transactions if we do a rollback! if (!d->rollback && d->activeUndoTransaction) { signalTransactionAppend(*pcObject, d->activeUndoTransaction); } signalActivatedObject(*pcObject); // return the Object return pcObject; }boost::signals2::signal<void(const App::DocumentObject&)> App::Document::signalNewObject; boost::signals2::signal<void(const App::DocumentObject&)> App::Document:: signalActivatedObject; DocumentObject * Document::addObject(const char* sType, const char* pObjectName, bool isNew) { Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(sType,true)); string ObjectName; if (!base) return 0; if (!base->getTypeId().isDerivedFrom(App::DocumentObject::getClassTypeId())) { delete base; std::stringstream str; str << "'" << sType << "' is not a document object type"; throw Base::TypeError(str.str()); } App::DocumentObject* pcObject = static_cast<App::DocumentObject*>(base); pcObject->setDocument(this); // do no transactions if we do a rollback! if (!d->rollback) { // Undo stuff if (d->activeUndoTransaction) d->activeUndoTransaction->addObjectDel(pcObject); } // get Unique name if (pObjectName && pObjectName[0] != '\\0') ObjectName = getUniqueObjectName(pObjectName); else ObjectName = getUniqueObjectName(sType); d->activeObject = pcObject; // insert in the name map d->objectMap[ObjectName] = pcObject; // cache the pointer to the name string in the Object (for performance of DocumentObject::getNameInDocument()) pcObject->pcNameInDocument = &(d->objectMap.find(ObjectName)->first); // insert in the vector d->objectArray.push_back(pcObject); // insert in the adjacence list and reference through the ConectionMap //_DepConMap[pcObject] = add_vertex(_DepList); // If we are restoring, don't set the Label object now; it will be restored later. This is to avoid potential duplicate // label conflicts later. if (!d->StatusBits.test(Restoring)) pcObject->Label.setValue( ObjectName ); // Call the object-specific initialization if (!d->undoing && !d->rollback && isNew) { pcObject->setupObject (); } // mark the object as new (i.e. set status bit 2) and send the signal pcObject->setStatus(ObjectStatus::New, true); signalNewObject(*pcObject); // do no transactions if we do a rollback! if (!d->rollback && d->activeUndoTransaction) { signalTransactionAppend(*pcObject, d->activeUndoTransaction); } signalActivatedObject(*pcObject); // return the Object return pcObject; }
在Gui::Document中,可以看到信号signalNewObject关联到了Gui::Document::slotNewObject,即
Document::Document(App::Document* pcDocument,Application * app){d = new DocumentP;d->_iWinCount = 1;// new instanced->_iDocId = (++_iDocCount);d->_isClosing = false;d->_isModified = false;d->_pcAppWnd = app;d->_pcDocument = pcDocument;d->_editViewProvider = 0;// Setup the connectionsd->connectNewObject = pcDocument->signalNewObject.connect(boost::bind(&Gui::Document::slotNewObject, this, _1));d->connectDelObject = pcDocument->signalDeletedObject.connect(boost::bind(&Gui::Document::slotDeletedObject, this, _1));d->connectCngObject = pcDocument->signalChangedObject.connect(boost::bind(&Gui::Document::slotChangedObject, this, _1, _2));d->connectRenObject = pcDocument->signalRelabelObject.connect(boost::bind(&Gui::Document::slotRelabelObject, this, _1));d->connectActObject = pcDocument->signalActivatedObject.connect(boost::bind(&Gui::Document::slotActivatedObject, this, _1));d->connectActObjectBlocker = boost::signals2::shared_connection_block(d->connectActObject, false);d->connectSaveDocument = pcDocument->signalSaveDocument.connect(boost::bind(&Gui::Document::Save, this, _1));d->connectRestDocument = pcDocument->signalRestoreDocument.connect(boost::bind(&Gui::Document::Restore, this, _1));d->connectStartLoadDocument = App::GetApplication().signalStartRestoreDocument.connect(boost::bind(&Gui::Document::slotStartRestoreDocument, this, _1));d->connectFinishLoadDocument = App::GetApplication().signalFinishRestoreDocument.connect(boost::bind(&Gui::Document::slotFinishRestoreDocument, this, _1));d->connectExportObjects = pcDocument->signalExportViewObjects.connect(boost::bind(&Gui::Document::exportObjects, this, _1, _2));d->connectImportObjects = pcDocument->signalImportViewObjects.connect(boost::bind(&Gui::Document::importObjects, this, _1, _2, _3));d->connectUndoDocument = pcDocument->signalUndo.connect(boost::bind(&Gui::Document::slotUndoDocument, this, _1));d->connectRedoDocument = pcDocument->signalRedo.connect(boost::bind(&Gui::Document::slotRedoDocument, this, _1));d->connectTransactionAppend = pcDocument->signalTransactionAppend.connect(boost::bind(&Gui::Document::slotTransactionAppend, this, _1, _2));d->connectTransactionRemove = pcDocument->signalTransactionRemove.connect(boost::bind(&Gui::Document::slotTransactionRemove, this, _1, _2));// pointer to the python class// NOTE: As this Python object doesn't get returned to the interpreter we// mustn't increment it (Werner Jan-12-2006)_pcDocPy = new Gui::DocumentPy(this);if (App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document")->GetBool("UsingUndo",true)){d->_pcDocument->setUndoMode(1);// set the maximum stack sized->_pcDocument->setMaxUndoStackSize(App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document")->GetInt("MaxUndoSize",20));}}Document::Document(App::Document* pcDocument,Application * app) { d = new DocumentP; d->_iWinCount = 1; // new instance d->_iDocId = (++_iDocCount); d->_isClosing = false; d->_isModified = false; d->_pcAppWnd = app; d->_pcDocument = pcDocument; d->_editViewProvider = 0; // Setup the connections d->connectNewObject = pcDocument->signalNewObject.connect (boost::bind(&Gui::Document::slotNewObject, this, _1)); d->connectDelObject = pcDocument->signalDeletedObject.connect (boost::bind(&Gui::Document::slotDeletedObject, this, _1)); d->connectCngObject = pcDocument->signalChangedObject.connect (boost::bind(&Gui::Document::slotChangedObject, this, _1, _2)); d->connectRenObject = pcDocument->signalRelabelObject.connect (boost::bind(&Gui::Document::slotRelabelObject, this, _1)); d->connectActObject = pcDocument->signalActivatedObject.connect (boost::bind(&Gui::Document::slotActivatedObject, this, _1)); d->connectActObjectBlocker = boost::signals2::shared_connection_block (d->connectActObject, false); d->connectSaveDocument = pcDocument->signalSaveDocument.connect (boost::bind(&Gui::Document::Save, this, _1)); d->connectRestDocument = pcDocument->signalRestoreDocument.connect (boost::bind(&Gui::Document::Restore, this, _1)); d->connectStartLoadDocument = App::GetApplication().signalStartRestoreDocument.connect (boost::bind(&Gui::Document::slotStartRestoreDocument, this, _1)); d->connectFinishLoadDocument = App::GetApplication().signalFinishRestoreDocument.connect (boost::bind(&Gui::Document::slotFinishRestoreDocument, this, _1)); d->connectExportObjects = pcDocument->signalExportViewObjects.connect (boost::bind(&Gui::Document::exportObjects, this, _1, _2)); d->connectImportObjects = pcDocument->signalImportViewObjects.connect (boost::bind(&Gui::Document::importObjects, this, _1, _2, _3)); d->connectUndoDocument = pcDocument->signalUndo.connect (boost::bind(&Gui::Document::slotUndoDocument, this, _1)); d->connectRedoDocument = pcDocument->signalRedo.connect (boost::bind(&Gui::Document::slotRedoDocument, this, _1)); d->connectTransactionAppend = pcDocument->signalTransactionAppend.connect (boost::bind(&Gui::Document::slotTransactionAppend, this, _1, _2)); d->connectTransactionRemove = pcDocument->signalTransactionRemove.connect (boost::bind(&Gui::Document::slotTransactionRemove, this, _1, _2)); // pointer to the python class // NOTE: As this Python object doesn't get returned to the interpreter we // mustn't increment it (Werner Jan-12-2006) _pcDocPy = new Gui::DocumentPy(this); if (App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/Document")->GetBool("UsingUndo",true)){ d->_pcDocument->setUndoMode(1); // set the maximum stack size d->_pcDocument->setMaxUndoStackSize(App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document")->GetInt("MaxUndoSize",20)); } }Document::Document(App::Document* pcDocument,Application * app) { d = new DocumentP; d->_iWinCount = 1; // new instance d->_iDocId = (++_iDocCount); d->_isClosing = false; d->_isModified = false; d->_pcAppWnd = app; d->_pcDocument = pcDocument; d->_editViewProvider = 0; // Setup the connections d->connectNewObject = pcDocument->signalNewObject.connect (boost::bind(&Gui::Document::slotNewObject, this, _1)); d->connectDelObject = pcDocument->signalDeletedObject.connect (boost::bind(&Gui::Document::slotDeletedObject, this, _1)); d->connectCngObject = pcDocument->signalChangedObject.connect (boost::bind(&Gui::Document::slotChangedObject, this, _1, _2)); d->connectRenObject = pcDocument->signalRelabelObject.connect (boost::bind(&Gui::Document::slotRelabelObject, this, _1)); d->connectActObject = pcDocument->signalActivatedObject.connect (boost::bind(&Gui::Document::slotActivatedObject, this, _1)); d->connectActObjectBlocker = boost::signals2::shared_connection_block (d->connectActObject, false); d->connectSaveDocument = pcDocument->signalSaveDocument.connect (boost::bind(&Gui::Document::Save, this, _1)); d->connectRestDocument = pcDocument->signalRestoreDocument.connect (boost::bind(&Gui::Document::Restore, this, _1)); d->connectStartLoadDocument = App::GetApplication().signalStartRestoreDocument.connect (boost::bind(&Gui::Document::slotStartRestoreDocument, this, _1)); d->connectFinishLoadDocument = App::GetApplication().signalFinishRestoreDocument.connect (boost::bind(&Gui::Document::slotFinishRestoreDocument, this, _1)); d->connectExportObjects = pcDocument->signalExportViewObjects.connect (boost::bind(&Gui::Document::exportObjects, this, _1, _2)); d->connectImportObjects = pcDocument->signalImportViewObjects.connect (boost::bind(&Gui::Document::importObjects, this, _1, _2, _3)); d->connectUndoDocument = pcDocument->signalUndo.connect (boost::bind(&Gui::Document::slotUndoDocument, this, _1)); d->connectRedoDocument = pcDocument->signalRedo.connect (boost::bind(&Gui::Document::slotRedoDocument, this, _1)); d->connectTransactionAppend = pcDocument->signalTransactionAppend.connect (boost::bind(&Gui::Document::slotTransactionAppend, this, _1, _2)); d->connectTransactionRemove = pcDocument->signalTransactionRemove.connect (boost::bind(&Gui::Document::slotTransactionRemove, this, _1, _2)); // pointer to the python class // NOTE: As this Python object doesn't get returned to the interpreter we // mustn't increment it (Werner Jan-12-2006) _pcDocPy = new Gui::DocumentPy(this); if (App::GetApplication().GetParameterGroupByPath ("User parameter:BaseApp/Preferences/Document")->GetBool("UsingUndo",true)){ d->_pcDocument->setUndoMode(1); // set the maximum stack size d->_pcDocument->setMaxUndoStackSize(App::GetApplication().GetParameterGroupByPath("User parameter:BaseApp/Preferences/Document")->GetInt("MaxUndoSize",20)); } }
在Document::slotNewObject()函数中,主要完成以下工作:
- 根绝文档对象obj关联的ViewProvider类型,创建派生于ViewProviderDocumentObject的pcProvider,
- pcProvider调用attach()关联文档对象obj
- 将pcProvider添加到所有ViewInventor3D视图中进行显示
void Document::slotNewObject(const App::DocumentObject& Obj){ViewProviderDocumentObject* pcProvider = static_cast<ViewProviderDocumentObject*>(getViewProvider(&Obj));if (!pcProvider) {//Base::Console().Log("Document::slotNewObject() called\\n");std::string cName = Obj.getViewProviderName();if (cName.empty()) {// handle document object with no view provider specifiedBase::Console().Log("%s has no view provider specified\\n", Obj.getTypeId().getName());return;}setModified(true);Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(cName.c_str(),true));if (base) {// type not derived from ViewProviderDocumentObject!!!assert(base->getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId()));pcProvider = static_cast<ViewProviderDocumentObject*>(base);d->_ViewProviderMap[&Obj] = pcProvider;try {// if successfully created set the right name and calculate the view//FIXME: Consider to change argument of attach() to const pointerpcProvider->attach(const_cast<App::DocumentObject*>(&Obj));pcProvider->updateView();pcProvider->setActiveMode();}catch(const Base::MemoryException& e){Base::Console().Error("Memory exception in '%s' thrown: %s\\n",Obj.getNameInDocument(),e.what());}catch(Base::Exception &e){e.ReportException();}#ifndef FC_DEBUGcatch(...){Base::Console().Error("App::Document::_RecomputeFeature(): Unknown exception in Feature \\"%s\\" thrown\\n",Obj.getNameInDocument());}#endif}else {Base::Console().Warning("Gui::Document::slotNewObject() no view provider for the object %s found\\n",cName.c_str());}}if (pcProvider) {std::list<Gui::BaseView*>::iterator vIt;// cycling to all views of the documentfor (vIt = d->baseViews.begin();vIt != d->baseViews.end();++vIt) {View3DInventor *activeView = dynamic_cast<View3DInventor *>(*vIt);if (activeView)activeView->getViewer()->addViewProvider(pcProvider);}// adding to the treesignalNewObject(*pcProvider);// it is possible that a new viewprovider already claims childrenhandleChildren3D(pcProvider);}}void Document::slotNewObject(const App::DocumentObject& Obj) { ViewProviderDocumentObject* pcProvider = static_cast<ViewProviderDocumentObject*>(getViewProvider(&Obj)); if (!pcProvider) { //Base::Console().Log("Document::slotNewObject() called\\n"); std::string cName = Obj.getViewProviderName(); if (cName.empty()) { // handle document object with no view provider specified Base::Console().Log("%s has no view provider specified\\n", Obj.getTypeId().getName()); return; } setModified(true); Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(cName.c_str(),true)); if (base) { // type not derived from ViewProviderDocumentObject!!! assert(base->getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId())); pcProvider = static_cast<ViewProviderDocumentObject*>(base); d->_ViewProviderMap[&Obj] = pcProvider; try { // if successfully created set the right name and calculate the view //FIXME: Consider to change argument of attach() to const pointer pcProvider->attach(const_cast<App::DocumentObject*>(&Obj)); pcProvider->updateView(); pcProvider->setActiveMode(); } catch(const Base::MemoryException& e){ Base::Console().Error("Memory exception in '%s' thrown: %s\\n",Obj.getNameInDocument(),e.what()); } catch(Base::Exception &e){ e.ReportException(); } #ifndef FC_DEBUG catch(...){ Base::Console().Error("App::Document::_RecomputeFeature(): Unknown exception in Feature \\"%s\\" thrown\\n",Obj.getNameInDocument()); } #endif } else { Base::Console().Warning("Gui::Document::slotNewObject() no view provider for the object %s found\\n",cName.c_str()); } } if (pcProvider) { std::list<Gui::BaseView*>::iterator vIt; // cycling to all views of the document for (vIt = d->baseViews.begin();vIt != d->baseViews.end();++vIt) { View3DInventor *activeView = dynamic_cast<View3DInventor *>(*vIt); if (activeView) activeView->getViewer()->addViewProvider(pcProvider); } // adding to the tree signalNewObject(*pcProvider); // it is possible that a new viewprovider already claims children handleChildren3D(pcProvider); } }void Document::slotNewObject(const App::DocumentObject& Obj) { ViewProviderDocumentObject* pcProvider = static_cast<ViewProviderDocumentObject*>(getViewProvider(&Obj)); if (!pcProvider) { //Base::Console().Log("Document::slotNewObject() called\\n"); std::string cName = Obj.getViewProviderName(); if (cName.empty()) { // handle document object with no view provider specified Base::Console().Log("%s has no view provider specified\\n", Obj.getTypeId().getName()); return; } setModified(true); Base::BaseClass* base = static_cast<Base::BaseClass*>(Base::Type::createInstanceByName(cName.c_str(),true)); if (base) { // type not derived from ViewProviderDocumentObject!!! assert(base->getTypeId().isDerivedFrom(Gui::ViewProviderDocumentObject::getClassTypeId())); pcProvider = static_cast<ViewProviderDocumentObject*>(base); d->_ViewProviderMap[&Obj] = pcProvider; try { // if successfully created set the right name and calculate the view //FIXME: Consider to change argument of attach() to const pointer pcProvider->attach(const_cast<App::DocumentObject*>(&Obj)); pcProvider->updateView(); pcProvider->setActiveMode(); } catch(const Base::MemoryException& e){ Base::Console().Error("Memory exception in '%s' thrown: %s\\n",Obj.getNameInDocument(),e.what()); } catch(Base::Exception &e){ e.ReportException(); } #ifndef FC_DEBUG catch(...){ Base::Console().Error("App::Document::_RecomputeFeature(): Unknown exception in Feature \\"%s\\" thrown\\n",Obj.getNameInDocument()); } #endif } else { Base::Console().Warning("Gui::Document::slotNewObject() no view provider for the object %s found\\n",cName.c_str()); } } if (pcProvider) { std::list<Gui::BaseView*>::iterator vIt; // cycling to all views of the document for (vIt = d->baseViews.begin();vIt != d->baseViews.end();++vIt) { View3DInventor *activeView = dynamic_cast<View3DInventor *>(*vIt); if (activeView) activeView->getViewer()->addViewProvider(pcProvider); } // adding to the tree signalNewObject(*pcProvider); // it is possible that a new viewprovider already claims children handleChildren3D(pcProvider); } }
运行结果:
TreeDockWidget通过内嵌的TreeWidget类型的对象来层次化地显示文档对象。
在TreeWidget的构造函数中,关联了新建文档等信号,可以看到App::Application::signalNewDocument信号关联到了Gui::TreeWidget::slotNewDocument。
TreeWidget::TreeWidget(QWidget* parent): QTreeWidget(parent), contextItem(0), fromOutside(false){this->setDragEnabled(true);this->setAcceptDrops(true);this->setDropIndicatorShown(false);this->setRootIsDecorated(false);this->createGroupAction = new QAction(this);this->createGroupAction->setText(tr("Create group..."));this->createGroupAction->setStatusTip(tr("Create a group"));connect(this->createGroupAction, SIGNAL(triggered()),this, SLOT(onCreateGroup()));this->relabelObjectAction = new QAction(this);this->relabelObjectAction->setText(tr("Rename"));this->relabelObjectAction->setStatusTip(tr("Rename object"));#ifndef Q_OS_MACthis->relabelObjectAction->setShortcut(Qt::Key_F2);#endifconnect(this->relabelObjectAction, SIGNAL(triggered()),this, SLOT(onRelabelObject()));this->finishEditingAction = new QAction(this);this->finishEditingAction->setText(tr("Finish editing"));this->finishEditingAction->setStatusTip(tr("Finish editing object"));connect(this->finishEditingAction, SIGNAL(triggered()),this, SLOT(onFinishEditing()));this->skipRecomputeAction = new QAction(this);this->skipRecomputeAction->setCheckable(true);this->skipRecomputeAction->setText(tr("Skip recomputes"));this->skipRecomputeAction->setStatusTip(tr("Enable or disable recomputations of document"));connect(this->skipRecomputeAction, SIGNAL(toggled(bool)),this, SLOT(onSkipRecompute(bool)));this->markRecomputeAction = new QAction(this);this->markRecomputeAction->setText(tr("Mark to recompute"));this->markRecomputeAction->setStatusTip(tr("Mark this object to be recomputed"));connect(this->markRecomputeAction, SIGNAL(triggered()),this, SLOT(onMarkRecompute()));this->searchObjectsAction = new QAction(this);this->searchObjectsAction->setText(tr("Search..."));this->searchObjectsAction->setStatusTip(tr("Search for objects"));connect(this->searchObjectsAction, SIGNAL(triggered()),this, SLOT(onSearchObjects()));// Setup connectionsconnectNewDocument = Application::Instance->signalNewDocument.connect(boost::bind(&TreeWidget::slotNewDocument, this, _1));connectDelDocument = Application::Instance->signalDeleteDocument.connect(boost::bind(&TreeWidget::slotDeleteDocument, this, _1));connectRenDocument = Application::Instance->signalRenameDocument.connect(boost::bind(&TreeWidget::slotRenameDocument, this, _1));connectActDocument = Application::Instance->signalActiveDocument.connect(boost::bind(&TreeWidget::slotActiveDocument, this, _1));connectRelDocument = Application::Instance->signalRelabelDocument.connect(boost::bind(&TreeWidget::slotRelabelDocument, this, _1));QStringList labels;labels << tr("Labels & Attributes");this->setHeaderLabels(labels);// make sure to show a horizontal scrollbar if needed#if QT_VERSION >= 0x050000this->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents);#elsethis->header()->setResizeMode(0, QHeaderView::ResizeToContents);#endifthis->header()->setStretchLastSection(false);// Add the first main labelthis->rootItem = new QTreeWidgetItem(this);this->rootItem->setText(0, tr("Application"));this->rootItem->setFlags(Qt::ItemIsEnabled);this->expandItem(this->rootItem);this->setSelectionMode(QAbstractItemView::ExtendedSelection);#if QT_VERSION >= 0x040200// causes unexpected drop events (possibly only with Qt4.1.x)this->setMouseTracking(true); // needed for itemEntered() to work#endifthis->statusTimer = new QTimer(this);connect(this->statusTimer, SIGNAL(timeout()),this, SLOT(onTestStatus()));connect(this, SIGNAL(itemEntered(QTreeWidgetItem*, int)),this, SLOT(onItemEntered(QTreeWidgetItem*)));connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)),this, SLOT(onItemCollapsed(QTreeWidgetItem*)));connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)),this, SLOT(onItemExpanded(QTreeWidgetItem*)));connect(this, SIGNAL(itemSelectionChanged()),this, SLOT(onItemSelectionChanged()));this->statusTimer->setSingleShot(true);this->statusTimer->start(300);documentPixmap = new QPixmap(Gui::BitmapFactory().pixmap("Document"));}TreeWidget::TreeWidget(QWidget* parent) : QTreeWidget(parent), contextItem(0), fromOutside(false) { this->setDragEnabled(true); this->setAcceptDrops(true); this->setDropIndicatorShown(false); this->setRootIsDecorated(false); this->createGroupAction = new QAction(this); this->createGroupAction->setText(tr("Create group...")); this->createGroupAction->setStatusTip(tr("Create a group")); connect(this->createGroupAction, SIGNAL(triggered()), this, SLOT(onCreateGroup())); this->relabelObjectAction = new QAction(this); this->relabelObjectAction->setText(tr("Rename")); this->relabelObjectAction->setStatusTip(tr("Rename object")); #ifndef Q_OS_MAC this->relabelObjectAction->setShortcut(Qt::Key_F2); #endif connect(this->relabelObjectAction, SIGNAL(triggered()), this, SLOT(onRelabelObject())); this->finishEditingAction = new QAction(this); this->finishEditingAction->setText(tr("Finish editing")); this->finishEditingAction->setStatusTip(tr("Finish editing object")); connect(this->finishEditingAction, SIGNAL(triggered()), this, SLOT(onFinishEditing())); this->skipRecomputeAction = new QAction(this); this->skipRecomputeAction->setCheckable(true); this->skipRecomputeAction->setText(tr("Skip recomputes")); this->skipRecomputeAction->setStatusTip(tr("Enable or disable recomputations of document")); connect(this->skipRecomputeAction, SIGNAL(toggled(bool)), this, SLOT(onSkipRecompute(bool))); this->markRecomputeAction = new QAction(this); this->markRecomputeAction->setText(tr("Mark to recompute")); this->markRecomputeAction->setStatusTip(tr("Mark this object to be recomputed")); connect(this->markRecomputeAction, SIGNAL(triggered()), this, SLOT(onMarkRecompute())); this->searchObjectsAction = new QAction(this); this->searchObjectsAction->setText(tr("Search...")); this->searchObjectsAction->setStatusTip(tr("Search for objects")); connect(this->searchObjectsAction, SIGNAL(triggered()), this, SLOT(onSearchObjects())); // Setup connections connectNewDocument = Application::Instance->signalNewDocument.connect(boost::bind(&TreeWidget::slotNewDocument, this, _1)); connectDelDocument = Application::Instance->signalDeleteDocument.connect(boost::bind(&TreeWidget::slotDeleteDocument, this, _1)); connectRenDocument = Application::Instance->signalRenameDocument.connect(boost::bind(&TreeWidget::slotRenameDocument, this, _1)); connectActDocument = Application::Instance->signalActiveDocument.connect(boost::bind(&TreeWidget::slotActiveDocument, this, _1)); connectRelDocument = Application::Instance->signalRelabelDocument.connect(boost::bind(&TreeWidget::slotRelabelDocument, this, _1)); QStringList labels; labels << tr("Labels & Attributes"); this->setHeaderLabels(labels); // make sure to show a horizontal scrollbar if needed #if QT_VERSION >= 0x050000 this->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); #else this->header()->setResizeMode(0, QHeaderView::ResizeToContents); #endif this->header()->setStretchLastSection(false); // Add the first main label this->rootItem = new QTreeWidgetItem(this); this->rootItem->setText(0, tr("Application")); this->rootItem->setFlags(Qt::ItemIsEnabled); this->expandItem(this->rootItem); this->setSelectionMode(QAbstractItemView::ExtendedSelection); #if QT_VERSION >= 0x040200 // causes unexpected drop events (possibly only with Qt4.1.x) this->setMouseTracking(true); // needed for itemEntered() to work #endif this->statusTimer = new QTimer(this); connect(this->statusTimer, SIGNAL(timeout()), this, SLOT(onTestStatus())); connect(this, SIGNAL(itemEntered(QTreeWidgetItem*, int)), this, SLOT(onItemEntered(QTreeWidgetItem*))); connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(onItemCollapsed(QTreeWidgetItem*))); connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(onItemExpanded(QTreeWidgetItem*))); connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(onItemSelectionChanged())); this->statusTimer->setSingleShot(true); this->statusTimer->start(300); documentPixmap = new QPixmap(Gui::BitmapFactory().pixmap("Document")); }TreeWidget::TreeWidget(QWidget* parent) : QTreeWidget(parent), contextItem(0), fromOutside(false) { this->setDragEnabled(true); this->setAcceptDrops(true); this->setDropIndicatorShown(false); this->setRootIsDecorated(false); this->createGroupAction = new QAction(this); this->createGroupAction->setText(tr("Create group...")); this->createGroupAction->setStatusTip(tr("Create a group")); connect(this->createGroupAction, SIGNAL(triggered()), this, SLOT(onCreateGroup())); this->relabelObjectAction = new QAction(this); this->relabelObjectAction->setText(tr("Rename")); this->relabelObjectAction->setStatusTip(tr("Rename object")); #ifndef Q_OS_MAC this->relabelObjectAction->setShortcut(Qt::Key_F2); #endif connect(this->relabelObjectAction, SIGNAL(triggered()), this, SLOT(onRelabelObject())); this->finishEditingAction = new QAction(this); this->finishEditingAction->setText(tr("Finish editing")); this->finishEditingAction->setStatusTip(tr("Finish editing object")); connect(this->finishEditingAction, SIGNAL(triggered()), this, SLOT(onFinishEditing())); this->skipRecomputeAction = new QAction(this); this->skipRecomputeAction->setCheckable(true); this->skipRecomputeAction->setText(tr("Skip recomputes")); this->skipRecomputeAction->setStatusTip(tr("Enable or disable recomputations of document")); connect(this->skipRecomputeAction, SIGNAL(toggled(bool)), this, SLOT(onSkipRecompute(bool))); this->markRecomputeAction = new QAction(this); this->markRecomputeAction->setText(tr("Mark to recompute")); this->markRecomputeAction->setStatusTip(tr("Mark this object to be recomputed")); connect(this->markRecomputeAction, SIGNAL(triggered()), this, SLOT(onMarkRecompute())); this->searchObjectsAction = new QAction(this); this->searchObjectsAction->setText(tr("Search...")); this->searchObjectsAction->setStatusTip(tr("Search for objects")); connect(this->searchObjectsAction, SIGNAL(triggered()), this, SLOT(onSearchObjects())); // Setup connections connectNewDocument = Application::Instance->signalNewDocument.connect(boost::bind(&TreeWidget::slotNewDocument, this, _1)); connectDelDocument = Application::Instance->signalDeleteDocument.connect(boost::bind(&TreeWidget::slotDeleteDocument, this, _1)); connectRenDocument = Application::Instance->signalRenameDocument.connect(boost::bind(&TreeWidget::slotRenameDocument, this, _1)); connectActDocument = Application::Instance->signalActiveDocument.connect(boost::bind(&TreeWidget::slotActiveDocument, this, _1)); connectRelDocument = Application::Instance->signalRelabelDocument.connect(boost::bind(&TreeWidget::slotRelabelDocument, this, _1)); QStringList labels; labels << tr("Labels & Attributes"); this->setHeaderLabels(labels); // make sure to show a horizontal scrollbar if needed #if QT_VERSION >= 0x050000 this->header()->setSectionResizeMode(0, QHeaderView::ResizeToContents); #else this->header()->setResizeMode(0, QHeaderView::ResizeToContents); #endif this->header()->setStretchLastSection(false); // Add the first main label this->rootItem = new QTreeWidgetItem(this); this->rootItem->setText(0, tr("Application")); this->rootItem->setFlags(Qt::ItemIsEnabled); this->expandItem(this->rootItem); this->setSelectionMode(QAbstractItemView::ExtendedSelection); #if QT_VERSION >= 0x040200 // causes unexpected drop events (possibly only with Qt4.1.x) this->setMouseTracking(true); // needed for itemEntered() to work #endif this->statusTimer = new QTimer(this); connect(this->statusTimer, SIGNAL(timeout()), this, SLOT(onTestStatus())); connect(this, SIGNAL(itemEntered(QTreeWidgetItem*, int)), this, SLOT(onItemEntered(QTreeWidgetItem*))); connect(this, SIGNAL(itemCollapsed(QTreeWidgetItem*)), this, SLOT(onItemCollapsed(QTreeWidgetItem*))); connect(this, SIGNAL(itemExpanded(QTreeWidgetItem*)), this, SLOT(onItemExpanded(QTreeWidgetItem*))); connect(this, SIGNAL(itemSelectionChanged()), this, SLOT(onItemSelectionChanged())); this->statusTimer->setSingleShot(true); this->statusTimer->start(300); documentPixmap = new QPixmap(Gui::BitmapFactory().pixmap("Document")); }
当新建文档时,调用TreeWidget:: slotNewDocument()完成了DocumentItem类型文档项的创建,对应文档模型的根节点。
void TreeWidget::slotNewDocument(const Gui::Document& Doc){DocumentItem* item = new DocumentItem(&Doc, this->rootItem);this->expandItem(item);item->setIcon(0, *documentPixmap);item->setText(0, QString::fromUtf8(Doc.getDocument()->Label.getValue()));DocumentMap[ &Doc ] = item;}void TreeWidget::slotNewDocument(const Gui::Document& Doc) { DocumentItem* item = new DocumentItem(&Doc, this->rootItem); this->expandItem(item); item->setIcon(0, *documentPixmap); item->setText(0, QString::fromUtf8(Doc.getDocument()->Label.getValue())); DocumentMap[ &Doc ] = item; }void TreeWidget::slotNewDocument(const Gui::Document& Doc) { DocumentItem* item = new DocumentItem(&Doc, this->rootItem); this->expandItem(item); item->setIcon(0, *documentPixmap); item->setText(0, QString::fromUtf8(Doc.getDocument()->Label.getValue())); DocumentMap[ &Doc ] = item; }
而在DocumentItem的构造函数中,完成了新建对象文档事件的关联。可以看到,Gui::Document::signalNewObject关联到了Gui::DocumentItem::slotNewObject
DocumentItem::DocumentItem(const Gui::Document* doc, QTreeWidgetItem * parent): QTreeWidgetItem(parent, TreeWidget::DocumentType), pDocument(doc){// Setup connectionsconnectNewObject = doc->signalNewObject.connect(boost::bind(&DocumentItem::slotNewObject, this, _1));connectDelObject = doc->signalDeletedObject.connect(boost::bind(&DocumentItem::slotDeleteObject, this, _1));connectChgObject = doc->signalChangedObject.connect(boost::bind(&DocumentItem::slotChangeObject, this, _1));connectRenObject = doc->signalRelabelObject.connect(boost::bind(&DocumentItem::slotRenameObject, this, _1));connectActObject = doc->signalActivatedObject.connect(boost::bind(&DocumentItem::slotActiveObject, this, _1));connectEdtObject = doc->signalInEdit.connect(boost::bind(&DocumentItem::slotInEdit, this, _1));connectResObject = doc->signalResetEdit.connect(boost::bind(&DocumentItem::slotResetEdit, this, _1));connectHltObject = doc->signalHighlightObject.connect(boost::bind(&DocumentItem::slotHighlightObject, this, _1,_2,_3));connectExpObject = doc->signalExpandObject.connect(boost::bind(&DocumentItem::slotExpandObject, this, _1,_2));connectScrObject = doc->signalScrollToObject.connect(boost::bind(&DocumentItem::slotScrollToObject, this, _1));setFlags(Qt::ItemIsEnabled/*|Qt::ItemIsEditable*/);}DocumentItem::DocumentItem(const Gui::Document* doc, QTreeWidgetItem * parent) : QTreeWidgetItem(parent, TreeWidget::DocumentType), pDocument(doc) { // Setup connections connectNewObject = doc->signalNewObject.connect(boost::bind(&DocumentItem::slotNewObject, this, _1)); connectDelObject = doc->signalDeletedObject.connect(boost::bind(&DocumentItem::slotDeleteObject, this, _1)); connectChgObject = doc->signalChangedObject.connect(boost::bind(&DocumentItem::slotChangeObject, this, _1)); connectRenObject = doc->signalRelabelObject.connect(boost::bind(&DocumentItem::slotRenameObject, this, _1)); connectActObject = doc->signalActivatedObject.connect(boost::bind(&DocumentItem::slotActiveObject, this, _1)); connectEdtObject = doc->signalInEdit.connect(boost::bind(&DocumentItem::slotInEdit, this, _1)); connectResObject = doc->signalResetEdit.connect(boost::bind(&DocumentItem::slotResetEdit, this, _1)); connectHltObject = doc->signalHighlightObject.connect(boost::bind(&DocumentItem::slotHighlightObject, this, _1,_2,_3)); connectExpObject = doc->signalExpandObject.connect(boost::bind(&DocumentItem::slotExpandObject, this, _1,_2)); connectScrObject = doc->signalScrollToObject.connect(boost::bind(&DocumentItem::slotScrollToObject, this, _1)); setFlags(Qt::ItemIsEnabled/*|Qt::ItemIsEditable*/); }DocumentItem::DocumentItem(const Gui::Document* doc, QTreeWidgetItem * parent) : QTreeWidgetItem(parent, TreeWidget::DocumentType), pDocument(doc) { // Setup connections connectNewObject = doc->signalNewObject.connect(boost::bind(&DocumentItem::slotNewObject, this, _1)); connectDelObject = doc->signalDeletedObject.connect(boost::bind(&DocumentItem::slotDeleteObject, this, _1)); connectChgObject = doc->signalChangedObject.connect(boost::bind(&DocumentItem::slotChangeObject, this, _1)); connectRenObject = doc->signalRelabelObject.connect(boost::bind(&DocumentItem::slotRenameObject, this, _1)); connectActObject = doc->signalActivatedObject.connect(boost::bind(&DocumentItem::slotActiveObject, this, _1)); connectEdtObject = doc->signalInEdit.connect(boost::bind(&DocumentItem::slotInEdit, this, _1)); connectResObject = doc->signalResetEdit.connect(boost::bind(&DocumentItem::slotResetEdit, this, _1)); connectHltObject = doc->signalHighlightObject.connect(boost::bind(&DocumentItem::slotHighlightObject, this, _1,_2,_3)); connectExpObject = doc->signalExpandObject.connect(boost::bind(&DocumentItem::slotExpandObject, this, _1,_2)); connectScrObject = doc->signalScrollToObject.connect(boost::bind(&DocumentItem::slotScrollToObject, this, _1)); setFlags(Qt::ItemIsEnabled/*|Qt::ItemIsEditable*/); }
在DocumentItem::slotNewObject中通过调用createNewItem创建树节点。
void DocumentItem::slotNewObject(const Gui::ViewProviderDocumentObject& obj) {createNewItem(obj);}void DocumentItem::slotNewObject(const Gui::ViewProviderDocumentObject& obj) { createNewItem(obj); }void DocumentItem::slotNewObject(const Gui::ViewProviderDocumentObject& obj) { createNewItem(obj); }
结合前述文档对象的创建过程可以看出,当App::Document::addObject()创建完成文档对象之后,触发App::Document::signalNewObject事件,由于此事件关联到了Gui::DocumentItem::slotNewObject槽函数,会调用createNewItem函数在TreeWidget创建对应的树节点。
boost::signals2::signal<void(const App::DocumentObject&)> App::Document::signalNewObject;bool DocumentItem::createNewItem(const Gui::ViewProviderDocumentObject& obj,QTreeWidgetItem *parent, int index, DocumentObjectItemsPtr ptrs){const char *name;if (!obj.showInTree() || !(name=obj.getObject()->getNameInDocument()))return false;if (!ptrs) {auto &items = ObjectMap[name];if (!items) {items.reset(new DocumentObjectItems);}else if(items->size() && parent==NULL) {Base::Console().Warning("DocumentItem::slotNewObject: Cannot add view provider twice.\\n");return false;}ptrs = items;}std::string displayName = obj.getObject()->Label.getValue();DocumentObjectItem* item = new DocumentObjectItem(const_cast<Gui::ViewProviderDocumentObject*>(&obj), ptrs);if (!parent)parent = this;if (index<0)parent->addChild(item);elseparent->insertChild(index,item);// Couldn't be added and thus don't continue populating it// and delete it againif (!item->parent()) {delete item;}else {item->setIcon(0, obj.getIcon());item->setText(0, QString::fromUtf8(displayName.c_str()));populateItem(item);}return true;}boost::signals2::signal<void(const App::DocumentObject&)> App::Document::signalNewObject; bool DocumentItem::createNewItem(const Gui::ViewProviderDocumentObject& obj, QTreeWidgetItem *parent, int index, DocumentObjectItemsPtr ptrs) { const char *name; if (!obj.showInTree() || !(name=obj.getObject()->getNameInDocument())) return false; if (!ptrs) { auto &items = ObjectMap[name]; if (!items) { items.reset(new DocumentObjectItems); } else if(items->size() && parent==NULL) { Base::Console().Warning("DocumentItem::slotNewObject: Cannot add view provider twice.\\n"); return false; } ptrs = items; } std::string displayName = obj.getObject()->Label.getValue(); DocumentObjectItem* item = new DocumentObjectItem( const_cast<Gui::ViewProviderDocumentObject*>(&obj), ptrs); if (!parent) parent = this; if (index<0) parent->addChild(item); else parent->insertChild(index,item); // Couldn't be added and thus don't continue populating it // and delete it again if (!item->parent()) { delete item; } else { item->setIcon(0, obj.getIcon()); item->setText(0, QString::fromUtf8(displayName.c_str())); populateItem(item); } return true; }boost::signals2::signal<void(const App::DocumentObject&)> App::Document::signalNewObject; bool DocumentItem::createNewItem(const Gui::ViewProviderDocumentObject& obj, QTreeWidgetItem *parent, int index, DocumentObjectItemsPtr ptrs) { const char *name; if (!obj.showInTree() || !(name=obj.getObject()->getNameInDocument())) return false; if (!ptrs) { auto &items = ObjectMap[name]; if (!items) { items.reset(new DocumentObjectItems); } else if(items->size() && parent==NULL) { Base::Console().Warning("DocumentItem::slotNewObject: Cannot add view provider twice.\\n"); return false; } ptrs = items; } std::string displayName = obj.getObject()->Label.getValue(); DocumentObjectItem* item = new DocumentObjectItem( const_cast<Gui::ViewProviderDocumentObject*>(&obj), ptrs); if (!parent) parent = this; if (index<0) parent->addChild(item); else parent->insertChild(index,item); // Couldn't be added and thus don't continue populating it // and delete it again if (!item->parent()) { delete item; } else { item->setIcon(0, obj.getIcon()); item->setText(0, QString::fromUtf8(displayName.c_str())); populateItem(item); } return true; }
附录A: OIV测试CMakeLists
#add_defintions(-D_FC_GUI_ENABLED_)#add_defintions(-DFREECADMAINPY)######################## OIV ########################SET(OIV_SRCSTest_OIV.cpp)include_directories(${COIN3D_INCLUDE_DIRS})SET(OIV_LIBS${Boost_LIBRARIES}${COIN3D_LIBRARIES}${SOQT_LIBRARIE}${OPENGL_gl_LIBRARY}FreeCADGui)if (BUILD_QT5)include_directories(${Qt5Core_INCLUDE_DIRS}${Qt5Widgets_INCLUDE_DIRS}${Qt5OpenGL_INCLUDE_DIRS}${Qt5PrintSupport_INCLUDE_DIRS}${Qt5Svg_INCLUDE_DIRS}${Qt5Network_INCLUDE_DIRS}${Qt5UiTools_INCLUDE_DIRS})list(APPEND OIV_LIBS${Qt5Core_LIBRARIES}${Qt5Widgets_LIBRARIES}${Qt5OpenGL_LIBRARIES}${Qt5PrintSupport_LIBRARIES}${Qt5Svg_LIBRARIES}${Qt5Network_LIBRARIES}${Qt5UiTools_LIBRARIES})else()include_directories(${QT_INCLUDE_DIR})list(APPEND OIV_LIBS${QT_LIBRARIES}${QT_QTUITOOLS_LIBRARY})endif()add_definitions(-D SOQT_DLL)add_executable(Test_OIV WIN32 ${OIV_SRCS})target_link_libraries(Test_OIV ${OIV_LIBS})SET_BIN_DIR(Test_OIV Test_OIV)if(WIN32)INSTALL(TARGETS Test_OIVRUNTIME DESTINATION binLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})elseif(APPLE AND NOT BUILD_WITH_CONDA)INSTALL(TARGETS Test_OIVRUNTIME DESTINATION MacOSLIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR})else()INSTALL(TARGETS Test_OIVRUNTIME DESTINATION bin)endif()#add_defintions(-D_FC_GUI_ENABLED_) #add_defintions(-DFREECADMAINPY) ######################## OIV ######################## SET(OIV_SRCS Test_OIV.cpp ) include_directories( ${COIN3D_INCLUDE_DIRS} ) SET(OIV_LIBS ${Boost_LIBRARIES} ${COIN3D_LIBRARIES} ${SOQT_LIBRARIE} ${OPENGL_gl_LIBRARY} FreeCADGui ) if (BUILD_QT5) include_directories( ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS} ${Qt5PrintSupport_INCLUDE_DIRS} ${Qt5Svg_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5UiTools_INCLUDE_DIRS} ) list(APPEND OIV_LIBS ${Qt5Core_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5OpenGL_LIBRARIES} ${Qt5PrintSupport_LIBRARIES} ${Qt5Svg_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5UiTools_LIBRARIES} ) else() include_directories( ${QT_INCLUDE_DIR} ) list(APPEND OIV_LIBS ${QT_LIBRARIES} ${QT_QTUITOOLS_LIBRARY} ) endif() add_definitions(-D SOQT_DLL) add_executable(Test_OIV WIN32 ${OIV_SRCS}) target_link_libraries(Test_OIV ${OIV_LIBS}) SET_BIN_DIR(Test_OIV Test_OIV) if(WIN32) INSTALL(TARGETS Test_OIV RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) elseif(APPLE AND NOT BUILD_WITH_CONDA) INSTALL(TARGETS Test_OIV RUNTIME DESTINATION MacOS LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) else() INSTALL(TARGETS Test_OIV RUNTIME DESTINATION bin ) endif()#add_defintions(-D_FC_GUI_ENABLED_) #add_defintions(-DFREECADMAINPY) ######################## OIV ######################## SET(OIV_SRCS Test_OIV.cpp ) include_directories( ${COIN3D_INCLUDE_DIRS} ) SET(OIV_LIBS ${Boost_LIBRARIES} ${COIN3D_LIBRARIES} ${SOQT_LIBRARIE} ${OPENGL_gl_LIBRARY} FreeCADGui ) if (BUILD_QT5) include_directories( ${Qt5Core_INCLUDE_DIRS} ${Qt5Widgets_INCLUDE_DIRS} ${Qt5OpenGL_INCLUDE_DIRS} ${Qt5PrintSupport_INCLUDE_DIRS} ${Qt5Svg_INCLUDE_DIRS} ${Qt5Network_INCLUDE_DIRS} ${Qt5UiTools_INCLUDE_DIRS} ) list(APPEND OIV_LIBS ${Qt5Core_LIBRARIES} ${Qt5Widgets_LIBRARIES} ${Qt5OpenGL_LIBRARIES} ${Qt5PrintSupport_LIBRARIES} ${Qt5Svg_LIBRARIES} ${Qt5Network_LIBRARIES} ${Qt5UiTools_LIBRARIES} ) else() include_directories( ${QT_INCLUDE_DIR} ) list(APPEND OIV_LIBS ${QT_LIBRARIES} ${QT_QTUITOOLS_LIBRARY} ) endif() add_definitions(-D SOQT_DLL) add_executable(Test_OIV WIN32 ${OIV_SRCS}) target_link_libraries(Test_OIV ${OIV_LIBS}) SET_BIN_DIR(Test_OIV Test_OIV) if(WIN32) INSTALL(TARGETS Test_OIV RUNTIME DESTINATION bin LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) elseif(APPLE AND NOT BUILD_WITH_CONDA) INSTALL(TARGETS Test_OIV RUNTIME DESTINATION MacOS LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR} ) else() INSTALL(TARGETS Test_OIV RUNTIME DESTINATION bin ) endif()