OPENCASCADE学习笔记——度量程序执行的进度著:RomanLygin译:GeorgeFeng这是一篇关于开源三维建模软件OPENCASCADE内核的博文:ROMANLYGIN是OPENCASCADE的前程序开发员和项目经理,曾经写过许多关于该开源软件开发包的深入文章,可以在他的博客()上面找到这些文章。序在OpenCascade的论坛上知道了RomanLygin在他的博客上写了OpenCascadenotes系列文章,考虑到OpenCascade的学习资料并不多,于是从他的博客上下载了其中绝大部分文章,将其翻译过来以方便大家学习交流。如果大家发现文中翻译有错误或不足之处,望不吝赐教,可以发到我的邮箱fenghongkui@sina.com.cn,十分感谢。2012年11月22日星期四第1节OCC中程序执行的进度在生活中为自己确定一个可以测度的目标非常重要,这样你就可以不断的检查自己的进度。程序在执行需要花费很长时间的操作时也应该能够显示当前的状态,否则用户可能认为程序死掉了,从而杀死程序。OpenCASCADE提供了一种方法显示你的应用程序还在执行。它就是Message_ProgressIndicator,这是一个用句柄操作的类,可以将该句柄传递给其他算法。当前,IGES和STEP转换器支持该类,BRep读取器也支持该类(从6.3开始)。好了,虽然讲的不多,但是也是个很好的开始。OCC团队可能想要将它扩展到消耗CPU比较高的算法中(例如Boolean运算和其他算法)。但是,借助它丰富的能力,也可以将它用在自己的算法中。虽然如此,让我们还是研究的更加深入一些吧。Message_ProgressIndicator扩展了进度条这个简单概念,成为范围在域[min,max]上的整数容器,并具有当前值。首先比较好的事情是Message_ProgressIndicator提供了double类型(而不是int)的范围,并且可以自定义每一步的大小值。Handle(MyProgressIndicator)anIndicator=newMyProgressIndicator;Standard_RealaStartAngle=0.5*PI;Standard_RealanEndAngle=1.5*PI;Standard_RealaDelta=PI/6.;anIndicator-SetRange(aStartAngle,anEndAngle);anIndicator-SetStep(aDelta);for(Standard_RealaCurrentAngle=aStartAngle;aCurrentAngle=aStartAngle;aCurrentAngle+=aDelta,anIndicator-Increment()){...}要在全局范围内获取当前值,应该调用函数Message_ProgressIndicator::GetPosition(),该函数会返回范围在[0,1]之间的一个值。另外一个比较好的事情是进度条(indicator)支持嵌入到某些区域中。这非常有用,假如你的操作是一个非常耗时的操作的一部分,它可能不知道整体的进度。例如你的算法正在进行可视化显示(AIS_InteractiveObject的子类),可能是一个复杂算法的最后一步或者只是从文件中恢复模型。一个操作可以分为许多子操作,每一个子操作具有一个子段,这样子操作就可以报告其在子段中的进展。进度条会将子段中的局部值映射为全局范围的值。(Anoperationcanallocateasub-rangeforitssub-operationssothattheyreportprogresswithintheirsub-ranges.Theprogressindicatorwilltakecaretomaplocalvalueintoaglobalrange.)voidMyLongOperation(){Handle(Message_ProgressIndicator)anIndicator=newMyIndicator;anIndicator-SetRange(0,100);//100%completeanIndicator-NewScope(60.,Thelongestsuboperation);//firstsuboperationtakes60%MyLongSubOperation1(anIndicator);anIndicator-EndScope();anIndicator-NewScope(30.);//30%MyLongSubOperation2(anIndicator);anIndicator-EndScope();anIndicator-NewScope(10.);//10%MyLongSubOperation3(anIndicator);anIndicator-EndScope();}voidMyLongSubOperation1(constHandle(Message_ProgressIndicator)&theIndicator){Standard_IntegeraNbSteps=...;//smallchunksofworktheIndicator-SetRange(0,aNbSteps);for(inti=1;i=aNbSteps;i++,theIndicator-Increment()){...}}在上面的代码中,全局范围被划分成3个不相等的部分,其与估计的每一个MyLongSubOperation...()函数执行的时间成正比,每一个函数都在子段内执行。这样MyLongSubOperation1()中的SetRange()仅仅影响为其分配的子段。第三,子段可以有他们自己的名字,显示控件可以使用该名字从而显示当前阶段。名字可以利用函数SetName()设置或者直接在NewScope()中设置。用户操作可以发出指令利用标识器打断操作。重新定义和使用虚函数UserBreak()。还有其他一些好的功能特征,例如支持无限区域(infiniteregions)或者Message_ProgressSentry类,该类的封装非常方便,而且当标示器为空句柄时特别有用。例如可以查看IGESToBRep_CurveAndSurface.cxx文件获知其使用方法。设计自己的进度标示器Message_ProgressIndicator是一个抽象类,你必须继承该类,然后重新定义两个纯虚函数——Show()和UserBreak()。它们定义了进度标示器应该如何显示,以及用户是否发出取消操作的消息。下一节将介绍一个这样的例子。(待续…...)POSTEDBYROMANLYGINAT23:51,2009-01-27第2节GUI显示进度(接上节)GUI显示进度从Message_ProgressIndicator继承类,从而与自己的应用程序GUI工具包结合起来工作。另外一种方法是提供简单的文字输出(例如,用于调试目的)。不管哪一种方法,都必须重新定义Show()和UserBreak()方法。在我的QOLib(Qt/OpenCASCADELibrary)中,我使用Qt的QProgressDialog控件来显示进度条和取消按钮。你可以使用自己的包含有QProgressBar,QLabel和可选项QButton控件。下面是一些来自于QLib的代码片段:classQOBase_ProgressIndicator:publicMessage_ProgressIndicator{public://!生成对象。Standard_EXPORTQOBase_ProgressIndicator(QWidget*theParent,inttheMinVal=0,inttheMaxVal=100,Qt::WindowFlagstheFlags=0);//!删除对象。Standard_EXPORTvirtual~QOBase_ProgressIndicator();//!更新当前的对象。Standard_EXPORTvirtualStandard_BooleanShow(constStandard_BooleantheForce);//!假如用户按了取消处理按钮,则返回True。Standard_EXPORTvirtualStandard_BooleanUserBreak();protected:QProgressDialog*myProgress;public:DEFINE_STANDARD_RTTI(QOBase_ProgressIndicator)};/*!使用特定的参数生成控件从而初始化QProgressIndicator。theMin和theMax也用于设置进度标识器的范围。*/QOBase_ProgressIndicator::QOBase_ProgressIndicator(QWidget*theParent,inttheMinVal,inttheMaxVal,Qt::WindowFlagstheFlags){QOLib_ASSERT(theMinValtheMaxVal);myProgress=newQProgressDialog(theParent,theFlags);myProgress-setWindowModality(Qt::WindowModal);myProgress-setMinimum(theMinVal);myProgress-setMaximum(theMaxVal);myProgress-setMinimumDuration(500);//操作时间500ms将会弹出对话框SetScale(theMinVal,theMaxVal,1);//同步更新Qt和OpenCASCADE的范围}/*!销毁关联的进度对话框。*/QOBase_ProgressIndicator::~QOBase_ProgressIndicator(){if(myProgress){deletemyProgress;myProgress=0;}}/*!根据当前的进度更新可视化显示。文本标识符的内容将根据当前步骤的名字更新。总是返回TRUE,从而表明显示已经被更新了*/Standard_BooleanQOBase_ProgressIndicator::Show(constStandard_BooleantheForce){Handle(TCollection_HAsciiString)aName=GetScope(1).GetName();//当前步骤if(!aName.IsNull())myProgress-setLabelText(aName-ToCString());Standard_RealaPc=GetPosition();//alwayswithin[0,1]intaVal=myProgress-minimum()+aPc*(myProgress-maximum()-myProgress-minimum());myProgress-setValue(aVal);QApplication::processEvents();//重新绘制并保持GUI响应returnStandard_True;}/*!返回True,假如用户已经按了窗口QProgressDialog中的取消按钮*/Standard_BooleanQOBase_ProgressIndicator::UserBreak(){returnmyProgress-wasCanceled();}OpenCASCADE移植了Tcl/Tk的实现(参见Draw_ProgressIndicator类),但是不幸的是6.3.0版本中特意删除了XProgressDraw命令,原本你可以利用这个命令激活它,并使用内部的绘制会话(