一、基于 Ot的 VTK 应用程序
以 VTK 读入一幅 JPG 图像,然后在 Qt 界面上使用 VTK 显示该图像为例,演示QT与VTK的交互。
1、创建QT项目QT_VTK_Demo
2、配置VTK库
在CMakeLists.txt中添加如下代码:
配置完成后重新打开工程加载VTK库。
3、编写代码
在mainwindow.ui中添加一个OpenGL Widget,提升为QVTKOpenGLNativeWidget ,头文件QVTKOpenGLNativeWidget.h
添加一个打开文件的菜单
mainwindow.h
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <vtkEventQtSlotConnect.h>
#include <vtkImageViewer2.h>
#include <vtkRenderer.h>
QT_BEGIN_NAMESPACE
namespace Ui {
class MainWindow;
}
QT_END_NAMESPACE
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
MainWindow(QWidget *parent = nullptr);
~MainWindow();
private slots:
void on_action_triggered(); //响应打开图像文件的槽函数
void updateCoords(vtkObject* obj);//响应鼠标移动的消息,实时输出鼠标的当前位置
private:
Ui::MainWindow *ui;
vtkEventQtSlotConnect* m_Connections;
vtkSmartPointer<vtkImageViewer2> m_pImageViewer;
vtkSmartPointer<vtkRenderer> m_pRenderder;
};
#endif // MAINWINDOW_H
mainwindow.cpp
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <QDir>
#include <QFileDialog>
#include <vtkRenderWindow.h>
#include <vtkJPEGReader.h>
#include <vtkImageActor.h>
#include <vtkEventQtSlotConnect.h>
MainWindow::MainWindow(QWidget *parent)
: QMainWindow(parent)
, ui(new Ui::MainWindow)
{
ui->setupUi(this);
m_pImageViewer = vtkSmartPointer< vtkImageViewer2 >::New();
m_pRenderder = vtkSmartPointer< vtkRenderer >::New();
m_pRenderder->SetBackground(1.0,1.0,1.0);
// 设置m_QVTKWidget的渲染器
ui->renWin->renderWindow()->AddRenderer(m_pRenderder);
m_Connections = vtkEventQtSlotConnect::New();
m_Connections->Connect(ui->renWin->renderWindow()->GetInteractor(), vtkCommand::MouseMoveEvent,this,SLOT(updateCoords(vtkObject*)));
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::on_action_triggered()
{
QString filter;
filter = "JPEG image file (*.jpg *.jpeg)";
QDir dir;
QString fileName = QFileDialog::getOpenFileName( this, QString(tr("打开图像")), dir.absolutePath() , filter );
if ( fileName.isEmpty() == true ) return;
// 用vtkJPEGReader读取JPG图像
vtkSmartPointer<vtkJPEGReader> reader = vtkSmartPointer<vtkJPEGReader>::New();
reader->SetFileName(fileName.toUtf8().data());
reader->Update();
// 将reader的输出作为m_pImageViewer的输入,并设置m_pImageViewer与渲染器m_pRenderer的关联
m_pImageViewer->SetInputData(reader->GetOutput());
m_pImageViewer->UpdateDisplayExtent();
m_pImageViewer->SetRenderWindow(ui->renWin->renderWindow());
m_pImageViewer->SetRenderer(m_pRenderder);
m_pImageViewer->SetupInteractor(ui->renWin->renderWindow()->GetInteractor());
m_pImageViewer->SetSliceOrientationToXY(); //默认就是这个方向的
m_pImageViewer->GetImageActor()->InterpolateOff();
m_pRenderder->ResetCamera();
m_pRenderder->DrawOn();
ui->renWin->renderWindow()->Render();
}
void MainWindow::updateCoords(vtkObject* obj)
{
// 获取交互器
vtkRenderWindowInteractor* iren = vtkRenderWindowInteractor::SafeDownCast(obj);
// 获取鼠标的当前位置
int event_pos[2];
iren->GetEventPosition(event_pos);
QString str = QString("x=%1 : y=%2").arg(event_pos[0]).arg(event_pos[1]);
ui->statusbar->showMessage(str);
}
运行效果:
vtkEventQtSlotConnect 类可以实现 VTK 的事件与 Qt 的槽函数的连接。示例中演示了当移动鼠标时,在窗口上显示当前鼠标的像素坐标值。
在VTK中,可以通过两种方式来实现观察者/命令模式实现交互行为,它们分别是使用事件回调函数以及从 vtkCommand 派生出具体的子类。
1) 事件回调函数
在类 vtkObject 中,有如下函数:
unsigned long AddObserver(unsigned long event, vtkCommand*, float priority = 0.0f);
unsigned long AddObserver(const char* event, vtkCommand*, float priority = 0.0f);
函数的作用就是针对某个事件添加观察者到某个 VTK 对象中,当该对象发生观察者感兴趣的事件时,就会自动调用回调函数,执行相关的操作。
2)vtkCommand 子类
观察者/命令模式除了使用事件回调函数外,还可以直接从vtkCommand 类中派生出子类来实现。
vtkRenderWindowInteractor 提供了一种独立于平台的交互机制,用来响应不同平台的鼠标、按键和时钟等消息。当渲染窗口中有事件发生时,如单击消息,vkRenderWimndowInteractor内部会调用与平台相关的子类,将该消息转换成对应平台的消息。
二、选择拾取
选择拾取是人机交互过程的一个重要功能。
1. 点拾取
vtkPointPicker是vtkPicker的子类,它返回选择点的ID以及坐标。它也是通过发射光线与Actor相交而拾取对象的,除了返回坐标值,Actor和Mapper,它也返回在指定容差内沿着光线,最靠近光线的那个点的ID。vtkPointPicker::GetPointId()可以获取点的ID号。
vtkPointPicker不能单一拾取,其速度比vtkPicker慢,但比vtkCellPicker要快。
2. 单元拾取
vtkCellPicker是vtkPicker的子类,它返回选择单元的ID以及坐标。它也是通过发射光线与Actor相交而拾取对象的,除了返回坐标值,Actor和Mapper,它也返回在指定容差内沿着光线,最靠近光线的那个单元的ID。vtkPointPicker::GetCellId()可以获取单元的ID号。vtkCellPicker可以单一拾取,其速度是所有Picker中最慢的,当然,提供的信息也是最丰富的。
3. Actor 的拾取(vtkPropPicker)
vtkPropPicker是基于图形硬件拾取一个Actor/Prop实例,速度比vtkCellPicker/vtkPointPicker快。这个类在世界坐标系统中判定Actor/Prop和拾取位置,对于点和单元的ID则不作判定。
vtkSmartPointer<vtkPropPicker>
m_pPropPicker{ nullptr };
m_pPropPicker = vtkSmartPointer<vtkPropPicker>::New();
//启用拾取列表,之后的拾取仅从该列表内匹配
m_pPropPicker->SetPickFromList(true);
//添加Actor
vtkNew<vtkActor> actor;
vtkNew<vtkActor> actor1;
m_pPropPicker->AddPickList(actor);
m_pPropPicker->AddPickList(actor1);
//拾取
if (m_pPropPicker->Pick(displayPos[0], displayPos[1], 0, DefaultRenderer) > 0)
{
//拾取结果是Assembly
auto pPickAssembly = m_pPropPicker->GetAssembly();
//拾取结果是Actor
auto pPickActor = m_pPropPicker->GetActor();
}