No interaction for vtkCameraOrientationWidget in QtQuick

I am developing a 3D application based on VTK and QtQuick. I am trying to use vtkCameraOrientationWidget and followed example here (https://kitware.github.io/vtk-examples/site/Cxx/PolyData/Curvatures/). To use vtkCameraOrientationWidget, it is basically three lines of code:

  vtkNew<vtkCameraOrientationWidget> camOrientManipulator;
  camOrientManipulator->SetParentRenderer(renderer);
  // Enable the widget.
  camOrientManipulator->On();

But there is no interaction (i.e., mouse click on the handle to change camera view). I can confirm that my vtkstyle get all the mouse events passed from QEvent. Any help is appreciated!

Thanks,
Cam

Hi,

I don’t use vtkCameraOrientationWidget, but something else from the same family. I setup my VTK widget with a code like this (the last four lines of code are the ones that make the thing work):

    //----------------------adding the orientation axes-------------------------
    vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New();
    _vtkAxesWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
    _vtkAxesWidget->SetOutlineColor(0.9300, 0.5700, 0.1300);
    _vtkAxesWidget->SetOrientationMarker(axes);
    _vtkAxesWidget->SetInteractor(_vtkwidget->GetRenderWindow()->GetInteractor());
    _vtkAxesWidget->SetViewport(0.0, 0.0, 0.2, 0.2);
    _vtkAxesWidget->SetEnabled(1);
    _vtkAxesWidget->InteractiveOn();
    //--------------------------------------------------------------------------

In the code above, _vtkwidget is a QVTKOpenGLWidget in my case. IMO, it’s missing a call to SetInteractor().

I hope this helps somehow.

regards,

Paulo

Hi Paulo,

Thanks for your reply. vtkOrientationMarkerWidget use a different design than vtkCameraOrientationWidget.
For vtkCameraOrientationWidget, SetInteractor() actually happens inside SetParentRenderer()
What I need to do is just to have a widget where I can click on a set of handles to actively change the camera view in my renderer. This is exactly what vtkCameraOrientationWidget does. Now in my codebase, I can get it work except for handle interactions. For some reasons, vtkCameraOrientationWidget doesn’t receive my mouse events.
Now I am trying vtkOrientationMarkerWidget which can receive my events but I have to implement the control by myself (i.e., click on the widget, pick the corresponding cell, then do something).

Did you try this:

?

I don’t have a _vtkwidget as QVTKOpenGLWidget.
What is _vtkwidget for?
In your example, if I do
_vtkAxesWidget->SetInteractor(_myRenderWindow->GetInteractor()); rather than
_vtkAxesWidget->SetInteractor(_vtkwidget->GetRenderWindow()->GetInteractor());. Isn’t it the same thing?

If _myRenderWindow points to the window you’re rendering the scene and receiving user input events, then yes.

That’s correct. I also tested some other widgets such as vtkImplicitPlaneWidget2
using standard boilerplate code:

vtkNew<vtkImplicitPlaneRepresentation> rep;
vtkNew<vtkImplicitPlaneWidget2> planeWidget;
planeWidget->SetRepresentation(rep);
auto iren = ren->GetRenderWindow()->GetInteractor();
planeWidget->SetInteractor(iren);
planeWidget->SetCurrentRenderer(ren);
planeWidget->SetEnabled(true);
planeWidget->SetProcessEvents(true);

And it works fine. I suspect that my issue with vtkCameraOrientationWidget is from SetParentRender() design which is different than other widgets.

For other widgets, the workflow is 1) setup widget representation. 2) setup widget interactor which is the interactor in my renderwindow. 3) enable widget. So there is only one renderer and one layer in renderwindow and the widget is rendered in this renderer.

For vtkCameraOrientationWidget, it is a little bit different as it creates a second renderer which is overlaid on my main renderer. So my renderwindow has two layers: layer 0 for my main renderer and layer 1 for a new renderer which renders vtkCameraOrientationWidget. Per instruction, I just have to do

  vtkNew<vtkCameraOrientationWidget> camOrientManipulator;
  camOrientManipulator->SetParentRenderer(renderer);
  // Enable the widget.
  camOrientManipulator->On();

Everything that I described above just happened inside SetParentRenderer(renderer). But there is no interaction. This particular design doesn’t fit my QtQuick framework or at least I am missing something. I am really confused.

I also have two renderers in my program (the main one called _rendererMainScene and another for always-on-top actors such as labels called _rendererForeground). Here’s how I set everthing up (before adding actors):

    _vtkwidget = new QVTKOpenGLWidget();

    _rendererMainScene = vtkSmartPointer<vtkRenderer>::New();

    // Create the renderer for always-on-top objects
    // It attaches to the same camera of the main renderer
    _rendererForeground = vtkSmartPointer<vtkRenderer>::New();
    _rendererForeground->SetActiveCamera( _rendererMainScene->GetActiveCamera() );
    _rendererForeground->SetLayer( 1 ); //layers greater than zero have no background and are rendered last.

    _vtkwidget->SetRenderWindow(vtkGenericOpenGLRenderWindow::New());
    _vtkwidget->GetRenderWindow()->AddRenderer(_rendererMainScene);
    _vtkwidget->GetRenderWindow()->AddRenderer(_rendererForeground);
    _vtkwidget->setFocusPolicy(Qt::StrongFocus);

    //----------------------adding the orientation axes-------------------------
    vtkSmartPointer<vtkAxesActor> axes = vtkSmartPointer<vtkAxesActor>::New();
    _vtkAxesWidget = vtkSmartPointer<vtkOrientationMarkerWidget>::New();
    _vtkAxesWidget->SetOutlineColor(0.9300, 0.5700, 0.1300);
    _vtkAxesWidget->SetOrientationMarker(axes);
    _vtkAxesWidget->SetInteractor(_vtkwidget->GetRenderWindow()->GetInteractor());
    _vtkAxesWidget->SetViewport(0.0, 0.0, 0.2, 0.2);
    _vtkAxesWidget->SetEnabled(1);
    _vtkAxesWidget->InteractiveOn();
    //--------------------------------------------------------------------------

    // Customize event handling through a subclass of vtkInteractorStyleTrackballCamera.
    // This allows picking and probing by clicking on objects in the scene, for example.
    m_myInteractor = vtkSmartPointer<v3dMouseInteractor>::New();
    m_myInteractor->setParentView3DWidget( this );
    m_myInteractor->SetDefaultRenderer(_rendererMainScene);
    _vtkwidget->GetRenderWindow()->GetInteractor()->SetInteractorStyle( m_myInteractor );

    // Set callback for any event
    vtkSmartPointer<vtkCallbackCommand> callBackCommand = vtkSmartPointer<vtkCallbackCommand>::New();
    callBackCommand->SetCallback( rendererCallback );
    callBackCommand->SetClientData((void*)this);
    _rendererMainScene->AddObserver( vtkCommand::AnyEvent , callBackCommand );   // mp_ren is the vtkRenderer object.

    // adjusts view so everything fits in the screen
    _rendererMainScene->ResetCamera();

    // add the VTK widget the layout
    ui->frmViewer->layout()->addWidget(_vtkwidget);

Maybe this helps to pinpoint the problem with your code.

Anything amiss with how the objects are wired you’ll end up with the mouse events not going to where they are supposed to.

SetParentRenderer() is supposed to wire everything for you, that is, to send mouse events to the target renderer so whenever you manipulate the vtkCameraOrientationWidget, the scene updates accordingly. Maybe the problem is on how two renderers coexist in the same render window. See the code I posted above on how two renderers can coexist so the can both display things and receive user input events through the same window.

Hi Cam,

I am the author of vtkCameraOrientationWidget and it’s representation. I’ll be honest, I haven’t tested it in a QtQuick window yet. As a matter of fact I have never done any QtQuick development besides the basics in QtCreator.

Could you help me by answering these questions?

  1. When you try to rotate the camera interactively in the main renderer (the viewport with your dataset (s)), does the camera rotate like usual?
  2. When you do the above, does the widget also rotate and does it show some sort of synchronization with the main renderer’s camera?
  3. When you move mouse on top of the widget, do you see any of these visual changes in the widget?
    a. A transparent greyish disk enclosing the widget
    b. The color of the text (x or y or z) on the hovered grabber changes from black to white

I want to know if the widget is at least hot. Basically, for any interaction, it needs to get activated by hovering, which makes it hot.

It would be great if you could call Print on the widget and the interactor on any mouse click, move, release events. I would like to see the event flow.

1 Like

Hi jaswantp,

Thanks for your reply.

  1. Yes, the camera rotates like usual in my main renderer.
  2. The camera in vtkCameraOrientationWidget is synchronized with the main renderer.
  3. I don’t see any kind of change. The widget is not hot. It doesn’t receive any mouse events including the mouse hovering.

I think the problem comes from QtQuick wrapper layer (VTK/GUISupport/QtQuick at master Ā· Kitware/VTK Ā· GitHub).
One thing I would look out for is in QQuickVTKRenderWindow.cxx, on line 129 ~ 132, it creates a dummy renderer in m_renderWindow and sets the number of layers to 2, which could mess up with the renderer which renders vtkCameraOrientationWidget .
The other thing is QQuickVTKInteractiveWidget.cxx where sync function is defined.

I have no intention to change the source. I am kind of switching my main application back to QtWidget. I realized that I can inject QML element into my main QtWidget application.

and sets the number of layers to 2, which could mess up with the renderer which renders vtkCameraOrientationWidget .

Hmm, I think there is no need to worry about this because I safely preserve/increase the number of layers. See 83 - 84 and 100 - 101

I’ve seen this widget work in ParaView which already uses 2 layers (base layer +1 for the older pvAxes orientation widget) so it should not be an issue with layers afaik.

This comment notes that the dummy renderer spans the entire viewport. QQuickVTKRenderWindow.cxx:127

I assume because both the renderers QQuickVTKRenderWindow::m_dummyRenderer and vtkCameraOrientationWidget::DefaultRenderer live in the same layer and one overlaps the other, the events are not propagated to my widget. I have to investigate this…

In ParaView, it might have worked because the old pvAxes orientation widget occupied a smaller viewport rectangle and did not overlap the widget renderer’s viewport.

Hi! I have the same problem. As a result, did you manage to accurately understand the problem and is it being solved?

Sorry, this is not being worked on at the moment. But I plan to look into it in my downtime.


Hello,I ran into the exact same issue with VTK events not firing properly in QML, so I dug into VTK’s official docs and source code to understand why—and how to fix it. Here’s a clear breakdown:

Why VTK Events Act ā€œPassiveā€ in QML

VTK’s official docs for QQuickVTKItem (VTK 9.0+) confirm the core issue:

VTK’s native event loop (vtkRenderWindowInteractor::Start()) conflicts with Qt’s event loop. All VTK events (mouse/timer/keyboard) are processed passively during QtQuick’s render cycle—not actively polled by VTK itself.

This is intentional (not a bug) for three key reasons:

  1. Thread safety: VTK’s OpenGL context is bound to QtQuick’s render thread (separate from the GUI thread); active VTK event loops cause crashes.
  2. Performance: QtQuick uses ā€œOn-Demand Renderingā€ (only renders when needed); passive event processing avoids wasted CPU.
  3. Cross-platform support: Reusing Qt’s event forwarding works across Windows/macOS/Linux/Android/iOS.

Direct Proof from VTK’s Official Code/Docs

1. VTK Source Code Comment (GUISupport/QtQuick/QQuickVTKItem.h)

VTK explicitly states:

/**
 * \note VTK events (including timer events) are processed only when QtQuick triggers a render pass (e.g., `updatePaintNode`).
 * VTK does NOT run its own event loop in QML.
 */
virtual vtkUserData initializeVTK(vtkRenderWindow* renderWindow);

2. VTK’s Official Guidance

The QtQuick Integration docs highlight:

  • Limitation: Calling vtkRenderWindowInteractor::Start() creates a separate window and blocks QML.
  • Fix: Force QtQuick to render continuously to trigger VTK’s ProcessEvents().

My Solution (Validated to Fix Passive VTK Events)

To make VTK events (e.g., timers) fire reliably (no mouse input required), I added a QML Timer to force constant refreshes of QQuickVTKItem:

// Force QtQuick to render at 60 FPS (16ms interval)
Timer {
    id: repeatTimer
    interval: 16  // Milliseconds (60 FPS)
    repeat: true
    running: true

    onTriggered: {
        vtkitem.update()  // Trigger VTK render cycle → process pending events
    }
}

Qt Event Forwarding → Simulating ā€œEvent Listeningā€ in VTK’s Event Loop

QQuickVTKItem::event serves as the core entry point: Qt captures all input events (mouse/keyboard/touch, etc.) and forwards them to the VTK interactor via dispatch_async:

cpp

// Core logic of QQuickVTKItem::event
bool QQuickVTKItem::event(QEvent* ev)
{
  // 1. Clone the Qt event (e.g., mouse move, key press)
  auto e = ev->clone();
  // 2. Add the event forwarding task to the async queue
  dispatch_async([d, e](vtkRenderWindow* vtkWindow, vtkUserData) mutable {
    // 3. Critical step: Convert Qt event → VTK event (via adapter)
    d->qt2vtkInteractorAdapter.ProcessEvent(e, vtkWindow->GetInteractor());
    delete e;
  });
  ev->accept();
  return true;
}

QQuickVTKInteractorAdapter::ProcessEvent: Converts Qt events (e.g., QMouseEvent) into VTK events (e.g., vtkCommand::MouseMoveEvent) and invokes InvokeEvent on the VTK interactor.This step replaces the ā€œlisten for and process input eventsā€ functionality of VTK’s native event loop.

Supplementary Notes (Critical Context)

  1. Event Cloning: Qt events are cloned before forwarding to avoid race conditions—since dispatch_async executes the task in the QML render thread (separate from the GUI thread where the original event lives).
  2. Adapter Role: QQuickVTKInteractorAdapter is a VTK-provided utility that maps Qt’s event types/coordinates to VTK’s event system (e.g., Qt’s Qt::LeftButtonPress → VTK’s vtkCommand::LeftButtonPressEvent).
  3. Async Execution: Forwarding via dispatch_async ensures VTK event processing happens in the QML render thread (where VTK’s OpenGL context resides), preventing cross-thread crashes.