VTK交互
vtk交互功能主要是通过 观察者/命令模式(Observer/Command)来实现。
其是指 一个 对象 可以有多个观察者(Observer),当该对象的状态发生改变时,所以依赖于它的观察者(Observer)对象都得到通知而被自动更新。
命令模式是属于对象行为模式,它把请求封装为一个对象,并提供一致性发送请求的接口,当一个事件发生时,它不直接把事件传递给事件的调用者,而是在命令和调用者之间增加一个中间者,将这种直接关系切断,同时将两者都隔离。事件调用者只是和接口打交道,不和具体事件实现交互。
vtk有两种方式来实现观察者/命令模式:
- 事件回调函数
- vtkCommand派生具体子类
1、事件回调函数示例
(使用回调函数来实现 键盘 上键和下键 ,三维图像切片的切换)
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkLookupTable.h>
#include <vtkDataSetMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCaptionRepresentation.h>
#include <vtkMetaImageReader.h>
#include <vtkImageViewer2.h>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
#include <vtkCallbackCommand.h>
int minSlice = 0;
int maxSlice = 0;
int currentSlice = minSlice;
vtkSmartPointer<vtkImageViewer2> imageViewer;
void MyCallbackFunc(vtkObject* caller, unsigned long eid, void* clientdata, void* calldata)
{
if (eid == vtkCommand::KeyPressEvent) {
vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
std::string key = interactor->GetKeySym();
if (key == "Up") {
std::cout << "Up arrow key pressed! " << currentSlice<< std::endl;
currentSlice--;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
if (key == "Down") {
std::cout << "Down arrow key pressed! " << currentSlice<< std::endl;
currentSlice++;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
}
}
int main(int argc, char* argv[])
{
// 1- read file
vtkSmartPointer<vtkMetaImageReader> reader = vtkSmartPointer<vtkMetaImageReader>::New();
reader->SetFileName("D:\\Projects\\visualStudio2019\\za\\qtVtkL01\\data\\brain.mhd");
reader->Update();
// 创建图像查看器
imageViewer = vtkSmartPointer<vtkImageViewer2>::New();
vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
imageViewer->SetInputConnection(reader->GetOutputPort());
imageViewer->SetColorLevel(500);
imageViewer->SetColorWindow(2000);
imageViewer->SetSliceOrientationToXY();
// 获取切片范围
minSlice = imageViewer->GetSliceMin();
maxSlice = imageViewer->GetSliceMax();
currentSlice = minSlice + 40;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
//Step1:设置事件回调函数
vtkSmartPointer<vtkCallbackCommand> mouseCallback = vtkSmartPointer<vtkCallbackCommand>::New();
mouseCallback->SetCallback(MyCallbackFunc); //
//Step2:将vtkCallbackCommand对象添加到观察者列表。
interactor->SetRenderWindow(imageViewer->GetRenderWindow()); //唤醒显示窗口
// 为interactor 添加 observer ,并设置观察的事件
interactor->AddObserver(vtkCommand::KeyPressEvent, mouseCallback);
imageViewer->SetupInteractor(interactor);
interactor->Initialize();
interactor->Start();
return EXIT_SUCCESS;
}
其步骤可以总结为三个步骤:
- 定义回调函数
其函数形式只能为:
void long MyCallbackFunc(vtkObject* caller, unsigned long eid, void* clientdata, void* calldata);
caller // 调用事件的对象,调用AddObserver函数的对象,本例中为 interactor
eid // 所要监听的事件id,本例中为 vtkCommand::KeyPressEven
clientdata //是与VTKCallbackCommand实例相关的数据,简单地说,是指回调函数里需要访问主程序里面得数据时,由主程序向回调函数传递的数据
calldata // 是执行vtkObject::InvokeEvent()函数时,随着回调函数发送得数据,比如说,当调用ProgressEvent事件时,会自动发送当前的进度值作为callback
- 创建 vtkCallbackCommand 对象,并调用 SetCallback 设置回调函数
vtkSmartPointer<vtkCallbackCommand> mouseCallback = vtkSmartPointer<vtkCallbackCommand>::New();
mouseCallback->SetCallback(MyCallbackFunc); //
- 将 vtkCallbackCommand 对象添加到对象的观察者列表中
interactor->SetRenderWindow(imageViewer->GetRenderWindow()); //唤醒显示窗口
// 为interactor 添加 observer ,并设置观察的事件
interactor->AddObserver(vtkCommand::KeyPressEvent, mouseCallback);
参考文章:https://www.cnblogs.com/ybqjymy/p/14244495.html
2、vtkCommand派生具体子类 示例
(使用vtkCommand派生子类来实现 键盘 上键和下键 ,三维图像切片的切换)
#include <vtkSmartPointer.h>
#include <vtkActor.h>
#include <vtkLookupTable.h>
#include <vtkDataSetMapper.h>
#include <vtkRenderWindow.h>
#include <vtkRenderer.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkCaptionRepresentation.h>
#include <vtkMetaImageReader.h>
#include <vtkImageViewer2.h>
#include <vtkAutoInit.h>
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);
VTK_MODULE_INIT(vtkRenderingFreeType);
#include <vtkCallbackCommand.h>
int minSlice = 0;
int maxSlice = 0;
int currentSlice = minSlice;
vtkSmartPointer<vtkImageViewer2> imageViewer;
class myCallback : public vtkCommand
{
public:
static myCallback* New() {
return new myCallback;
}
virtual void Execute(vtkObject* caller, unsigned long eventId, void* callData) {
if (eventId == vtkCommand::KeyPressEvent) {
vtkRenderWindowInteractor* interactor = static_cast<vtkRenderWindowInteractor*>(caller);
std::string key = interactor->GetKeySym();
if (key == "Up") {
std::cout << "Up arrow key pressed! " << currentSlice << std::endl;
currentSlice--;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
if (key == "Down") {
std::cout << "Down arrow key pressed! " << currentSlice << std::endl;
currentSlice++;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
}
}
}
};
int main(int argc, char* argv[])
{
// 1- read file
vtkSmartPointer<vtkMetaImageReader> reader = vtkSmartPointer<vtkMetaImageReader>::New();
reader->SetFileName("brain.mhd");
reader->Update();
// 创建图像查看器
imageViewer = vtkSmartPointer<vtkImageViewer2>::New();
vtkSmartPointer<vtkRenderWindowInteractor> interactor = vtkSmartPointer<vtkRenderWindowInteractor>::New();
imageViewer->SetInputConnection(reader->GetOutputPort());
imageViewer->SetColorLevel(500);
imageViewer->SetColorWindow(2000);
imageViewer->SetSliceOrientationToXY();
// 获取切片范围
minSlice = imageViewer->GetSliceMin();
maxSlice = imageViewer->GetSliceMax();
currentSlice = minSlice + 40;
imageViewer->SetSlice(currentSlice);
imageViewer->Render();
//Step1:
vtkSmartPointer<myCallback> Callback = vtkSmartPointer<myCallback>::New();
//Step2:
interactor->SetRenderWindow(imageViewer->GetRenderWindow()); //唤醒显示窗口
// 为interactor 添加 observer ,并设置观察的事件
interactor->AddObserver(vtkCommand::KeyPressEvent, Callback);
imageViewer->SetupInteractor(interactor);
interactor->Initialize();
interactor->Start();
return EXIT_SUCCESS;
}
其步骤也可以总结为三个:
- 从vtkCommand派生出子类,实现vtkCommand::Execute()虚函数 [事件发生要执行的操作写入此函数体内]
- 创建派生出子类的实例对象
- 调用观察者函数
interactor->AddObserver(vtkCommand::KeyPressEvent, Callback);
interactor通过AddObserve会监听键盘事件的发生与否,若发生,则会调用相关的Execute()函数。