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();
		}
相关推荐
南东山人1 小时前
一文说清:C和C++混合编程
c语言·c++
Ysjt | 深4 小时前
C++多线程编程入门教程(优质版)
java·开发语言·jvm·c++
ephemerals__4 小时前
【c++丨STL】list模拟实现(附源码)
开发语言·c++·list
Microsoft Word4 小时前
c++基础语法
开发语言·c++·算法
一只小小汤圆5 小时前
opencascade源码学习之BRepOffsetAPI包 -BRepOffsetAPI_DraftAngle
c++·学习·opencascade
legend_jz5 小时前
【Linux】线程控制
linux·服务器·开发语言·c++·笔记·学习·学习方法
嘿BRE5 小时前
【C++】几个基本容器的模拟实现(string,vector,list,stack,queue,priority_queue)
c++
ö Constancy6 小时前
c++ 笔记
开发语言·c++
fengbizhe6 小时前
笔试-笔记2
c++·笔记