zoukankan      html  css  js  c++  java
  • VTK 交互与Widget_观察者/命令模式

    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中,有如下函数:
    1 unsigned long AddObserver(unsigned long event, vtkCommand *,  float priority = 0.0f);
    2 unsigned long AddObserver(const char* event, vtkCommand *, float priority = 0.0f);
    AddObserver()函数的作用就是针对某个事件添加挂插着到某个VTK对象中。
    当该对象发生观察者感兴趣的事件时,就会自动调用回调函数,执行相关操作。
    下面是一个非常简单的例子演示了在VTK里是如何使用“观察者-事件回调函数”方案的:
     1 #include <vtkAutoInit.h>
     2 VTK_MODULE_INIT(vtkRenderingOpenGL);
     3 VTK_MODULE_INIT(vtkInteractionStyle);
     4  
     5 #include <vtkCallbackCommand.h>
     6  
     7 long cntPress = 0;
     8 void MyCallbackFunc(vtkObject*, unsigned long eid, void* clientdata, void* calldata)
     9 {
    10     std::cout << "You have clicked : " << ++cntPress << " times" << std::endl;
    11 }
    12  
    13 #include <vtkSmartPointer.h>
    14 #include <vtkPNGReader.h>
    15 #include <vtkImageViewer2.h>
    16 #include <vtkRenderer.h>
    17 #include <vtkRenderWindow.h>
    18 #include <vtkRenderWindowInteractor.h>
    19  
    20 int main()
    21 {
    22     vtkSmartPointer<vtkPNGReader> reader =
    23         vtkSmartPointer<vtkPNGReader>::New();
    24     reader->SetFileName("vtk.png");
    25     reader->Update();
    26  
    27     vtkSmartPointer<vtkImageViewer2> viewer =
    28         vtkSmartPointer<vtkImageViewer2>::New();
    29     viewer->SetInputData(reader->GetOutput());
    30  
    31     viewer->GetRenderer()->SetBackground(0, 0, 0);
    32     viewer->SetSize(480, 320);
    33     viewer->GetRenderWindow()->SetWindowName("Observer-Callback");
    34  
    35     vtkSmartPointer<vtkRenderWindowInteractor> rwi =
    36         vtkSmartPointer<vtkRenderWindowInteractor>::New();
    37     viewer->SetupInteractor(rwi);
    38     viewer->Render();
    39     /*************************************************************/
    40     //Step1:设置事件回调函数
    41     vtkSmartPointer<vtkCallbackCommand> mouseCallback =
    42         vtkSmartPointer<vtkCallbackCommand>::New();
    43     mouseCallback->SetCallback(MyCallbackFunc); //很重要!!!
    44  
    45     //Step2:将vtkCallbackCommand对象添加到观察者列表。
    46     rwi->SetRenderWindow(viewer->GetRenderWindow()); //唤醒显示窗口
    47     rwi->AddObserver(vtkCommand::LeftButtonPressEvent, mouseCallback);
    48  
    49     rwi->Initialize();
    50     rwi->Start();
    51     return 0;
    52 }
    输出结果:
    通过上例,我们可以总结一下“观察者-回调函数”交互方案主要可以分为以下三个步骤:
    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()函数设置所定义的回调函数。
    1 //Step1:设置事件回调函数
    2 vtkSmartPointer<vtkCallbackCommand> mouseCallback =
    3     vtkSmartPointer<vtkCallbackCommand>::New();
    4 mouseCallback->SetCallback(MyCallbackFunc); //很重要!!!

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

    1 //Step2:将vtkCallbackCommand对象添加到观察者列表。
    2 rwi->SetRenderWindow(viewer->GetRenderWindow()); //唤醒显示窗口
    3 rwi->AddObserver(vtkCommand::LeftButtonPressEvent, mouseCallback);
    注意:
    VTK中的vtkRenderWindowInteractor提供了一种独立于平台的交互机制,用来响应不同平台的鼠标、按键和时钟等消息。当渲染窗口中有事件发生时(比如说单机消息),vtkRenderWindowInteractor内部会调用与平台相关的子类,将该消息转换成对应平台的消息。
    因此,该例的核心在于:通过vtkRenderWindowInteractor来监听鼠标左键的消息,一旦监听到对象的观察者列表中的消息时,程序会自动调用事件回调函数。

    2.2 vtkCommand子类

    观察者/命令模式除了使用事件回调函数外,还可以直接从vtkCommand类中派生出子类来实现。
    示例代码如下:
     1 #include <vtkAutoInit.h>
     2 VTK_MODULE_INIT(vtkRenderingOpenGL);
     3 VTK_MODULE_INIT(vtkInteractionStyle);
     4  
     5 #include <vtkCommand.h>
     6 #include <vtkSmartPointer.h>
     7 #include <vtkConeSource.h>
     8 #include <vtkPolyDataMapper.h>
     9 #include <vtkActor.h>
    10 #include <vtkRenderer.h>
    11 #include <vtkRenderWindow.h>
    12 #include <vtkRenderWindowInteractor.h>
    13 #include <vtkInteractorStyleTrackballCamera.h>
    14  
    15 class myCallback :public vtkCommand
    16 {
    17 public:
    18     static myCallback* New()
    19     {
    20         return new myCallback;
    21     }
    22     void SetObject(vtkConeSource* cone)
    23     {
    24         m_cone = cone;
    25     }
    26     virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData)
    27     {
    28         std::cout << "LeftButton has been pressed: " << std::endl
    29             << "The Height: " << m_cone->GetHeight() << std::endl
    30             << "The Radius: " << m_cone->GetRadius() << std::endl;
    31     }
    32  
    33 private:
    34     vtkConeSource* m_cone;
    35 };
    36 int main()
    37 {
    38     vtkSmartPointer<vtkConeSource> cone =
    39         vtkSmartPointer<vtkConeSource>::New();
    40     cone->SetHeight(10);
    41     cone->SetRadius(4);
    42     cone->Update();
    43     /*************************************************************/
    44     vtkSmartPointer<vtkPolyDataMapper> mapper =
    45         vtkSmartPointer<vtkPolyDataMapper>::New();
    46     mapper->SetInputConnection(cone->GetOutputPort());
    47     
    48     vtkSmartPointer<vtkActor> actor =
    49         vtkSmartPointer<vtkActor>::New();
    50     actor->SetMapper(mapper);
    51  
    52     vtkSmartPointer<vtkRenderer> render =
    53         vtkSmartPointer<vtkRenderer>::New();
    54     render->AddActor(actor);
    55     render->SetBackground(1, 1, 1);
    56  
    57     vtkSmartPointer<vtkRenderWindow> rw =
    58         vtkSmartPointer<vtkRenderWindow>::New();
    59     rw->AddRenderer(render);
    60     rw->SetWindowName("Interaction By SubCommand");
    61     rw->SetSize(320, 320);
    62  
    63     vtkSmartPointer<vtkRenderWindowInteractor> rwi =
    64         vtkSmartPointer<vtkRenderWindowInteractor>::New();
    65     rwi->SetRenderWindow(rw);
    66  
    67     
    68     vtkSmartPointer<vtkInteractorStyleTrackballCamera> style =
    69         vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New();
    70     rwi->SetInteractorStyle(style);
    71  
    72     vtkSmartPointer<myCallback> callback =
    73         vtkSmartPointer<myCallback>::New();
    74     callback->SetObject(cone);
    75  
    76     rwi->AddObserver(vtkCommand::LeftButtonPressEvent, callback);
    77     rwi->Initialize();
    78     rwi->Start();
    79     return 0;
    80 }
    输出结果为:
    这个例子实现的功能也是监听鼠标左键单机的消息。
    基于“观察者-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函数中。
  • 相关阅读:
    Unity3d Platformer Pro 2D游戏开发框架使用教程
    程序员如何学习一门新的编程语言
    走进函数式编程
    1001. Exponentiation高精度运算总结
    Kindle PaperWhite3 越狱和PDF插件的安装
    Unity3d中的PlayerPrefs游戏存档API的扩展
    程序员学习路线和学习书单
    1000. A+B Problem
    Mac端SVN工具CornerStone详解
    Unity3d粒子系统详解
  • 原文地址:https://www.cnblogs.com/ybqjymy/p/14244495.html
Copyright © 2011-2022 走看看