VTK的交互除了可以监听来自鼠标、键盘等外部设备的消息,还可以在渲染场景中生成功能各异的交互部件(Widget),用于控制可化过程的参数,达到用户渲染要求。 在VTK中要实现与数据的交互,可以基于观察者/命令模式。可以通过两种方法来实现该模式:一种是通过定义回调函数;另外一种是通过从vtkCommand类派生子类。二者的实现过程基本类似,都是通过AddObserver监听感兴趣的时间,然后在回调函数或者vtkCommand::Execute()函数里实现所需的功能。 观察者/命令模式(Observer/Command)是VTK里用得较多的设计模式。VTK中绝大多数的类派生自vtkObject。查看vtkObject的接口可以找到AddObserver()、RemoveObserver()、GetCommand()等函数。
VTK里使用回调函数实现观察者/命令模式主要分为以下三个步骤。 1、定义回调函数。回调函数的函数签名只能是以下形式:
void func(vtkObject* obj, unsigned long eid, void* clientdata, void* calldata)其中
obj是调用事件的对象(即调用AddObserver()函数的主题对象,例如interactor);eid为所要监听的事件ID,VTK中的事件定义在vtkCommand.h文件中;clientdata是与vtkCalbackCommand实例相关的数据。简单说,是指回调函数里需要访问主程序里的数据时,由主程序向回调函数传递的数据,可以通过vtkCallbackCommand::SetClientData()函数设置。calldata是执行vtkObject::InvokeEvent()函数时,随着回调函数发送的数据,比如当调用ProgressEvent事件时,会自动发送当前的进度值作为calldata。2、创建一个vtkCallbackCommand对象,并调用vtkCallbackCommand::SetCallback()函数设置所定义的回调函数。
vtkSmartPointer<vtkCallbackCommand> mouseCallback = vtkSmartPointer<vtkCallbackCommand>::New(); mouseCallback->SetCallback(MyCallbackFunc);3、将vtkCallbackCommand对象添加到主题对象的观察者列表中。
interactor->AddObserver(vtkCommand::LeftButtonPressEvent, mouseCallback);CMakeLists.txt文件代码如下:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(CallbackFunction) FIND_PACKAGE(VTK REQUIRED) INCLUDE(${VTK_USE_FILE}) ADD_EXECUTABLE(CallbackFunction CallbackFunction.cpp) TARGET_LINK_LIBRARIES(CallbackFunction ${VTK_LIBRARIES})CallbackFunction.cpp文件代码如下:
#include <vtkSmartPointer.h> #include <vtkPNGReader.h> #include <vtkImageViewer2.h> #include <vtkRenderWindowInteractor.h> #include <vtkCallbackCommand.h> #include <vtkRenderWindow.h> #include <vtkRenderer.h> long pressCounts = 0; //第一步,定义回调函数。注意回调函数的签名,不能更改。 void MyCallbackFunc(vtkObject*, unsigned long eid, void* clientdata, void *calldata) { std::cout<<"You have clicked: "<<++pressCounts<<" times."<<std::endl; } int main() { vtkSmartPointer<vtkPNGReader> reader = vtkSmartPointer<vtkPNGReader>::New(); reader->SetFileName("E:\\TestData\\VTK-logo.png"); vtkSmartPointer<vtkImageViewer2> viewer = vtkSmartPointer<vtkImageViewer2>::New(); viewer->SetInputConnection(reader->GetOutputPort()); vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New(); viewer->SetupInteractor(interactor); viewer->Render(); viewer->GetRenderer()->SetBackground(1.0, 1.0, 1.0); viewer->SetSize(640, 480); viewer->GetRenderWindow()->SetWindowName("事件回调函数"); //第二步,设置回调函数。 vtkSmartPointer<vtkCallbackCommand> mouseCallback = vtkSmartPointer<vtkCallbackCommand>::New(); mouseCallback->SetCallback ( MyCallbackFunc ); //第三步,将vtkCallbackCommand对象添加到观察者列表。 interactor->SetRenderWindow(viewer->GetRenderWindow()); interactor->AddObserver(vtkCommand::LeftButtonPressEvent, mouseCallback); interactor->Initialize(); interactor->Start(); return EXIT_SUCCESS; }运行结果:
写vtkCommand的子类来实现观察者/命令模式,分为以下三个步骤。 1、从vtkCommand类派生出子类,并实现vtkCommand::Execute()函数,该函数原型是:
virtual void Exeute(vtkObject* caller, unsigned long eventId, void* callData) = 0;Execute()是纯虚函数,所以从vtkCommand派生的类都必须实现这个方法。
2、在主程序中实例化一个vtkCommand子类的对象以及调用相关的方法。
3、调用AddObserver()函数监听感兴趣的事件。
CMakeLists.txt文件代码如下:
CMAKE_MINIMUM_REQUIRED(VERSION 2.8) PROJECT(Command) FIND_PACKAGE(VTK REQUIRED) INCLUDE(${VTK_USE_FILE}) ADD_EXECUTABLE(Command Command.cpp) TARGET_LINK_LIBRARIES(Command ${VTK_LIBRARIES})Command.cpp文件代码如下:
#include <vtkSmartPointer.h> #include <vtkConeSource.h> #include <vtkPolyDataMapper.h> #include <vtkRenderWindow.h> #include <vtkRenderWindowInteractor.h> #include <vtkCamera.h> #include <vtkActor.h> #include <vtkRenderer.h> #include <vtkCommand.h> #include <vtkBoxWidget.h> #include <vtkTransform.h> #include <vtkInteractorStyleTrackballCamera.h> //第一步 class vtkMyCommand : public vtkCommand { public: static vtkMyCommand *New() { return new vtkMyCommand; } void SetObject(vtkConeSource* cone) { m_Cone = cone; } virtual void Execute(vtkObject *caller, unsigned long eventId, void* callData) { std::cout<<"Left button pressed.\n" <<"The Height: "<<m_Cone->GetHeight()<<"\n" <<"The Radius: "<<m_Cone->GetRadius()<<std::endl; } private: vtkConeSource *m_Cone; }; int main() { vtkSmartPointer<vtkConeSource> cone = vtkSmartPointer<vtkConeSource>::New(); cone->SetHeight( 3.0 ); cone->SetRadius( 1.0 ); cone->SetResolution( 10 ); vtkSmartPointer<vtkPolyDataMapper> coneMapper = vtkSmartPointer<vtkPolyDataMapper>::New(); coneMapper->SetInputConnection( cone->GetOutputPort() ); vtkSmartPointer<vtkActor> coneActor = vtkSmartPointer<vtkActor>::New(); coneActor->SetMapper( coneMapper ); vtkSmartPointer<vtkRenderer> ren1= vtkSmartPointer<vtkRenderer>::New(); ren1->AddActor( coneActor ); ren1->SetBackground( 0.1, 0.4, 0.9 ); vtkSmartPointer<vtkRenderWindow> renWin = vtkSmartPointer<vtkRenderWindow>::New(); renWin->AddRenderer( ren1 ); renWin->SetSize( 640, 480 ); renWin->Render(); renWin->SetWindowName("vtkCommand子类"); vtkSmartPointer<vtkRenderWindowInteractor> iren = vtkSmartPointer<vtkRenderWindowInteractor>::New(); iren->SetRenderWindow(renWin); vtkSmartPointer<vtkInteractorStyleTrackballCamera> style = vtkSmartPointer<vtkInteractorStyleTrackballCamera>::New(); iren->SetInteractorStyle(style); //第二步 vtkSmartPointer<vtkMyCommand> callback = vtkSmartPointer<vtkMyCommand>::New(); callback->SetObject(cone); //第三步 iren->AddObserver(vtkCommand::LeftButtonPressEvent, callback); iren->Initialize(); iren->Start(); return EXIT_SUCCESS; }运行结果:
在VTK中,用观察者/命令模式实现交互,一般有两种方法:1、实例化vtkCallbackCommand,将自己写的回调函数,设给其;2、从vtkCommand类派生出子类,重写Execute方法。