1.前言

一个强大的可视化系统不仅需要强大的数据处理能力,也需要方便易用的交互功能。图形处理软件ParaView(hhttp://www.paraview.org)、德国癌症研究中心研发的MITK(http://www.mitk.org)等开源软件系统都提供了强大的交互功能,作为ParaView、MITK等软件构件基础的VTK同样也提供了各种各样的交互功能。
VTK的交互除了可以监听来自鼠标、键盘等外部设备的消息,还可以在渲染场景中生成功能各异的交互部件(Widget),用于控制可视化过程的参数,达到用户的渲染要求。

2.观察者/命令模式(Observe/Command)

观察者/命令模式是VTK里用的比较多的设计模式。
VTK中绝大多数的类都派生自vtkObject。查看类vtkObject的接口可以找到AddObserve()、RemoveObserve()、GetCommand()等函数。
观察者/命令模式是指一个Object可以有多个Observe,他定义了对象间的一种“一对多”的依赖关系,当一个Object对象的状态发生改变时,所有依赖于它的Observe对象都得到通知而被自动更新命令模式属于对象行为模式,他将一个请求封装为一个对象,并提供一致性发送请求的接口,当一个事件发生时,他不直接把事件传递给事件调用者,而是在命令和调用者之间增加一个中间者,讲这种直接关系切断,同时将两者都隔离。事件调用者只是和接口打交道,不和具体事件实现交互。
在VTK中,可以通过两种方式来实现观察者/命令模式,他们分别是使用时间回调函数、从VTKCommand派生出具体的子类。

2.1 观察者-事件回调方案

在vtkObject中,有如下函数:

unsigned long AddObserver(unsigned long event, vtkCommand *,  float priority = 0.0f);
unsigned long AddObserver(const char* event, vtkCommand *, float priority = 0.0f);

AddObserver()函数的作用就是针对某个事件添加挂插着到某个VTK对象中

当该对象发生观察者感兴趣的事件时,就会自动调用回调函数,执行相关操作
下面是一个非常简单的例子演示了在VTK里是如何使用“观察者-事件回调函数”方案的:
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);
VTK_MODULE_INIT(vtkInteractionStyle);#include <vtkCallbackCommand.h>long cntPress = 0;
void MyCallbackFunc(vtkObject*, unsigned long eid, void* clientdata, void* calldata)
{std::cout << "You have clicked : " << ++cntPress << " times" << std::endl;
}#include <vtkSmartPointer.h>
#include <vtkPNGReader.h>
#include <vtkImageViewer2.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>int main()
{vtkSmartPointer<vtkPNGReader> reader =vtkSmartPointer<vtkPNGReader>::New();reader->SetFileName("vtk.png");reader->Update();vtkSmartPointer<vtkImageViewer2> viewer =vtkSmartPointer<vtkImageViewer2>::New();viewer->SetInputData(reader->GetOutput());viewer->GetRenderer()->SetBackground(0, 0, 0);viewer->SetSize(480, 320);viewer->GetRenderWindow()->SetWindowName("Observer-Callback");vtkSmartPointer<vtkRenderWindowInteractor> rwi =vtkSmartPointer<vtkRenderWindowInteractor>::New();viewer->SetupInteractor(rwi);viewer->Render();/*************************************************************///Step1:设置事件回调函数vtkSmartPointer<vtkCallbackCommand> mouseCallback =vtkSmartPointer<vtkCallbackCommand>::New();mouseCallback->SetCallback(MyCallbackFunc); //很重要!!!//Step2:将vtkCallbackCommand对象添加到观察者列表。rwi->SetRenderWindow(viewer->GetRenderWindow()); //唤醒显示窗口rwi->AddObserver(vtkCommand::LeftButtonPressEvent, mouseCallback);rwi->Initialize();rwi->Start();return 0;
}

输出结果:

通过上例,我们可以总结一下“观察者-回调函数”交互方案主要可以分为以下三个步骤:

1.定义回调函数
回调函数的函数签名只能是以下形式:
void long MyCallbackFunc(vtkObject* obj, unsigned long eid, void* clientdata, void* calldata);
其中,
obj:是调用事件的对象(即调用AddObserver()函数的对象,对应于本例的rwi);
eid:是所要监听的事件ID,VTK中的事件定义在vtkCommand.h文件中;
clientdata:是与VTKCallbackCommand实例相关的数据,简单地说,是指回调函数里需要访问主程序里面得数据时,由主程序向回调函数传递的数据。
calldata:是执行vtkObject::InvokeEvent()函数时,随着回调函数发送得数据,比如说,当调用ProgressEvent事件时,会自动发送当前的进度值作为callback。
2.创建一个VTKCallbackCommand对象,并调用VTKCallbackCommand::SetCallback()函数设置所定义的回调函数
//Step1:设置事件回调函数
vtkSmartPointer<vtkCallbackCommand> mouseCallback =vtkSmartPointer<vtkCallbackCommand>::New();
mouseCallback->SetCallback(MyCallbackFunc); //很重要!!!

3.将VTKCallbackCommand对象添加到对象的观察者列表中

//Step2:将vtkCallbackCommand对象添加到观察者列表。
rwi->SetRenderWindow(viewer->GetRenderWindow()); //唤醒显示窗口
rwi->AddObserver(vtkCommand::LeftButtonPressEvent, mouseCallback);

注意:

VTK中的vtkRenderWindowInteractor提供了一种独立于平台的交互机制,用来响应不同平台的鼠标、按键和时钟等消息。当渲染窗口中有事件发生时(比如说单机消息),vtkRenderWindowInteractor内部会调用与平台相关的子类,将该消息转换成对应平台的消息。
因此,该例的核心在于:通过vtkRenderWindowInteractor来监听鼠标左键的消息,一旦监听到对象的观察者列表中的消息时,程序会自动调用事件回调函数。

2.2 vtkCommand子类

观察者/命令模式除了使用事件回调函数外,还可以直接从vtkCommand类中派生出子类来实现。
示例代码如下:
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL);
VTK_MODULE_INIT(vtkInteractionStyle);#include <vtkCommand.h>
#include <vtkSmartPointer.h>
#include <vtkConeSource.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkInteractorStyleTrackballCamera.h>class myCallback :public vtkCommand
{
public:static myCallback* New(){return new myCallback;}void SetObject(vtkConeSource* cone){m_cone = cone;}virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData){std::cout << "LeftButton has been pressed: " << std::endl<< "The Height: " << m_cone->GetHeight() << std::endl<< "The Radius: " << m_cone->GetRadius() << std::endl;}private:vtkConeSource* m_cone;
};
int main()
{vtkSmartPointer<vtkConeSource> cone =vtkSmartPointer<vtkConeSource>::New();cone->SetHeight(10);cone->SetRadius(4);cone->Update();/*************************************************************/vtkSmartPointer<vtkPolyDataMapper> mapper =vtkSmartPointer<vtkPolyDataMapper>::New();mapper->SetInputConnection(cone->GetOutputPort());vtkSmartPointer<vtkActor> actor =vtkSmartPointer<vtkActor>::New();actor->SetMapper(mapper);vtkSmartPointer<vtkRenderer> render =vtkSmartPointer<vtkRenderer>::New();render->AddActor(actor);render->SetBackground(1, 1, 1);vtkSmartPointer<vtkRenderWindow> rw =vtkSmartPointer<vtkRenderWindow>::New();rw->AddRenderer(render);rw->SetWindowName("Interaction By SubCommand");rw->SetSize(320, 320);vtkSmartPointer<vtkRenderWindowInteractor> rwi =vtkSmartPointer<vtkRenderWindowInteractor>::New();rwi->SetRenderWindow(rw);vtkSmartPointer<vtkInteractorStyleTrackballCamera> style =vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();rwi->SetInteractorStyle(style);vtkSmartPointer<myCallback> callback =vtkSmartPointer<myCallback>::New();callback->SetObject(cone);rwi->AddObserver(vtkCommand::LeftButtonPressEvent, callback);rwi->Initialize();rwi->Start();return 0;
}

输出结果为:

这个例子实现的功能也是监听鼠标左键单机的消息。

基于“观察者-vtkCommand子类”的实现方案也遵循三个步骤。
1. 从vtkCommand类中派生出子类,并实现vtkCommand::Execute()虚函数
该函数原型为:
virtual void Execute(vtkObject* caller,  unsigned long eventId,  void* callData )= 0;
Execute()时纯虚函数,所以,从vtkCommand派生类中都必须要实现这个方法。
2.实例化vtkCommand子类的对象,并调用相关的方法
3.调用观察者函数
调用AddObserver()函数监听感兴趣的事件,如果所监听的事件发生,就会调用vtkCommand子类中定义的Execute()函数。
因此,针对所监听的事件,程序需要把实现的功能放在Execute函数中。

3.参看资料

1.《C++ primer》
2.《The VTK User’s Guide – 11thEdition》
3.  张晓东, 罗火灵. VTK图形图像开发进阶[M]. 机械工业出版社, 2015.

VTK修炼之道71:交互与Widget_观察者/命令模式相关推荐

  1. VTK修炼之道77:交互部件_分割/配准类Widget与其他Widget

    1.分割/配准交互部件 图像分割与配准是数字图像处理技术两大主要的应用领域,特别是在医学图像处理中. 著名的医学图像分割与配准工具包ITK(Insight Segmentation & Reg ...

  2. VTK修炼之道75:交互部件_测量类Widget的应用

    1.与测量类相关的主要Widget 与测量类相关的主要Widget如下: vtkDistanceWidget:用于在二维平面上测量两点之间的距离. vtkAngleWidget:用于在平面的角度测量. ...

  3. VTK修炼之道74:交互部件_Widget的创建

    1.创建Widget交互的步骤 虽然每个Widget都提供了不同的功能以及不同的API,但是,Widget的创建以及使用基本都是类似的.创建Widget的一般步骤如下: 1.实例化Widget: 2. ...

  4. VTK修炼之道27:图像基本操作_三维图像切片交互提取(回调函数、观察者-命令模式)

    1.鼠标滑动提取三维图像切片 学习三维图像切面的提取后,我们可以实现一个稍微复杂的程序--通过滑动鼠标来切换三维图像切片,这也是医学图像处理软件中一个很基本的功能.实现该功能难点是怎样在VTK中控制鼠 ...

  5. VTK修炼之道81:VTK开发基础_vtkObject类深入分析

    1.前言 相比于vtkObjectBase,我们接触更多的是vtkObject类. vtkObjectBase类主要实现了引用计数,因此vtkObject及其相关子类都继承了该特性. 与此同时,vtk ...

  6. VTK修炼之道80:VTK开发基础_智能指针与引用计数

    1.引用计数 VTK经过多年的开发与维护,已经形成了一套稳定的框架和开发规则.因此,了解这些规则和框架是定制VTK类的基础,这其中用到了大量面向对象的设计模式,例如对象工程模式.观察者/命令模式:还有 ...

  7. 【VTK】VTK框选表面拾取三角面片——通过观察者命令模式

    VTK框选拾取三角面片 最近需要实现拾取三角面片的交互功能,看了官方示例和网友分享,都是使用vtkInteractorStyleRubberBandPick搭配vtkAreaPicker.但是具体实现 ...

  8. VTK源码阅读--vtkObject类-观察者/命令模式

    vtkObject类 vtkObject类是VTK中大多数类的基类:         vtkObject类提供了很多API用于跟踪对象修改时间,debug消息,打印类堆栈,以及事件回调:        ...

  9. VTK修炼之道78:交互与拾取_点拾取

    1.拾取 选择拾取是人机交互过程的一个重要功能. 一个最经典的例子就是,在玩3D游戏时,场景中可能会存在多个角色,有时需要用鼠标来选择所要控制的角色,这就要用到拾取功能. 另外,在某些三维图形的编辑软 ...

最新文章

  1. php encode 数组,PHP中json_encode转换数组时要注意的地方
  2. Android自定义Shape
  3. ElasticSearch 文档路由,你的数据到底存在哪一个分片上_06
  4. [ jenkins ] git+jenkins+maven + ansible 部署java程序
  5. CST材料库相关问题
  6. VS2013新建项目出现脚本错误的解决办法
  7. mysql数据库加载太慢_MySQL数据库导致网站打开速度慢的解决思路
  8. 先有鸡还是先有蛋?应用程序开发与安全
  9. IOS 获取网络图片的大小 改变 图片色值 灰度什么的方法集合
  10. Ronald Van Loon/Greg White带你领略Hadoop/Spark大数据CCA175认证(一)
  11. canvas制作简单表格
  12. 链路层链路发现协议(LLDP)讲解
  13. velo2cam_calibration——最新最准确的激光雷达Lidar和相机Camera外参标定算法实现
  14. 【Python机器学习】01_机器学习概述
  15. UWB TDOA一维定位解算
  16. 单击鼠标右键没有新建WORD\EXCEL怎么办?
  17. (附源码)计算机毕业设计ssm本科生专业分流管理系统
  18. vivo是安卓手机吗_ams与vivo深化合作,引领安卓智能手机市场发展新趋势
  19. 2.2.1 定点数的表示 原码 反码 补码 移码
  20. 说话人识别和说话人性别识别SDK-通过语音判断说话人,及说话人的性别

热门文章

  1. Oracle执行计划顺序
  2. Linux下VNC配置多个桌面和修改密码 不会当系统重启vnc失效
  3. ASP.NET真假分页—真分页
  4. java 读取中文配置文件问题
  5. OpenCV 自动调取摄像头并显示屏幕
  6. Anchor free Detector:FCOS
  7. 数据结构(严蔚敏)之一——顺序表之c语言实现
  8. Java中vector的用法
  9. LA 2659 poj 3076 zoj 3122 Sudoku(精确覆盖 + DLX)
  10. Python socket的客户端