/*========================================================================= Program: Visualization Toolkit Module: QQuickVTKRenderWindow.h Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen All rights reserved. See Copyright.txt or http://www.kitware.com/Copyright.htm for details. This software is distributed WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the above copyright notice for more information. =========================================================================*/ /** * @class QQuickVTKRenderWindow * @brief [QQuickItem] subclass that manages the vtkRenderWindow and, in * turn, the OpenGL context of the QML application * * QQuickVTKRenderWindow extends [QQuickItem] in a way that allows for VTK to get a handle to, and * draw inside of the QtQuick scenegraph, using OpenGL draw calls. * * This item is exported to the QML layer via the QQmlVTKPlugin under the module VTK. It is * registered as a type \b VTKRenderWindow. Since, this class is intended to manage an OpenGL * context in the window, a single instance would be needed for most QML applications. * * Typical usage for QQuickVTKRenderWindow in a Qml application is as follows: * * @code * // import related modules * import QtQuick 2.15 * import QtQuick.Controls 2.15 * import QtQuick.Window 2.15 * * // import the VTK module * import VTK 9.0 * * // window containing the application * ApplicationWindow { * // title of the application * title: qsTr("VTK QtQuick App") * width: 400 * height: 400 * color: palette.window * * SystemPalette { * id: palette * colorGroup: SystemPalette.Active * } * * // Instantiate the vtk render window * VTKRenderWindow { * id: vtkwindow * width: 400 * height: 400 * } * * // add one or more vtk render items * VTKRenderItem { * objectName: "ConeView" * x: 200 * y: 200 * width: 200 * height: 200 * // Provide the handle to the render window * renderWindow: vtkwindow * } * VTKRenderItem { * objectName: "VolumeView" * x: 0 * y: 0 * width: 200 * height: 200 * // Provide the handle to the render window * renderWindow: vtkwindow * } * } * @endcode * * To ensure that the graphics backend set up by QtQuick matches that expected by VTK, use the * method QQuickVTKRenderWindow::setupGraphicsBackend() before a QApplication/QGuiApplication is * instantiated in the main method of the application. * * @code * int main(int argc, char* argv[]) * { * // Setup the graphics backend * QQuickVTKRenderWindow::setupGraphicsBackend(); * QGuiApplication app(argc, argv); * ... * return EXIT_SUCCESS; * } * @endcode * * The VTK pipeline can be then set up for each \b VTKRenderItem in the C++ code. * * ## QtQuick scenegraph and threaded render loop * * QtQuick/QML scenegraph rendering is done via private API inside the [QQuickWindow] class. For * details on QtQuick's render loop, see [QtQuick Scenegraph Rendering]( * https://doc.qt.io/qt-6/qtquick-visualcanvas-scenegraph.html#scene-graph-and-rendering). * Qt automatically decides between a threaded and basic render loop for most applications. * QQuickVTKRenderWindow and QQuickVTKRenderItem support both these variants of the QtQuick render * loop. * * When the scenegraph render loop is threaded, i.e. there is a dedicated rendering thread, vtk * sticks to doing all rendering on this render thread. This means that all the vtk classes, * pipelines etc. can be set up on the main thread but vtkRenderWindow::Render should only be * invoked on the render thread. Care must be taken not to call Render on the main thread because * the OpenGL context would not be valid on the main thread. * * [QQuickItem]: https://doc.qt.io/qt-5/qquickitem.html * [QQuickWindow]: https://doc.qt.io/qt-5/qquickwindow.html */ #ifndef QQuickVTKRenderWindow_h #define QQuickVTKRenderWindow_h // vtk includes #include "vtkSmartPointer.h" // For vtkSmartPointer // Qt includes #include // For QOpenGLFunctions #include // For QPointer #include #include "vtkGUISupportQtQuickModule.h" // for export macro // Forward declarations class QEvent; class QQuickVTKInteractorAdapter; class QQuickWindow; class QWheelEvent; class vtkGenericOpenGLRenderWindow; class vtkImageData; class vtkRenderWindow; class vtkRenderer; class vtkWindowToImageFilter; class VTKGUISUPPORTQTQUICK_EXPORT QQuickVTKRenderWindow : public QQuickItem , protected QOpenGLFunctions { Q_OBJECT typedef QQuickItem Superclass; public: /** * Constructor * Creates a QQuickVTKRenderWindow with: * - a vtkGenericOpenGLRenderWindow to manage the OpenGL context * - an interactor adapter to forward Qt events to vtk's interactor */ QQuickVTKRenderWindow(QQuickItem* parent = nullptr); /** * Destructor */ ~QQuickVTKRenderWindow(); /** * Set up the graphics surface format and api. * * This method sets the graphics API to OpenGLRhi and sets up the surface format for intermixed * VTK and QtQuick rendering. * Use this method before instantiating a QApplication/QGuiApplication in a QtQuick/QML app with * a VTK render view like QQuickVTKRenderItem. */ static void setupGraphicsBackend(); ///@{ /** * Set/Get the vtkRenderWindow for the view. * Note that this render window should be of type vtkGenericOpenGLRenderWindow. This is necessary * since that would allow vtk's opengl draw calls to work seamlessly inside the QtQuick created * scenegraph and OpenGL context. * * By default, a vtkGenericOpenGLRenderWindow is created and set on this item at construction * time. */ virtual void setRenderWindow(vtkRenderWindow* renWin); virtual void setRenderWindow(vtkGenericOpenGLRenderWindow* renWin); vtkRenderWindow* renderWindow() const; ///@} /** * Map a Qt item rect to viewport coordinates */ virtual void mapToViewport(const QRectF& rect, double viewport[4]); /** * Get access to the interactor adapter */ QPointer interactorAdapter() const; ///@{ /** * Capture a screenshot of the window * * \param viewport area to capture. * \returns Image data containing the window capture. * \note This triggers a scenegraph update to capture the render window view. */ virtual vtkSmartPointer captureScreenshot(); virtual vtkSmartPointer captureScreenshot(double* viewport); ///@} /** * Get whether the render window is initialized * Used internally to determine if the OpenGL context, QQuickWindow, children items and viewports * have been initialized. */ virtual bool isInitialized() const; public Q_SLOTS: /** * This is the function called on the QtQuick render thread before the scenegraph state * is synchronized. This is where most of the pipeline updates, camera manipulations, etc. and * other pre-render steps can be performed. * * \note At the time of this method execution, the GUI thread is blocked. Hence, it is safe to * perform state synchronization between the GUI elements and the VTK classes here. */ virtual void sync(); /** * Initialize the VTK render window for OpenGL based on the context created by QtQuick * * \note This method is called at the beforeRenderPassRecording stage of the QtQuick scenegraph. * All the QtQuick element rendering is stacked visually above the vtk rendering. */ virtual void init(); /** * This is the function called on the QtQuick render thread right before the scenegraph is * rendered. This is the stage where all the vtk rendering is performed. Applications would rarely * need to override this method. * * \note This method is called at the beforeRenderPassRecording stage of the QtQuick scenegraph. * All the QtQuick element rendering is stacked visually above the vtk rendering. */ virtual void paint(); /** * This is the function called on the QtQuick render thread when the scenegraph is invalidated. * This is where all graphics resources allocated by vtk are released. */ virtual void cleanup(); /** * Convenience method that schedules a scenegraph update and waits for the update. * \sa render() */ virtual void renderNow(); /** * Schedule a scenegraph update * * \note Since this schedules a scenegraph update, it does not guarantee that the scene will be * updated after this call. * \sa renderNow() */ virtual void render(); protected Q_SLOTS: virtual void handleWindowChanged(QQuickWindow* w); protected: QPointer m_interactorAdapter; vtkSmartPointer m_renderWindow; bool m_initialized = false; // Screenshot stuff bool m_screenshotScheduled = false; vtkNew m_screenshotFilter; vtkNew m_dummyRenderer; // Event handlers #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) override; #else void geometryChange(const QRectF& newGeometry, const QRectF& oldGeometry) override; #endif /** * Check the scenegraph backend and graphics API being used. */ bool checkGraphicsBackend(); private: QQuickVTKRenderWindow(const QQuickVTKRenderWindow&) = delete; void operator=(const QQuickVTKRenderWindow) = delete; }; #endif // QQuickVTKRenderWindow_h