011_BestMPRBaseVtk Qt交互器QVTKInteractor

638 阅读4分钟

本文已参与「新人创作礼」活动,一起开启掘金创作之路。

头图

BestMPRBaseVtk 交互

这两天折腾了好几个交互方式,但是终究还是没有理解其中的奥,所以决定好好看看vtkQt 的交互。


BestMPRBaseVtk 交互1 源码分析2 QVTKInteractor3 QVTKInteractor 源代码QVTKInteractor.hQVTKInteractor.cpp☞ 源码

关键字: QVTKInteractor交互器Qtvtk关键字5

1 源码分析

 void QVTKOpenGLNativeWidget::setRenderWindow(vtkGenericOpenGLRenderWindow* win)
 {
   if (this->RenderWindow == win)
   {
     return;
   }
 ​
   // this will release all OpenGL resources associated with the old render
   // window, if any.
   if (this->RenderWindowAdapter)
   {
     this->makeCurrent();
     this->RenderWindowAdapter.reset(nullptr);
   }
   this->RenderWindow = win;
   if (this->RenderWindow)
   {
     this->RenderWindow->SetReadyForRendering(false);
 ​
     // if an interactor wasn't provided, we'll make one by default
     if (!this->RenderWindow->GetInteractor())
     {
       // create a default interactor
       vtkNew<QVTKInteractor> iren;
       // iren->SetUseTDx(this->UseTDx);
       this->RenderWindow->SetInteractor(iren);
       iren->Initialize();
 ​
       // now set the default style
       vtkNew<vtkInteractorStyleTrackballCamera> style;
       iren->SetInteractorStyle(style);
     }
 ​
     if (this->isValid())
     {
       // this typically means that the render window is being changed after the
       // QVTKOpenGLNativeWidget has initialized itself in a previous update
       // pass, so we emulate the steps to ensure that the new vtkRenderWindow is
       // brought to the same state (minus the actual render).
       this->makeCurrent();
       this->initializeGL();
       this->updateSize();
     }
   }
 }

重点

  if (!this->RenderWindow->GetInteractor())
     {
       // create a default interactor
       vtkNew<QVTKInteractor> iren;
       // iren->SetUseTDx(this->UseTDx);
       this->RenderWindow->SetInteractor(iren);
       iren->Initialize();
       // now set the default style
       vtkNew<vtkInteractorStyleTrackballCamera> style;
       iren->SetInteractorStyle(style);
     }

翻译官方源码注释,这段代码意思就是创建一个默认的交互器QVTKInteractor类型的。并调用当前RenderWindowSetInteractor()接口,把创建的QVTKInteractor类型的交互器传给RenderWindow,完成后交互器调用自己的Initialize()进行初始化。完后创建一个vtkInteractorStyleTrackballCamera类型的交互器样式,并将这个样式通过交互器的SetInteractorStyle()接口传输给交互器。

image-20211221134439359

2 QVTKInteractor

官网给的说明很少,只是说QVTKInteractorQVTKOpenGLNativeWidget和(QVTKWiget)的交互器。将Qt 的事件中继到vtk中。

image-20211221135125732

从下图可以看出,QVTKInteractor是直接继承于vtkRenderWindowInteractor的一个分支,而 vtkRenderWindowInteractor是一个独立于平台的渲染窗口交互器,包括拾取和帧速率控制等。详细的后面再扒拉。今天重点还是研究QVTKInteractor.

image-20211221135957554

3 QVTKInteractor 源代码

QVTKInteractor 源代码如下,大致看了一下,完全搞不懂事干啥,大致感觉就是分几个平台,来监听硬件信号。不过在代码里面我到时发现了一个能认识的东西QEvent,我们前面使用的wheelEvent时间就是继承他的。所以底层的原理应该一样的吧。

QVTKInteractor.h


#ifndef Q_VTK_INTERACTOR_H
#define Q_VTK_INTERACTOR_H

#include "QVTKWin32Header.h"
#include "vtkGUISupportQtModule.h" // For export macro
#include <QtCore/QObject>
#include <vtkCommand.h>
#include <vtkRenderWindowInteractor.h>

#include "vtkTDxConfigure.h" // defines VTK_USE_TDX
#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
class vtkTDxWinDevice;
#endif
#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
class vtkTDxMacDevice;
#endif
#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
class vtkTDxDevice;
class vtkTDxUnixDevice;
#endif

class QVTKInteractorInternal;

/**
 * @class QVTKInteractor
 * @brief - an interactor for QVTKOpenGLNativeWidget (and QVTKWiget).
 *
 * QVTKInteractor handles relaying Qt events to VTK.
 * @sa QVTKOpenGLNativeWidget
 */

class VTKGUISUPPORTQT_EXPORT QVTKInteractor : public vtkRenderWindowInteractor
{
public:
  static QVTKInteractor* New();
  vtkTypeMacro(QVTKInteractor, vtkRenderWindowInteractor);

  /**
   * Enum for additional event types supported.
   * These events can be picked up by command observers on the interactor.
   */
  enum vtkCustomEvents
  {
    ContextMenuEvent = vtkCommand::UserEvent + 100,
    DragEnterEvent,
    DragMoveEvent,
    DragLeaveEvent,
    DropEvent
  };

  /**
   * Overloaded terminate app, which does nothing in Qt.
   * Use qApp->exit() instead.
   */
  void TerminateApp() override;

  /**
   * Overloaded start method does nothing.
   * Use qApp->exec() instead.
   */
  void Start() override;
  void Initialize() override;

  /**
   * Start listening events on 3DConnexion device.
   */
  virtual void StartListening();

  /**
   * Stop listening events on 3DConnexion device.
   */
  virtual void StopListening();

  /**
   * timer event slot
   */
  virtual void TimerEvent(int timerId);

#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
  virtual vtkTDxUnixDevice* GetDevice();
  virtual void SetDevice(vtkTDxDevice* device);
#endif

protected:
  // constructor
  QVTKInteractor();
  // destructor
  ~QVTKInteractor() override;

  // create a Qt Timer
  int InternalCreateTimer(int timerId, int timerType, unsigned long duration) override;
  // destroy a Qt Timer
  int InternalDestroyTimer(int platformTimerId) override;
#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
  vtkTDxWinDevice* Device;
#endif
#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
  vtkTDxMacDevice* Device;
#endif
#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
  vtkTDxUnixDevice* Device;
#endif

private:
  QVTKInteractorInternal* Internal;

  QVTKInteractor(const QVTKInteractor&) = delete;
  void operator=(const QVTKInteractor&) = delete;
};

#endif

QVTKInteractor.cpp



#ifdef _MSC_VER
// Disable warnings that Qt headers give.
#pragma warning(disable : 4127)
#pragma warning(disable : 4512)
#endif

#include "QVTKInteractor.h"
#include "QVTKInteractorInternal.h"

#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
#include "vtkTDxWinDevice.h"
#endif

#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
#include "vtkTDxMacDevice.h"
#endif

#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
#include "vtkTDxUnixDevice.h"
#endif

#include <QEvent>
#include <QResizeEvent>
#include <QSignalMapper>
#include <QTimer>

#include "vtkCommand.h"
#include "vtkObjectFactory.h"
#include "vtkRenderWindow.h"

QVTKInteractorInternal::QVTKInteractorInternal(QVTKInteractor* p)
  : Parent(p)
{
  this->SignalMapper = new QSignalMapper(this);
  QObject::connect(this->SignalMapper, SIGNAL(mapped(int)), this, SLOT(TimerEvent(int)));
}

QVTKInteractorInternal::~QVTKInteractorInternal() {}

void QVTKInteractorInternal::TimerEvent(int id)
{
  Parent->TimerEvent(id);
}

/*! allocation method for Qt/VTK interactor
 */
vtkStandardNewMacro(QVTKInteractor);

/*! constructor for Qt/VTK interactor
 */
QVTKInteractor::QVTKInteractor()
{
  this->Internal = new QVTKInteractorInternal(this);

#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
  this->Device = vtkTDxWinDevice::New();
#endif
#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
  this->Device = vtkTDxMacDevice::New();
#endif
#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
  this->Device = 0;
#endif
}

void QVTKInteractor::Initialize()
{
#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
  if (this->UseTDx)
  {
    // this is QWidget::winId();
    HWND hWnd = static_cast<HWND>(this->GetRenderWindow()->GetGenericWindowId());
    if (!this->Device->GetInitialized())
    {
      this->Device->SetInteractor(this);
      this->Device->SetWindowHandle(hWnd);
      this->Device->Initialize();
    }
  }
#endif
#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
  if (this->UseTDx)
  {
    if (!this->Device->GetInitialized())
    {
      this->Device->SetInteractor(this);
      // Do not initialize the device here.
    }
  }
#endif
  this->Initialized = 1;
  this->Enable();
}

#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
// ----------------------------------------------------------------------------
vtkTDxUnixDevice* QVTKInteractor::GetDevice()
{
  return this->Device;
}

// ----------------------------------------------------------------------------
void QVTKInteractor::SetDevice(vtkTDxDevice* device)
{
  if (this->Device != device)
  {
    this->Device = static_cast<vtkTDxUnixDevice*>(device);
  }
}
#endif

/*! start method for interactor
 */
void QVTKInteractor::Start()
{
  vtkErrorMacro(<< "QVTKInteractor cannot control the event loop.");
}

/*! terminate the application
 */
void QVTKInteractor::TerminateApp()
{
  // we are in a GUI so let's terminate the GUI the normal way
  // qApp->exit();
}

// ----------------------------------------------------------------------------
void QVTKInteractor::StartListening()
{
#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
  if (this->Device->GetInitialized() && !this->Device->GetIsListening())
  {
    this->Device->StartListening();
  }
#endif
#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
  if (this->UseTDx && !this->Device->GetInitialized())
  {
    this->Device->Initialize();
  }
#endif
#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
  if (this->UseTDx && this->Device != 0)
  {
    this->Device->SetInteractor(this);
  }
#endif
}

// ----------------------------------------------------------------------------
void QVTKInteractor::StopListening()
{
#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
  if (this->Device->GetInitialized() && this->Device->GetIsListening())
  {
    this->Device->StopListening();
  }
#endif
#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
  if (this->UseTDx && this->Device->GetInitialized())
  {
    this->Device->Close();
  }
#endif
#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
  if (this->UseTDx && this->Device != 0)
  {
    // this assumes that a outfocus event is emitted prior
    // a infocus event on another widget.
    this->Device->SetInteractor(0);
  }
#endif
}

/*! handle timer event
 */
void QVTKInteractor::TimerEvent(int timerId)
{
  if (!this->GetEnabled())
  {
    return;
  }
  this->InvokeEvent(vtkCommand::TimerEvent, (void*)&timerId);

  if (this->IsOneShotTimer(timerId))
  {
    this->DestroyTimer(timerId); // 'cause our Qt timers are always repeating
  }
}

/*! constructor
 */
QVTKInteractor::~QVTKInteractor()
{
  delete this->Internal;
#if defined(VTK_USE_TDX) && defined(Q_OS_WIN)
  this->Device->Delete();
#endif
#if defined(VTK_USE_TDX) && defined(Q_OS_MAC)
  this->Device->Delete();
#endif
#if defined(VTK_USE_TDX) && (defined(Q_WS_X11) || defined(Q_OS_LINUX))
  this->Device = 0;
#endif
}

/*! create Qt timer with an interval of 10 msec.
 */
int QVTKInteractor::InternalCreateTimer(
  int timerId, int vtkNotUsed(timerType), unsigned long duration)
{
  QTimer* timer = new QTimer(this->Internal);
  timer->start(duration);
  this->Internal->SignalMapper->setMapping(timer, timerId);
  QObject::connect(timer, SIGNAL(timeout()), this->Internal->SignalMapper, SLOT(map()));
  int platformTimerId = timer->timerId();
  this->Internal->Timers.insert(
    QVTKInteractorInternal::TimerMap::value_type(platformTimerId, timer));
  return platformTimerId;
}

/*! destroy timer
 */
int QVTKInteractor::InternalDestroyTimer(int platformTimerId)
{
  QVTKInteractorInternal::TimerMap::iterator iter = this->Internal->Timers.find(platformTimerId);
  if (iter != this->Internal->Timers.end())
  {
    iter->second->stop();
    iter->second->deleteLater();
    this->Internal->Timers.erase(iter);
    return 1;
  }
  return 0;
}

☞ 源码

源码链接:GitHub仓库自取

使用方法:☟☟☟

源码


博客签名2021