VTK随笔十三:QT与VTK的交互

一、基于 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();
		}
相关推荐
wen__xvn4 分钟前
每日一题洛谷P8664 [蓝桥杯 2018 省 A] 付账问题c++
c++·职场和发展·蓝桥杯
熬夜苦读学习24 分钟前
Linux进程信号
linux·c++·算法
榆榆欸40 分钟前
14.主从Reactor+线程池模式,Connection对象引用计数的深入分析
linux·服务器·网络·c++·tcp/ip
编程侦探1 小时前
【设计模式】原型模式:用“克隆”术让对象创建更灵活
c++·设计模式·原型模式
Once_day2 小时前
Linux错误(6)X64向量指令访问地址未对齐引起SIGSEGV
linux·c++·sse·x64·sigsegv·xmm0
JhonKI2 小时前
【从零实现Json-Rpc框架】- 项目实现 - 客户端注册主题整合 及 rpc流程示意
c++·qt·网络协议·rpc·json
__lost2 小时前
为什么new分配在堆上,函数变量在栈上+递归调用时栈内存的变化过程
c++·内存分配
序属秋秋秋3 小时前
算法基础_基础算法【位运算 + 离散化 + 区间合并】
c语言·c++·学习·算法·蓝桥杯
jyyyx的算法博客3 小时前
【再探图论】深入理解图论经典算法
c++·算法·图论
念_ovo3 小时前
【算法/c++】利用中序遍历和后序遍历建二叉树
数据结构·c++·算法