VTK Observer和Command模式详解

本文记录一些在VTK开发中遇到的一些细节问题

VTK 不显示窗口问题

需要添加入如下code

#include "vtkAutoInit.h"
VTK_MODULE_INIT(vtkRenderingOpenGL2);
VTK_MODULE_INIT(vtkInteractionStyle);

VTK Observer和Command 模式

vtkObject提供了AddObserver和InvokeEvent来支持添加观察者和执行事件。
其中AddObserver可以添加vtkCommand(或者其子类vtkCallbackCommand)和 类的成员函数。类成员函数可以有返值,可以终止事件路由。如下:

/**
   * Allow people to add/remove/invoke observers (callbacks) to any VTK
   * object.  This is an implementation of the subject/observer design
   * pattern. An observer is added by specifying an event to respond to
   * and a vtkCommand to execute. It returns an unsigned long tag which
   * can be used later to remove the event or retrieve the command.
   * When events are invoked, the observers are called in the order they
   * were added. If a priority value is specified, then the higher
   * priority commands are called first. A command may set an abort
   * flag to stop processing of the event. (See vtkCommand.h for more
   * information.)
   */
  unsigned long AddObserver(unsigned long event, vtkCommand *,
                            float priority=0.0f);

//@{
  /**
   * Overloads to AddObserver that allow developers to add class member
   * functions as callbacks for events.  The callback function can
   * be one of these two types:
   * \code
   * void foo(void);\n
   * void foo(vtkObject*, unsigned long, void*);
   * \endcode
   * If the callback is a member of a vtkObjectBase-derived object,
   * then the callback will automatically be disabled if the object
   * destructs (but the observer will not automatically be removed).
   * If the callback is a member of any other type of object, then
   * the observer must be removed before the object destructs or else
   * its dead pointer will be used the next time the event occurs.
   * Typical usage of these functions is as follows:
   * \code
   * SomeClassOfMine* observer = SomeClassOfMine::New();\n
   * to_observe->AddObserver(event, observer, \&SomeClassOfMine::SomeMethod);
   * \endcode
   * Note that this does not affect the reference count of a
   * vtkObjectBase-derived \c observer, which can be safely deleted
   * with the observer still in place. For non-vtkObjectBase observers,
   * the observer should never be deleted before it is removed.
   * Return value is a tag that can be used to remove the observer.
   */
  template <class U, class T>
  unsigned long AddObserver(unsigned long event,
    U observer, void (T::*callback)(), float priority=0.0f)
  {
    vtkClassMemberCallback<T> *callable =
      new vtkClassMemberCallback<T>(observer, callback);
    // callable is deleted when the observer is cleaned up (look at
    // vtkObjectCommandInternal)
    return this->AddTemplatedObserver(event, callable, priority);
  }
  template <class U, class T>
  unsigned long AddObserver(unsigned long event,
    U observer, void (T::*callback)(vtkObject*, unsigned long, void*),
    float priority=0.0f)
  {
    vtkClassMemberCallback<T> *callable =
      new vtkClassMemberCallback<T>(observer, callback);
    // callable is deleted when the observer is cleaned up (look at
    // vtkObjectCommandInternal)
    return this->AddTemplatedObserver(event, callable, priority);
  }

  /**
   * Allow user to set the AbortFlagOn() with the return value of the callback
   * method.
   */
  template <class U, class T>
  unsigned long AddObserver(unsigned long event,
    U observer, bool (T::*callback)(vtkObject*, unsigned long, void*),
    float priority=0.0f)
  {
    vtkClassMemberCallback<T> *callable =
      new vtkClassMemberCallback<T>(observer, callback);
    // callable is deleted when the observer is cleaned up (look at
    // vtkObjectCommandInternal)
    return this->AddTemplatedObserver(event, callable, priority);
  }

InvokeEvent主要有3种事件,一是passive的,最先执行,不会影响系统状态;二是focus的,不再执行其他命令;三是普通事件,最后执行。细节如下:

int vtkSubjectHelper::InvokeEvent(unsigned long event, void *callData,
                                   vtkObject *self)
{
  int focusHandled = 0;

  // When we invoke an event, the observer may add or remove observers.  To make
  // sure that the iteration over the observers goes smoothly, we capture any
  // change to the list with the ListModified ivar.  However, an observer may
  // also do something that causes another event to be invoked in this object.
  // That means that this method will be called recursively, which means that we
  // will obliterate the ListModified flag that the first call is relying on.
  // To get around this, save the previous ListModified value on the stack and
  // then restore it before leaving.
  int saveListModified = this->ListModified;
  this->ListModified = 0;

  // We also need to save what observers we have called on the stack (lest it
  // get overridden in the event invocation).  Also make sure that we do not
  // invoke any new observers that were added during another observer's
  // invocation.
  typedef std::vector<unsigned long> VisitedListType;
  VisitedListType visited;
  vtkObserver *elem = this->Start;
  // If an element with a tag greater than maxTag is found, that means it has
  // been added after InvokeEvent is called (as a side effect of calling an
  // element command. In that case, the element is discarded and not executed.
  const unsigned long maxTag = this->Count;

  // Loop two or three times, giving preference to passive observers
  // and focus holders, if any.
  //
  // 0. Passive observer loop
  //   Loop over all observers and execute those that are passive observers.
  //   These observers should not affect the state of the system in any way,
  //   and should not be allowed to abort the event.
  //
  // 1. Focus loop
  //   If there is a focus holder, loop over all observers and execute
  //   those associated with either focus holder. Set focusHandled to
  //   indicate that a focus holder handled the event.
  //
  // 2. Remainder loop
  //   If no focus holder handled the event already, loop over the
  //   remaining observers. This loop will always get executed when there
  //   is no focus holder.

  // 0. Passive observer loop
  //
  vtkObserver *next;
  while (elem)
  {
    // store the next pointer because elem could disappear due to Command
    next = elem->Next;
    if (elem->Command->GetPassiveObserver() &&
        (elem->Event == event || elem->Event == vtkCommand::AnyEvent) &&
        elem->Tag < maxTag)
    {
      VisitedListType::iterator vIter =
        std::lower_bound(visited.begin(), visited.end(), elem->Tag);
      if (vIter == visited.end() || *vIter != elem->Tag)
      {
        // Sorted insertion by tag to speed-up future searches at limited
        // insertion cost because it reuses the search iterator already at the
        // correct location
        visited.insert(vIter, elem->Tag);
        vtkCommand* command = elem->Command;
        command->Register(command);
        elem->Command->Execute(self,event,callData);
        command->UnRegister();
      }
    }
    if (this->ListModified)
    {
      vtkGenericWarningMacro(<<"Passive observer should not call AddObserver or RemoveObserver in callback.");
      elem = this->Start;
      this->ListModified = 0;
    }
    else
    {
      elem = next;
    }
  }

  // 1. Focus loop
  //
  if (this->Focus1 || this->Focus2)
  {
    elem = this->Start;
    while (elem)
    {
      // store the next pointer because elem could disappear due to Command
      next = elem->Next;
      if (((this->Focus1 == elem->Command) || (this->Focus2 == elem->Command)) &&
          (elem->Event == event || elem->Event == vtkCommand::AnyEvent) &&
          elem->Tag < maxTag)
      {
        VisitedListType::iterator vIter =
          std::lower_bound(visited.begin(), visited.end(), elem->Tag);
        if (vIter == visited.end() || *vIter != elem->Tag)
        {
          // Don't execute the remainder loop
          focusHandled = 1;
          // Sorted insertion by tag to speed-up future searches at limited
          // insertion cost because it reuses the search iterator already at the
          // correct location
          visited.insert(vIter, elem->Tag);
          vtkCommand* command = elem->Command;
          command->Register(command);
          command->SetAbortFlag(0);
          elem->Command->Execute(self,event,callData);
          // if the command set the abort flag, then stop firing events
          // and return
          if(command->GetAbortFlag())
          {
            command->UnRegister();
            this->ListModified = saveListModified;
            return 1;
          }
          command->UnRegister();
        }
      }
      if (this->ListModified)
      {
        elem = this->Start;
        this->ListModified = 0;
      }
      else
      {
        elem = next;
      }
    }
  }

  // 2. Remainder loop
  //
  if (!focusHandled)
  {
    elem = this->Start;
    while (elem)
    {
      // store the next pointer because elem could disappear due to Command
      next = elem->Next;
      if ((elem->Event == event || elem->Event == vtkCommand::AnyEvent) &&
          elem->Tag < maxTag)
      {
        VisitedListType::iterator vIter =
          std::lower_bound(visited.begin(), visited.end(), elem->Tag);
        if (vIter == visited.end() || *vIter != elem->Tag)
        {
          // Sorted insertion by tag to speed-up future searches at limited
          // insertion cost because it reuses the search iterator already at the
          // correct location
          visited.insert(vIter, elem->Tag);
          vtkCommand* command = elem->Command;
          command->Register(command);
          command->SetAbortFlag(0);
          elem->Command->Execute(self,event,callData);
          // if the command set the abort flag, then stop firing events
          // and return
          if(command->GetAbortFlag())
          {
            command->UnRegister();
            this->ListModified = saveListModified;
            return 1;
          }
          command->UnRegister();
        }
      }
      if (this->ListModified)
      {
        elem = this->Start;
        this->ListModified = 0;
      }
      else
      {
        elem = next;
      }
    }
  }

  this->ListModified = saveListModified;
  return 0;
}

成员函数的Command,在执行之后会终止事件的路由AbortFlagOn(); 细节如下:

// Internal observer used by vtkObject::AddTemplatedObserver to add a
// vtkClassMemberCallbackBase instance as an observer to an event.
class vtkObjectCommandInternal : public vtkCommand
{
  vtkObject::vtkClassMemberCallbackBase* Callable;
public:
  static vtkObjectCommandInternal* New()
    { return new vtkObjectCommandInternal(); }

  vtkTypeMacro(vtkObjectCommandInternal, vtkCommand);
  void Execute(
    vtkObject *caller, unsigned long eventId, void *callData) override
  {
    if (this->Callable)
    {
      this->AbortFlagOff();
      if((*this->Callable)(caller, eventId, callData))
      {
        this->AbortFlagOn();
      }
    }
  }

  // Takes over the ownership of \c callable.
  void SetCallable(vtkObject::vtkClassMemberCallbackBase* callable)
  {
    delete this->Callable;
    this->Callable = callable;
  }

protected:
  vtkObjectCommandInternal()
  {
    this->Callable = nullptr;
  }
  ~vtkObjectCommandInternal() override
  {
    delete this->Callable;
  }
};

//----------------------------------------------------------------------------
unsigned long vtkObject::AddTemplatedObserver(
  unsigned long event, vtkObject::vtkClassMemberCallbackBase* callable, float priority)
{
  vtkObjectCommandInternal* command = vtkObjectCommandInternal::New();
  // Takes over the ownership of \c callable.
  command->SetCallable(callable);
  unsigned long id = this->AddObserver(event, command, priority);
  command->Delete();
  return id;
}

所有事件都是由vtkRenderWindowInteractor触发,如下:

void vtkRenderWindowInteractor::LeftButtonPressEvent()
{
  if (!this->Enabled)
  {
    return;
  }

  // are we translating multitouch into gestures?
  if (this->RecognizeGestures)
  {
    if (!this->PointersDown[this->PointerIndex])
    {
      this->PointersDown[this->PointerIndex] = 1;
      this->PointersDownCount++;
    }
    // do we have multitouch
    if (this->PointersDownCount > 1)
    {
      // did we just transition to multitouch?
      if (this->PointersDownCount == 2)
      {
        this->InvokeEvent(vtkCommand::LeftButtonReleaseEvent, nullptr);
      }
      // handle the gesture
      this->RecognizeGesture(vtkCommand::LeftButtonPressEvent);
      return;
    }
  }

  this->InvokeEvent(vtkCommand::LeftButtonPressEvent, nullptr);
}

VTK Interactorstyle 和 vtkCommand之间的关系

当在style中addObserver(vtkComand), 则只执行vtkCommand中的Execute(), 可以Command中直接调用style的函数。

vtkInteractorStyle* style =
          vtkInteractorStyle::SafeDownCast(interactor->GetInteractorStyle());
        if (style)
        {
          style->OnMouseMove();
        }

具体的执行过程是: vtkRenderWindowInteractor: Start() -> vtkXRenderWindowInteractor:( StartEventLoop() -> ProcessEvents() -> DispatcherEvent() -> InvokeEvent() -> (vtkCallbackCommand)Execute() ) -> vtkInteractorStyle: ( ProcessEvent() -> InvokeEvent() -> Execute() ) -> Callback Command.
这里需要提到的是,vtkInteractorStyle在构造的时候,已经把ProcessEvents注册到EventCallbackCommand中了。

this->EventCallbackCommand->SetCallback(vtkInteractorStyle::ProcessEvents);

vtkInteractorStyle在SetInteractor的时候,添加了所有的事件,所有事件的函数是EventCallbackCommand,该类会执行Execute,分发所有的注册方法。通俗来说,style所有的Event都是一个callback:EventCallbackCommand,而他的具体函数是style的ProcessEvents. 然后ProcessEvents中去触发自己的事件还是observer的事件。

//------------------------------------------------------------------------------
// NOTE!!! This does not do any reference counting!!!
// This is to avoid some ugly reference counting loops
// and the benefit of being able to hold only an entire
// renderwindow from an interactor style doesn't seem worth the
// mess.   Instead the vtkInteractorStyle sets up a DeleteEvent callback, so
// that it can tell when the vtkRenderWindowInteractor is going away.

void vtkInteractorStyle::SetInteractor(vtkRenderWindowInteractor* i)
{
  if (i == this->Interactor)
  {
    return;
  }

  // if we already have an Interactor then stop observing it
  if (this->Interactor)
  {
    this->Interactor->RemoveObserver(this->EventCallbackCommand);
  }
  this->Interactor = i;

  // add observers for each of the events handled in ProcessEvents
  if (i)
  {
    i->AddObserver(vtkCommand::EnterEvent, this->EventCallbackCommand, this->Priority);

    i->AddObserver(vtkCommand::LeaveEvent, this->EventCallbackCommand, this->Priority);

    i->AddObserver(vtkCommand::MouseMoveEvent, this->EventCallbackCommand, this->Priority);

    i->AddObserver(vtkCommand::LeftButtonPressEvent, this->EventCallbackCommand, this->Priority);

    i->AddObserver(vtkCommand::LeftButtonReleaseEvent, this->EventCallbackCommand, this->Priority);

    i->AddObserver(
      vtkCommand::LeftButtonDoubleClickEvent, this->EventCallbackCommand, this->Priority);

    i->AddObserver(vtkCommand::MiddleButtonPressEvent, this->EventCallbackCommand, this->Priority);

那为什么style中添加了command后,自己对应的事件不执行了呢。原因是,内部进行了判断处理,如果有对应的observer则只执行oberser的command。如下:

    case vtkCommand::LeftButtonPressEvent:
      if (self->HandleObservers && self->HasObserver(vtkCommand::LeftButtonPressEvent))
      {
        self->InvokeEvent(vtkCommand::LeftButtonPressEvent, nullptr);
      }
      else
      {
        self->OnLeftButtonDown();
      }
      break;
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值