VTK开发笔记(五):示例Cone2,熟悉观察者模式,在Qt窗口中详解复现对应的Demo

若该文为原创文章,转载请注明出处

本文章博客地址:https://hpzwl.blog.csdn.net/article/details/151314851

长沙红胖子Qt(长沙创微智科)博文大全:开发技术集合(包含Qt实用技术、树莓派、三维、OpenCV、OpenGL、ffmpeg、OSG、单片机、软硬结合等等)持续更新中...

OSG、VTK、OSGEARTH三维开发专栏(点击传送门)

上一篇:《VTK开发笔记(四):示例Cone,创建圆锥体,在Qt窗口中详解复现对应的Demo

下一篇:敬请期待...

前言

  Vtk的了解学习途径跟随代码中的示例,循序渐进。

  本篇详细解析Cone2.exe,在Cone.exe基础上新增了观察者的概念,在Qt中复现一样的观察者。

Demo

  

观察者模式

概述

  观察者模式(Observer Pattern),允许一个对象(观察者)关注另一个对象(被观察者)的状态变化,并在事件发生时自动执行预定操作。观察者回调(Observer-Callback) 是一种事件驱动的编程机制,用于处理 VTK 对象状态变化或特定事件的响应。

核心概念

  事件(Event)VTK 对象在特定操作或状态变化时会触发事件,例如: 数据更新(如vtkCommand::ModifiedEvent) 渲染完成(如vtkCommand::EndEvent) 鼠标交互(如vtkCommand::LeftButtonPressEvent) 每个事件都有唯一的标识符(如枚举值或字符串)。

  观察者(Observer) 注册到 VTK 对象上,用于 "监听" 特定事件的对象。当被监听的事件触发时,观察者会执行关联的回调函数。 回调函数(Callback) 事件触发时实际执行的代码逻辑,通常是自定义函数或方法,用于响应事件(如更新 UI、处理数据、日志记录等)。

工作流程

  • 步骤一:定义回调函数:实现处理事件的逻辑(需符合 VTK 的回调接口规范)。
  • 步骤二:获取被观察者:确定需要监听的 VTK 对象(如vtkRenderer、vtkActor等)。
  • 步骤三:注册观察者:将回调函数与特定事件绑定到被观察者上。
  • 步骤四:事件触发与响应:当被观察者触发事件时,VTK 自动调用对应的回调函数。

简单示例

  

  

观察者实现vtkCommand

  vtkCommand是观察器/命令设计模式的实现。在这种设计模式中,可以"观察"vtkObject的任何实例,以了解它可能调用的任何事件。例如,vtkRenderer在开始渲染时调用StartEvent,在完成渲染时调用EndEvent。过滤器(vtkProcessObject的子类)在过滤器处理数据时调用StartEvent、ProgressEvent和EndEvent。事件的观察者是通过vtkObject中的AddObserver()方法添加的。AddObserver()除了需要一个事件id或名称外,还需要一个vtkCommand实例(或子类)。请注意,vtkCommand旨在被子类化,以便可以打包支持回调所需的信息。

  事件处理可以按优先级列表进行组织,因此可以通过设置AbortFlag变量截断特定事件的处理。优先级是通过以下方式设定的AddObserver()方法。默认情况下,优先级为0,具有相同优先级的事件将按第一个处理顺序中的最后一个处理。事件的排序/中止对于像3D小部件这样的东西很重要,如果选择了小部件,它们会处理事件(然后中止对该事件的进一步处理)。否则。事件被传递以供进一步处理。

  当vtkObject的实例调用事件时,它还会将可选的void指针传递给callData。这个callData在大多数时候都是空的。callData并不特定于某一类型的事件,而是特定于调用特定事件的vtkObject类型。例如,  vtkCommand::PickEvent由vtkProp使用空指针调用,但由vtkInteractiorStyleImage使用指向  vtkInteractitorStyleImage对象本身的指针调用。

  以下是可以使用非nullptr callData调用的事件列表:

  

  

复现Demo

  有一个很重要的点,这个示例代码是阻塞时的循环刷新,与Qt的基于消息的编程处理方式不一样,这是过程式的编程,我们复刻示例,保持一块Demo就一个函数,否则的话,可以使用Qt定时器来实现更新位置就可以了。

步骤一:创建圆锥体数据源

  

步骤二:创建多边形映射器

  

  

步骤三:创建演员类(类似osg模型结点)

  

  

步骤四:创建渲染器

  

  

步骤五:设置渲染器到渲染窗口

  

步骤六:设置观察者回调函数

  这里回调类,放在函数里面定义,方便归类demo

  

步骤七:用Qt的方式实现不阻塞又是过程化旋转

  为了看到更加清晰,我们设置过渡延迟一循环为1000ms:

  

  

运行效果

  

  

Demo源码

VTKWidget.cpp

cpp 复制代码
void VTKWidget::test_demo4_createCone()
{
    // 步骤一:创建圆锥体数据源
    vtkSmartPointer<vtkConeSource> pConeSource =
            VTKManager::createConeSource(0, 0, 0, 10, 30, 10);
    // 步骤二:创建多边形映射器
#if 0
    vtkSmartPointer<vtkPolyDataMapper> pPolyDataMapper =
            VTKManager::createPolyDataMapper(pConeSource);
#else
    vtkSmartPointer<vtkPolyDataMapper> pPolyDataMapper =
            VTKManager::createPolyDataMapper(pConeSource->GetOutputPort());
#endif
    // 步骤三:创建演员
    vtkSmartPointer<vtkActor> pActor =
            VTKManager::createActor(pPolyDataMapper);
    // 步骤四:创建渲染器
    vtkSmartPointer<vtkRenderer> pRenderer =
            VTKManager::createRenderer(pActor, 0.1, 0.2, 0.4);

    // 步骤五:渲染器添加到QVTKWidget渲染
    _pQVTKWidget->GetRenderWindow()->AddRenderer(pRenderer);

    // 步骤六:设置观察者,触发回调函数,交互回调
    class vtkMyCallback : public vtkCommand
    {
    public:
        static vtkMyCallback *New() {
            return new vtkMyCallback;
        }
        void Execute(vtkObject *caller, unsigned long, void*) override
        {
            // 注意:这个类在函数里面定义,其函数内部无法直接->或者.来得到成员函数
            vtkRenderer *pRenderer = reinterpret_cast<vtkRenderer*>(caller);
            LOG << pRenderer->GetActiveCamera()->GetPosition()[0]
                << pRenderer->GetActiveCamera()->GetPosition()[1]
                << pRenderer->GetActiveCamera()->GetPosition()[2];
        }
    };
    vtkMyCallback *pVtkMyCallback = vtkMyCallback::New();
    pRenderer->AddObserver(vtkCommand::StartEvent, pVtkMyCallback);
    pVtkMyCallback->Delete();

    // 步骤七:过程循环的方式实现旋转
    QElapsedTimer elapsedTimer;
    for(int index = 0; index < 360; index++)
    {
        LOG << index;
        if(!isVisible())
        {
            continue;
        }
        // 渲染一次
        _pQVTKWidget->GetRenderWindow()->Render();

        elapsedTimer.start();
        while(elapsedTimer.elapsed() < 100)
        {
            qApp->processEvents();
        }
        if(!isVisible())
        {
            continue;
        }
        // 渲染器相机绕焦点旋转
        VTKManager::rotateAzimuth(pRenderer, 1);
    }
}

VTKManager.cpp

cpp 复制代码
vtkSmartPointer<vtkConeSource> VTKManager::createConeSource(double x, double y, double z, double r, int h, int n)
{
    // 步骤一:智能指针定义
    vtkSmartPointer<vtkConeSource> pConeSource;
    // 步骤二:智能指针实例化
    pConeSource = vtkSmartPointer<vtkConeSource>::New();
    // 步骤三:设置中心坐标
    pConeSource->SetCenter(x, y, z);
    // 步骤三:设置半径
    pConeSource->SetRadius(r);
    // 步骤四:设置圆锥的高度
    pConeSource->SetHeight(h);
    // 步骤五:设置圆锥球体的经度分辨率,即横向的切片数量(横向/水平精细度)
    pConeSource->SetResolution(n);

    return pConeSource;
}
vtkSmartPointer<vtkPolyDataMapper> VTKManager::createPolyDataMapper(vtkAlgorithmOutput *pAlgorithmOutput)
{
    // 步骤一:智能指针定义
    vtkSmartPointer<vtkPolyDataMapper> pPolyDataMapper;
    // 步骤二:智能指针实例化
    pPolyDataMapper = vtkSmartPointer<vtkPolyDataMapper>::New();
    // 步骤三:设置
    pPolyDataMapper->SetInputConnection(pAlgorithmOutput);

    return pPolyDataMapper;
}
vtkSmartPointer<vtkActor> VTKManager::createActor(vtkPolyDataMapper *pPolyDataMapper)
{
    // 步骤一:智能指针定义
    vtkSmartPointer<vtkActor> pActor;
    // 步骤二:智能指针实例化
    pActor = vtkSmartPointer<vtkActor>::New();
    // 步骤三:设置映射器
    pActor->SetMapper(pPolyDataMapper);

    return pActor;
}

vtkSmartPointer<vtkRenderer> VTKManager::createRenderer(std::vector<vtkActor *> vectorPActor, double r, double g, double b)
{
    // 步骤一:智能指针定义
    vtkSmartPointer<vtkRenderer> pRenderer;
    // 步骤二:智能指针实例化
    pRenderer = vtkSmartPointer<vtkRenderer>::New();
    // 步骤三:设置映射器
    for(int index = 0; index < vectorPActor.size(); index++)
    {
        pRenderer->AddActor(vectorPActor.at(index));
    }
    // 步骤四:设置背景色
    pRenderer->SetBackground(r, g, b);

    return pRenderer;
}

工程模板v1.3.0

  

上一篇:《VTK开发笔记(四):示例Cone,创建圆锥体,在Qt窗口中详解复现对应的Demo

下一篇:敬请期待...

本文章博客地址:https://hpzwl.blog.csdn.net/article/details/151314851

相关推荐
用户805533698032 天前
不止三件套:QObject 属性系统全关键字与运行时反射!
c++·qt
xcyxiner2 天前
DicomViewer (vcpkg Windows和ubuntu编译)7
qt
Quz7 天前
QML Hello World 入门示例
qt
xcyxiner10 天前
DicomViewer (dcmtk读取dcm文件)5
qt
xcyxiner10 天前
DicomViewer (后台线程处理文件)4
qt
xcyxiner11 天前
DicomViewer (添加模型类)3
qt
xcyxiner11 天前
DicomViewer (目录调整) 2
qt
xcyxiner12 天前
dcmtk vtk vtk-dicom(gdcm) 编译(debug) v2
qt
桥田智能13 天前
桥田智能 QT-650S:面向白车身焊装的 800kg 重载快换解决方案
开发语言·qt·系统架构
森G14 天前
75、服务器源码解析---------云视频服务项目
linux·服务器·网络·c++·qt