[vtkusers] SphereWidget InteractionEvent not triggered?

Alex Malyushytskyy alexmalvtk at gmail.com
Fri Mar 8 22:40:17 EST 2013


All I can say without long debugging , such behavior is mostly likely
related to
unexpected SetAbortFlag be called in the case when such observer is
added somewhere in vtkSphereWidget::ProcessEvents( probably
onMouseMove ),
Callback is listening to events from different objects:
interactor(mouse related events) and MySphereWidget ()
It looks like this is causing aborting LeftButtonReleaseEvent  and
widget never returns in initial state.
Such problem might or might not be fixed by setting priority
(AddObserver) to custom events,
 but this require deep understanding of vtkSphereWidget functionality.

With more confidence I can say that all this is result of attempt to
re-use EventCallbackCommand in subclass and can be avoided
by creating a new callback instance, so notification about events
would not mess with parent class behavior.
Code below seems works. I put //! comments do show the changes and
provide extra comments

Hope this helps


#include <vtkCallbackCommand.h>
#include <vtkCommand.h>
#include <vtkObjectFactory.h>
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkSmartPointer.h>
#include <vtkSphereWidget.h>
#include <vtkSphereRepresentation.h>

class MySphereWidget : public vtkSphereWidget
{
 public:
  static MySphereWidget* New();
  vtkTypeMacro(MySphereWidget, vtkSphereWidget);

  // Handles the events
  static void ProcessEvents(vtkObject* object,
                            unsigned long event,
                            void* clientdata,
                            void* calldata);

protected:
  MySphereWidget();

  //! our own callback command
  vtkSmartPointer<vtkCallbackCommand> myEventCallbackCommand;

};


MySphereWidget::MySphereWidget()
{
  //! add our own callback command, so we do not mess with
SphereWidget::ProcessEvents
  this->myEventCallbackCommand = vtkSmartPointer<vtkCallbackCommand>::New();
  this->myEventCallbackCommand->SetCallback( MySphereWidget::ProcessEvents );

  this->AddObserver(vtkCommand::StartInteractionEvent,
	  this->myEventCallbackCommand);

  this->AddObserver(vtkCommand::InteractionEvent,
                    this->myEventCallbackCommand);

  this->AddObserver(vtkCommand::EndInteractionEvent,
                   this->myEventCallbackCommand);

//! even we added observer event will never occur, line below will not work
//! if you would need listen to mouse events you might need overload
setEnabled function
  this->AddObserver(vtkCommand::MouseMoveEvent,
                   this->myEventCallbackCommand, this->Priority);
}

void MySphereWidget::ProcessEvents( vtkObject* object,
                                    unsigned long event,
                                    void* clientdata,
                                    void* calldata )
{
  MySphereWidget* self = reinterpret_cast<MySphereWidget *>( clientdata );

  switch(event)
    {
    case vtkCommand::StartInteractionEvent:
      std::cout << "StartInteractionEvent" << std::endl;
      break;
    case vtkCommand::EndInteractionEvent:
      std::cout << "EndInteractionEvent" << std::endl;
      break;
    case vtkCommand::InteractionEvent:
      std::cout << "InteractionEvent" << std::endl;
      break;

	case vtkCommand::MouseMoveEvent:
  //! this event should never occur cause we listen to the widget ,
not interactor
      std::cout << "Unexpected MouseMoveEvent " << std::endl;
      break;
    }

//! we do not need to call vtkSphereWidget::ProcessEvents here anymore
}

vtkStandardNewMacro(MySphereWidget);

int main(int, char *[])
{
  // Create  a renderer and render window
  vtkSmartPointer<vtkRenderer> renderer =
    vtkSmartPointer<vtkRenderer>::New();
  vtkSmartPointer<vtkRenderWindow> renderWindow =
    vtkSmartPointer<vtkRenderWindow>::New();
  renderWindow->AddRenderer(renderer);

  // Create an interactor
  vtkSmartPointer<vtkRenderWindowInteractor> renderWindowInteractor =
    vtkSmartPointer<vtkRenderWindowInteractor>::New();
  renderWindowInteractor->SetRenderWindow(renderWindow);

  vtkSmartPointer<MySphereWidget> sphereWidget =
    vtkSmartPointer<MySphereWidget>::New();
  sphereWidget->SetInteractor(renderWindowInteractor);
  sphereWidget->SetRepresentationToSurface();
  sphereWidget->HandleVisibilityOn();

  renderWindow->Render();
  renderWindowInteractor->Initialize();
  renderWindow->Render();
  sphereWidget->On();
  renderWindowInteractor->Start();

  return EXIT_SUCCESS;
}
On Fri, Mar 8, 2013 at 6:05 AM, David Doria <daviddoria at gmail.com> wrote:
> On Thu, Mar 7, 2013 at 2:11 PM, Alex Malyushytskyy <alexmalvtk at gmail.com> wrote:
>> David,
>>
>> On top of previous changes you need add an observer for event(s) you
>> are interested.
>> SphereWidget does it only for :
>> vtkCommand::MouseMoveEvent,
>>  vtkCommand::LeftButtonPressEvent,
>> vtkCommand::LeftButtonReleaseEvent,
>> vtkCommand::RightButtonPressEvent,
>> vtkCommand::RightButtonReleaseEvent,
>>
>> In order to to it in event handler you already have You can do it in
>> constructor in the referred example
>>
>> MySphereWidget::MySphereWidget()
>> {
>>   this->EventCallbackCommand->SetCallback( MySphereWidget::ProcessEvents );
>>   this->AddObserver(vtkCommand::InteractionEvent, this->EventCallbackCommand);
>>   this->AddObserver(vtkCommand::StartInteractionEvent,
>> this->EventCallbackCommand);
>> }
>>
>> A side note.
>> Keep in mind that previous change is also required - somebody still
>> needs to create InteractionEvent, etc.
>> Normally this is done in  vtkSphereWidget::ProcessEvents or more
>> precisely in the functions it calls
>>
>> Example is  OnLeftButtonUp().
>> If you changed behavior of such function and call it directly from
>> MySphereWidget::ProcessEvents
>> So You should skip calling vtkSphereWidget::ProcessEvents in case  of
>> vtkCommand::LeftButtonPressEvent
>> or it will be called twice (in current implementation)
>>
>>     case vtkCommand::LeftButtonPressEvent:
>>       std::cout << "LeftButtonPressEvent" << std::endl;
>>       self->OnLeftButtonDown();
>>       return; /// make sure parent process event is not called
>>       break;
>>     }
>>
>> Since currently in your new example you call parent class
>> implementation of OnLeftButtonDown, it will invoke required by
>> interaction
>> process event, but if you want to change widget behavior you will be
>> responsible to invoke  appropriate events.
>>
>> For example if you new implementation of OnLeftButtonDown still want
>> to start some interaction it should be doing something like:
>>
>>   this->EventCallbackCommand->SetAbortFlag(1);
>>   this->StartInteraction();
>>   this->InvokeEvent(vtkCommand::StartInteractionEvent,NULL);
>>   this->Interactor->Render();
>>
>>
>> Alex
>
> We're getting closer...
>
> I see what you're saying now that the widget is generating these
> events in it's On*Button*() functions. As a first step, I just want to
> show how to handle the events that are already generated (and not
> reimplement the functions that actually generate them, as I was
> accidentally doing).
>
> This shows that InteractionEvent and EndInteractionEvent are behaving
> as I'd expect:
> http://www.vtk.org/Wiki/VTK/Examples/Cxx/Widgets/SphereWidgetEvents
>
> That is, when I have the left mouse button held down and move the
> mouse around, InteractionEvent is repeatedly triggered. When I release
> the left mouse button, EndInteractionEvent is triggered. However, if
> you uncomment the AddObserver line for StartInteractionEvent, when I
> click the left mouse button, StartInteractionEvent is triggered, but
> when I release it, EndInteractionEvent is no longer triggered. Then
> even with the mouse button released, the sphere still drags around as
> it does normally when the mouse button is held down. Can you explain
> this? I am not doing anything different with this event than the
> others - shouldn't it just produce the std::cout <<
> "StartInteractionEvent" << std::endl; output and then proceed as
> normal?
>
> David



More information about the vtkusers mailing list