本文记录一些在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的事件。文章来源:https://uudwc.com/A/gage
//------------------------------------------------------------------------------
// 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。如下:文章来源地址https://uudwc.com/A/gage
case vtkCommand::LeftButtonPressEvent:
if (self->HandleObservers && self->HasObserver(vtkCommand::LeftButtonPressEvent))
{
self->InvokeEvent(vtkCommand::LeftButtonPressEvent, nullptr);
}
else
{
self->OnLeftButtonDown();
}
break;