Why does vtkRenderWindowInteractor eat up LeftButtonRelease event?

@ken-martin @utkarshayachit There is no way to listen to LeftButtonReleaseEvent on an interactor.
A quick google search shows up this, http://vtk.1045678.n5.nabble.com/Mouse-button-release-event-is-still-broken-in-VTK-6-0-0-td5724762.html thread. It’s sort of dead since 2013.

The workaround is

iStyle = interactor->GetInteractorStyle()->GetCurrentStyle()
iStyle->HandleObserversOn();
iStyle->AddObserver(vtkCommand::LeftButtonReleaseEvent, ...)

The first line gets a bit complicated if the interactor is not using vtkInteractorStyleSwitch. It gets all the more complicated when you start pressing the ‘c’, ‘a’, ‘t’, ‘j’ keys. Those shortcut keys switch the style itself, so it would require detaching the observers on the old interactor style and reattaching to the newer interactor style. One would end up making a class to handle this complication for all events.

I attached the callback to the HelloWorld vtk example, it doesn’t print “Left Release”.

#include <vtkActor.h>
#include <vtkCamera.h>
#include <vtkCallbackCommand.h>
#include <vtkCylinderSource.h>
#include <vtkNamedColors.h>
#include <vtkNew.h>
#include <vtkPolyDataMapper.h>
#include <vtkProperty.h>
#include <vtkRenderWindow.h>
#include <vtkRenderWindowInteractor.h>
#include <vtkRenderer.h>

#include <array>

void Callback(vtkObject* caller, unsigned long ev, void* clientdata, void* calldata) {
  switch (ev)
  {
  case vtkCommand::LeftButtonPressEvent:
    std::cout << "Left Press\n";
    break;
  case vtkCommand::LeftButtonReleaseEvent:
    std::cout << "Left Release\n";
    break;
  default:
    break;
  }
}

int main(int, char* [])
{
  vtkNew<vtkNamedColors> colors;

  // Set the background color.
  std::array<unsigned char, 4> bkg{ {26, 51, 102, 255} };
  colors->SetColor("BkgColor", bkg.data());

  // This creates a polygonal cylinder model with eight circumferential facets
  // (i.e, in practice an octagonal prism).
  vtkNew<vtkCylinderSource> cylinder;
  cylinder->SetResolution(8);

  // The mapper is responsible for pushing the geometry into the graphics
  // library. It may also do color mapping, if scalars or other attributes are
  // defined.
  vtkNew<vtkPolyDataMapper> cylinderMapper;
  cylinderMapper->SetInputConnection(cylinder->GetOutputPort());

  // The actor is a grouping mechanism: besides the geometry (mapper), it
  // also has a property, transformation matrix, and/or texture map.
  // Here we set its color and rotate it around the X and Y axes.
  vtkNew<vtkActor> cylinderActor;
  cylinderActor->SetMapper(cylinderMapper);
  cylinderActor->GetProperty()->SetColor(
    colors->GetColor4d("Tomato").GetData());
  cylinderActor->RotateX(30.0);
  cylinderActor->RotateY(-45.0);

  // The renderer generates the image
  // which is then displayed on the render window.
  // It can be thought of as a scene to which the actor is added
  vtkNew<vtkRenderer> renderer;
  renderer->AddActor(cylinderActor);
  renderer->SetBackground(colors->GetColor3d("BkgColor").GetData());
  // Zoom in a little by accessing the camera and invoking its "Zoom" method.
  renderer->ResetCamera();
  renderer->GetActiveCamera()->Zoom(1.5);

  // The render window is the actual GUI window
  // that appears on the computer screen
  vtkNew<vtkRenderWindow> renderWindow;
  renderWindow->SetSize(300, 300);
  renderWindow->AddRenderer(renderer);
  renderWindow->SetWindowName("Cylinder");

  // The render window interactor captures mouse events
  // and will perform appropriate camera or actor manipulation
  // depending on the nature of the events.
  vtkNew<vtkRenderWindowInteractor> renderWindowInteractor;
  renderWindowInteractor->SetRenderWindow(renderWindow);

  // This starts the event loop and as a side effect causes an initial render.
  vtkNew<vtkCallbackCommand> lbtnCb;
  lbtnCb->SetCallback(Callback);
  renderWindowInteractor->AddObserver(vtkCommand::LeftButtonPressEvent, lbtnCb);
  renderWindowInteractor->AddObserver(vtkCommand::LeftButtonReleaseEvent, lbtnCb);
  renderWindow->Render();
  renderWindowInteractor->Start();

  return EXIT_SUCCESS;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.3 FATAL_ERROR)

project(CylinderExample)

find_package(VTK COMPONENTS 
  vtkCommonColor
  vtkCommonCore
  vtkFiltersSources
  vtkInteractionStyle
  vtkRenderingContextOpenGL2
  vtkRenderingCore
  vtkRenderingFreeType
  vtkRenderingGL2PSOpenGL2
  vtkRenderingOpenGL2
  QUIET
)

if (NOT VTK_FOUND)
  message("Skipping CylinderExample: ${VTK_NOT_FOUND_MESSAGE}")
  return ()
endif()
message (STATUS "VTK_VERSION: ${VTK_VERSION}")
if (VTK_VERSION VERSION_LESS "8.90.0")
  # old system
  include(${VTK_USE_FILE})
  add_executable(CylinderExample MACOSX_BUNDLE CylinderExample.cxx )
  target_link_libraries(CylinderExample PRIVATE ${VTK_LIBRARIES})
else ()
  # include all components
  add_executable(CylinderExample MACOSX_BUNDLE CylinderExample.cxx )
  target_link_libraries(CylinderExample PRIVATE ${VTK_LIBRARIES})
  # vtk_module_autoinit is needed
  vtk_module_autoinit(
    TARGETS CylinderExample
    MODULES ${VTK_LIBRARIES}
    )
endif ()

Hello,

Have you tried this answer: http://vtk.1045678.n5.nabble.com/Mouse-button-release-event-is-still-broken-in-VTK-6-0-0-tp5724762p5724874.html further down that topic? The proposed solution makes sense to me to work around that ongoing bug.

take care,

Paulo

@Paulo_Carvalho , that is exactly what I do and I don’t like the look of it. :confused:

Then, well, I implemented my own style and I get my LMB releases intercepted: gammaray/v3dmouseinteractor.h at master · PauloCarvalhoRJ/gammaray · GitHub and gammaray/v3dmouseinteractor.cpp at master · PauloCarvalhoRJ/gammaray · GitHub .

To use it in your code:

    vtkSmartPointer<v3dMouseInteractor> myInteractor = vtkSmartPointer<v3dMouseInteractor>::New();
    myInteractor->SetDefaultRenderer(_rendererMainScene);
    _vtkwidget->GetRenderWindow()->GetInteractor()->SetInteractorStyle( myInteractor );

I hope that helps,

Paulo

Hi @Paulo_Carvalho
This is perfect in your use case where you have got the freedom to implement and use your own style class. What if there are certain restrictions here? What if the style cannot be altered in code? What if the style changes in runtime? Think of vtk-based applications, paraview, etc…
When the user changes the style at runtime, with the shortcut keys ‘a’, ‘c’, ‘j’, ‘t’ then it is required to detach the observers from the previous style and reattach the observers to the new style. Afaik neither vtkRenderWindowInteractor::SetInteractorStyle() nor vtkInteractorStyleSwitch::SetCurrentStyle() ensures any existing observers are safely transferred and its good since that would be a bad design. This is something vtkRenderWindowInteractor should take care of. If a press event was triggered, it’d be expected for a release event to be triggered too.

In code for all possible events, this translates to something of this sort.

A runtime check to see if the style has been changed.

  auto iStyle = InteractionEventsHelper::GetInteractorStyle(interactor);
  if (this->EventHelper->GetTarget() != iStyle)
  {
    this->EventHelper->Ignore();
    this->EventHelper->Observe(iStyle, this);
  }

The member methods are in the link I shared above

Hello, friend,

If switching styles in runtime is a possibility, then I suggest you to work at UI layer (e.g. Qt) by forwarding the intercepted LMB release event to VTK’s API. Other than that is to fix the bug yourself (VTK/CONTRIBUTING.md at master · Kitware/VTK · GitHub) or report it first here: Development - VTK and then here: https://gitlab.kitware.com/vtk/vtk/-/issues .

good luck,

Paulo

Other than that is to fix the bug yourself (VTK/CONTRIBUTING.md at master · Kitware/VTK · GitHub) or report it first here: Development - VTK and then here: https://gitlab.kitware.com/vtk/vtk/-/issues .

Yes, that is what is to be done here. Thanks for the suggestion.