VTK 动画:利用 Qt 的时间控制

发布于:2024-04-18 ⋅ 阅读:(30) ⋅ 点赞:(0)

VTK 动画:利用 Qt 的时间控制

第一篇文章 《VTK 动画:框架、流程与实现》 讲到了 VTK 的动画框架、动画流程,并给出了一个简单的 VTK 动画程序。

第二篇文章 《VTK 动画:面向对象的设计》 介绍了面向对象的 VTK 动画框架设计,达到了动画场景和动画内容的隔离的效果。

视频参考:https://www.bilibili.com/video/BV1Ya4y1V7ed

VS2022 + QT6.2.3 + VTK9.3.0 环境配置

VTK9.3.0 的编译:使用 Cmake 对 VTK-9.3.0 进行编译

这里重点就是把QT的内容全设成YES,VTK_QT_VERSION 一定要设置为自己的版本,我的是 6。

VS2022 的插件 Qt Visual Studio Tools 的安装:Visual Studio 2022 中 Qt 开发环境的搭建

都配好后,新建 VS Qt Widgets 项目,再把 VTK 的路径配置一下就行了,这里不赘述。

还有就是,用户变量 Path 要配置 Qt mvsc bin 的路径,比如我的就是C:\Qt\Qt6.2.3\6.2.3\msvc2019_64\bin,如果你有多个 Qt 版本,要把当时使用的路径放到 Path 的靠前位置,不如有可能出错。

Qt 定时器类 QTimer

Qt定时器类QTimer是一个用于重复执行或延迟执行函数的类,它直接从QObject类继承而来,不是界面组件类。

它可以在一定时间间隔内发送一个信号,也可以在指定的时间后发送一个信号。QTimer是一个基于事件的定时器,即它使用Qt的事件循环来触发定时器事件。

要使用它,只需创建一个QTimer类对象,然后调用其 start() 函数开启定时器,此后QTimer对象就会周期性的发出 timeout() 信号。

槽函数:

// 构造函数
// 如果指定了父对象, 创建的堆内存可以自动析构
QTimer::QTimer(QObject *parent = nullptr);

// 设置定时器时间间隔为 msec 毫秒
// 默认值是0,一旦窗口系统事件队列中的所有事件都已经被处理完,一个时间间隔为0的QTimer就会触发
void QTimer::setInterval(int msec);
// 获取定时器的时间间隔, 返回值单位: 毫秒
int QTimer::interval() const;

// 根据指定的时间间隔启动或者重启定时器, 需要调用 setInterval() 设置时间间隔
[slot] void QTimer::start();
// 启动或重新启动定时器,超时间隔为msec毫秒。
[slot] void QTimer::start(int msec);
// 停止定时器。
[slot] void QTimer::stop();

// 设置定时器精度
/*
参数: 
    - Qt::PreciseTimer -> 精确的精度, 毫秒级
    - Qt::CoarseTimer  -> 粗糙的精度, 和1毫秒的误差在5%的范围内, 默认精度
    - Qt::VeryCoarseTimer -> 非常粗糙的精度, 精度在1秒左右
*/
void QTimer::setTimerType(Qt::TimerType atype);
Qt::TimerType QTimer::timerType() const;	// 获取当前定时器的精度

// 如果定时器正在运行,返回true; 否则返回false。
bool QTimer::isActive() const;

// 判断定时器是否只触发一次
bool QTimer::isSingleShot() const;
// 设置定时器是否只触发一次, 参数为true定时器只触发一次, 为false定时器重复触发, 默认为false
void QTimer::setSingleShot(bool singleShot);

信号:

这个类的信号只有一个,当定时器超时时,该信号就会被发射出来。给这个信号通过 conect() 关联一个槽函数,就可以在槽函数中处理超时事件了。

void QTimer::timeout();

静态函数:

/*
功能: 在msec毫秒后发射一次信号, 并且只发射一次
参数:
	- msec:     在msec毫秒后发射信号
	- receiver: 接收信号的对象地址
	- method:   槽函数地址
*/
[static] void QTimer::singleShot(
        int msec, const QObject *receiver, 
        PointerToMemberFunction method);

事件:

void timerEvent(QTimerEvent *event);

实现

QVTKAnimation.h:

#pragma once

#include <QtWidgets/QMainWindow>
#include "ui_QtVTKAnimation.h"

#include <QTimer>
#include <QVTKOpenGLStereoWidget.h>
#include <vtkCameraOrientationWidget.h>
#include <vtkInterpolateDataSetAttributes.h>

class QtVTKAnimation : public QMainWindow
{
	Q_OBJECT

public:
	QtVTKAnimation(QWidget* parent = nullptr);
	~QtVTKAnimation();

private:
	Ui::QtVTKAnimationClass ui;

	QVTKOpenGLStereoWidget* _pVTKWidget = NULL;

	vtkNew<vtkInterpolateDataSetAttributes> interpolate;
	int _nTimeStep = 0;
	void timerEvent(QTimerEvent* event);
};

QtVTKAnimation.cpp:

#include "QtVTKAnimation.h"

#include <vtkRenderWindow.h> 
#include <vtkProperty.h>
#include <vtkRenderer.h>
#include <vtkVectorText.h>
#include <vtkImplicitModeller.h>
#include <vtkContourFilter.h>
#include <vtkPolyDataMapper.h>
#include <vtkActor.h>
#include <vtkAnimationScene.h>
#include <vtkAnimationCue.h>

QtVTKAnimation::QtVTKAnimation(QWidget* parent)
	: QMainWindow(parent)
{
	ui.setupUi(this);

	_pVTKWidget = new QVTKOpenGLStereoWidget();
	this->setCentralWidget(_pVTKWidget);
	this->showMaximized();

	vtkNew<vtkRenderer> renderer;
	this->_pVTKWidget->renderWindow()->AddRenderer(renderer);
	this->_pVTKWidget->renderWindow()->Render();

	vtkNew<vtkVectorText> letterV;
	letterV->SetText("V");
	vtkNew<vtkVectorText> letterT;
	letterT->SetText("T");
	vtkNew<vtkVectorText> letterK;
	letterK->SetText("K");

	vtkNew<vtkImplicitModeller> bloddyV;
	bloddyV->SetInputConnection(letterV->GetOutputPort());
	bloddyV->SetMaximumDistance(0.2);
	bloddyV->SetSampleDimensions(50, 50, 12);
	bloddyV->SetModelBounds(-0.5, 1.5, -0.5, 1.5, -0.5, 0.5);
	vtkNew<vtkImplicitModeller> bloddyT;
	bloddyT->SetInputConnection(letterT->GetOutputPort());
	bloddyT->SetMaximumDistance(0.2);
	bloddyT->SetSampleDimensions(50, 50, 12);
	bloddyT->SetModelBounds(-0.5, 1.5, -0.5, 1.5, -0.5, 0.5);
	vtkNew<vtkImplicitModeller> bloddyK;
	bloddyK->SetInputConnection(letterK->GetOutputPort());
	bloddyK->SetMaximumDistance(0.2);
	bloddyK->SetSampleDimensions(50, 50, 12);
	bloddyK->SetModelBounds(-0.5, 1.5, -0.5, 1.5, -0.5, 0.5);

	// 插值实现动画过渡
	
	interpolate->AddInputConnection(bloddyV->GetOutputPort());
	interpolate->AddInputConnection(bloddyT->GetOutputPort());
	interpolate->AddInputConnection(bloddyK->GetOutputPort());
	interpolate->SetT(0.0);

	vtkNew<vtkContourFilter> bloddyIso;
	bloddyIso->AddInputConnection(interpolate->GetOutputPort());
	bloddyIso->SetValue(0, 0.1);

	vtkNew<vtkPolyDataMapper> bloddyMapper;
	bloddyMapper->AddInputConnection(bloddyIso->GetOutputPort());
	bloddyMapper->ScalarVisibilityOff();

	vtkNew<vtkActor> bloddyActor;
	bloddyActor->SetMapper(bloddyMapper);
	bloddyActor->GetProperty()->EdgeVisibilityOn();

	renderer->AddActor(bloddyActor);

	startTimer(100);
}

QtVTKAnimation::~QtVTKAnimation()
{}

void QtVTKAnimation::timerEvent(QTimerEvent * event)
{
	_nTimeStep++;
	int nLen = 100;
	if (_nTimeStep <= 100)
	{
		interpolate->SetT(_nTimeStep * 2.0 / nLen);
		interpolate->Modified(); // 更新
		this->_pVTKWidget->renderWindow()->Render(); // 渲染
	}
}

运行结果

创建了3个矢量对象:V、T、K,并网格化。

利用定时器类实现了它们转换的动画,利用 vtkInterpolateDataSetAttributes 实现了插值过渡。

在这里插入图片描述

在这里插入图片描述

在这里插入图片描述

参考

  1. https://www.bilibili.com/video/BV1Ya4y1V7ed
  2. https://blog.csdn.net/weixin_43780415/article/details/131389737
  3. https://blog.csdn.net/qq_45445740/article/details/127826774

网站公告

今日签到

点亮在社区的每一天
去签到