diff --git a/.gitlab/artifacts.yml b/.gitlab/artifacts.yml index 40e5ca73f8aad06f49f0a25d312bf62b3604fcff..4a70ec7c7e17b717d8bf1049c52259b31d14958c 100644 --- a/.gitlab/artifacts.yml +++ b/.gitlab/artifacts.yml @@ -67,6 +67,10 @@ # Virtual environment - build/ci-venv/ + # QML artifacts + - build/*/*/qmldir + - build/*/*/VTK.qmltypes + .cmake_test_artifacts: artifacts: expire_in: 1d diff --git a/.gitlab/ci/ctest_exclusions.cmake b/.gitlab/ci/ctest_exclusions.cmake index 69e9bf266aab933d7aad111e84ec4b1d233638a6..a20b6f78fd697944dff8bbbad1d95f78253c2ab1 100644 --- a/.gitlab/ci/ctest_exclusions.cmake +++ b/.gitlab/ci/ctest_exclusions.cmake @@ -18,6 +18,9 @@ if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "fedora") # Numerical problems? "^VTK::FiltersOpenTURNSCxx-TestOTKernelSmoothing$" + # QtQuick event loop issue with CI VNC + "^VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderItemWidget$" + # These tests all seem to have some problem with the rendering order of # some components of the scenes that are being tested. Needs investigation. # https://gitlab.kitware.com/vtk/vtk/-/issues/18098 @@ -146,7 +149,10 @@ if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "windows") "^VTK::GUISupportQtCxx-TestQVTKOpenGLWidgetPicking$" "^VTK::GUISupportQtCxx-TestQVTKOpenGLWidgetQWidgetWidget$" "^VTK::GUISupportQtCxx-TestQVTKOpenGLWidgetWithDisabledInteractor$" - "^VTK::GUISupportQtCxx-TestQVTKOpenGLWidgetWithMSAA$") + "^VTK::GUISupportQtCxx-TestQVTKOpenGLWidgetWithMSAA$" + "^VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderItem$" + "^VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderItemWidget$" + "^VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderWindow$") endif () if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "osmesa") @@ -158,6 +164,14 @@ if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "osmesa") "^VTK::RenderingOpenGL2Cxx-TestGlyph3DMapperPickability$") endif () +if ("$ENV{CMAKE_CONFIGURATION}" MATCHES "macos") + list(APPEND test_exclusions + # QtQuick event loop / OpenGL context issues. Needs investigation + "^VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderItem$" + "^VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderItemWidget$" + "^VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderWindow$") +endif () + string(REPLACE ";" "|" test_exclusions "${test_exclusions}") if (test_exclusions) set(test_exclusions "(${test_exclusions})") diff --git a/.gitlab/ci/docker/fedora33/install_deps.sh b/.gitlab/ci/docker/fedora33/install_deps.sh index 3ad09ed4df8f1d1af5a829c68902ffae0b2fc9e0..9339c24fcde58fd7a7a0948cbf036ca9838d223e 100755 --- a/.gitlab/ci/docker/fedora33/install_deps.sh +++ b/.gitlab/ci/docker/fedora33/install_deps.sh @@ -19,7 +19,7 @@ dnf install -y --setopt=install_weak_deps=False \ # Qt dependencies dnf install -y --setopt=install_weak_deps=False \ - qt5-qtbase-devel qt5-qttools-devel + qt5-qtbase-devel qt5-qttools-devel qt5-qtquickcontrols2-devel # Mesa dependencies dnf install -y --setopt=install_weak_deps=False \ diff --git a/.gitlab/ci/download_qt.cmake b/.gitlab/ci/download_qt.cmake index 0ea2e667560762680f01c2197506b453c73dd491..b6a0492a7fd8c5d5cee4a5208bb85a4c6a773ced 100644 --- a/.gitlab/ci/download_qt.cmake +++ b/.gitlab/ci/download_qt.cmake @@ -47,7 +47,7 @@ if (qt_platform STREQUAL "windows_x86") "${qt_file_name_prefix}d3dcompiler_47-x64.7z" "${qt_file_name_prefix}opengl32sw-64-mesa_12_0_rc2.7z") - foreach (qt_component IN ITEMS qtbase qttools) + foreach (qt_component IN ITEMS qtbase qttools qtdeclarative qtquickcontrols2) list(APPEND qt_files "${qt_file_name_prefix}${qt_component}-Windows-Windows_10-MSVC${msvc_year}-Windows-Windows_10-X86_64.7z") endforeach () @@ -57,7 +57,7 @@ elseif (qt_platform STREQUAL "mac_x64") set(qt_build_stamp "202009071110") set(qt_file_name_prefix "${qt_version}-0-${qt_build_stamp}") - foreach (qt_component IN ITEMS qtbase qttools) + foreach (qt_component IN ITEMS qtbase qttools qtdeclarative qtquickcontrols2) list(APPEND qt_files "${qt_file_name_prefix}${qt_component}-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z") endforeach () diff --git a/.gitlab/ci/download_qt_hashes.cmake b/.gitlab/ci/download_qt_hashes.cmake index 5b4b0da83fede9e0b7e22322fc9b32e9bc669977..93596d1abaaaf7a9ceaa3c61bd60f66ad6401bd5 100644 --- a/.gitlab/ci/download_qt_hashes.cmake +++ b/.gitlab/ci/download_qt_hashes.cmake @@ -4,6 +4,8 @@ set("5.15.1-0-202009071110qtbase-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64.7z_hash" a5635124a135f383d9fb92bf628b018cff9f781addfd388926a367cda5b7cd38) set("5.15.1-0-202009071110qttools-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64.7z_hash" 45b3debfc317f875d327e4506c0d211dc82ec92c1e9aa60e17b1a747ada22811) +set("5.15.1-0-202009071110qtdeclarative-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64.7z_hash" c40623f08fb54f0f46758b863d79d88449c04044415eb133b61df02055edd028) +set("5.15.1-0-202009071110qtquickcontrols2-Windows-Windows_10-MSVC2019-Windows-Windows_10-X86_64.7z_hash" e09fa3e44bc3edb102963a197f2d1e8444d6ad0b1cae6e2da27fb423c9c62d76) # There's a filename conflict here. if (msvc_year STREQUAL "2019") @@ -15,6 +17,10 @@ elseif (msvc_year STREQUAL "2015") endif () set("5.15.1-0-202009071110qtbase-Windows-Windows_10-MSVC2015-Windows-Windows_10-X86_64.7z_hash" 5d0d2e71e3b00cf88ac4c616b4b314a7e73871f325512821f53c464cdfee961f) set("5.15.1-0-202009071110qttools-Windows-Windows_10-MSVC2015-Windows-Windows_10-X86_64.7z_hash" 393630ea20cc5fa29e7987df21526586bcac23c3499fb9889d980758942f942e) +set("5.15.1-0-202009071110qtdeclarative-Windows-Windows_10-MSVC2015-Windows-Windows_10-X86_64.7z_hash" a40ece71b5685de946084b04642cbd9b80cf51e82b17c3cbdfb926f8ae6063e9) +set("5.15.1-0-202009071110qtquickcontrols2-Windows-Windows_10-MSVC2015-Windows-Windows_10-X86_64.7z_hash" ef1ea627321bb0a36684dc02303aaf56f667672262cd3c74097397177f53981e) set("5.15.1-0-202009071110qtbase-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z_hash" df2813ce7c6cb4287abd7956cd1cb9d08312e4ac1208b6cb57af4df11b8ebba1) set("5.15.1-0-202009071110qttools-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z_hash" 94c5e98afa548bf56c7ccc29bccf727b75e2e90df98e83d22575d07f64359cda) +set("5.15.1-0-202009071110qtdeclarative-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z_hash" 9a23e1a2771a2a202b73c640e2eab032c480c2037e22842a113fca9e0f234c46) +set("5.15.1-0-202009071110qtquickcontrols2-MacOS-MacOS_10_13-Clang-MacOS-MacOS_10_13-X86_64.7z_hash" fc305bbdf6e6301dbb0b9b8c7466b5a57ce8ee2ffb0a8a438a273639b62236c5) diff --git a/.gitlab/os-linux.yml b/.gitlab/os-linux.yml index 31b5b80b3ed21c808d95b73619ce14a728321fd8..300e3cd3223d9b91eea0c358e391fc8abc73b627 100644 --- a/.gitlab/os-linux.yml +++ b/.gitlab/os-linux.yml @@ -9,7 +9,7 @@ .fedora33: extends: .linux - image: "kitware/vtk:ci-fedora33-20210419" + image: "kitware/vtk:ci-fedora33-20210426" .fedora_mpich_addon: variables: diff --git a/Documentation/release/dev/qtquick-qml-support.md b/Documentation/release/dev/qtquick-qml-support.md new file mode 100644 index 0000000000000000000000000000000000000000..3fdcfe495b8dc538919eccec05cadcbf03fc1698 --- /dev/null +++ b/Documentation/release/dev/qtquick-qml-support.md @@ -0,0 +1,6 @@ +# QtQuick / QML support + +VTK can now be used in a QtQuick / QML application. + +A new module `vtkGUISupportQtQuick` provides the necessary integration classes along with the QML +module infrastructure required to import VTK into a QtQuick application. diff --git a/GUISupport/QtQuick/CMakeLists.txt b/GUISupport/QtQuick/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..ec5e5f2bac4dea360185a640110671d551738f85 --- /dev/null +++ b/GUISupport/QtQuick/CMakeLists.txt @@ -0,0 +1,183 @@ +set(classes + QQmlVTKPlugin + QQuickVTKInteractiveWidget + QQuickVTKInteractorAdapter + QQuickVTKRenderItem + QQuickVTKRenderWindow + ) + +include(vtkQt) +set(qt_components + Gui + OpenGL + Quick + Qml + ) + +vtk_module_find_package( + PACKAGE "Qt${vtk_qt_major_version}" + VERSION 5.9 + COMPONENTS ${qt_components} + FORWARD_VERSION_REQ MINOR + VERSION_VAR "Qt${vtk_qt_major_version}_VERSION") + +set(CMAKE_AUTOMOC 1) + +vtk_module_add_module(VTK::GUISupportQtQuick + CLASSES ${classes} + ) + +foreach(_qt_comp IN LISTS qt_components) + list(APPEND qt_modules "Qt${vtk_qt_major_version}::${_qt_comp}") +endforeach() + +vtk_module_definitions(VTK::GUISupportQtQuick PRIVATE QT_NO_KEYWORDS) +vtk_module_link(VTK::GUISupportQtQuick PUBLIC ${qt_modules}) + +_vtk_module_real_target(vtk_guisupportqtquick_target VTK::GUISupportQtQuick) + +# Generate the qmldir file for module plugin consumption in a Qml project + +# QML plugin qmldir and qmltypes for build tree +# Set VTK_MODULE_PLUGIN_NAME and VTK_MODULE_PLUGIN_DIR for the qmldir configuration +set(vtk_qml_module_dir + ${_vtk_build_PACKAGE}.${VTK_MAJOR_VERSION}.${VTK_MINOR_VERSION}) +set(vtk_qml_module_superpath + ${CMAKE_BINARY_DIR}/$<CONFIG>) +set(vtk_qml_module_path + ${vtk_qml_module_superpath}/${vtk_qml_module_dir}) + +# We use a three step approach to resolving the QML plugin path for qmldir file +# during compilation of this module. +# The first step would be to configure the qmldir.in file to a pre-generator +# file. This step would be followed by a generator step that fills in the right +# directory location for VTK and generates a qmldir file for each +# possible build configuration i.e. Debug, Release, etc. +# The third step is to copy the right configuration file to the plugin +# location at build time. This is done as a custom command. +set(VTK_MODULE_PLUGIN_NAME "$<TARGET_FILE_BASE_NAME:${vtk_guisupportqtquick_target}>") +set(VTK_MODULE_PLUGIN_DIR "$<TARGET_FILE_DIR:${vtk_guisupportqtquick_target}>") +set(vtk_qml_module_tmp_build_path + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/qmldirBuild + ) +set(qmldir_pregen_file + "${vtk_qml_module_tmp_build_path}/qmldir.pregen") +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/qmldir.in + ${qmldir_pregen_file} + @ONLY + ) +set(qmldir_gen_file + "${vtk_qml_module_tmp_build_path}/qmldir$<CONFIG>.gen") +file(GENERATE OUTPUT ${qmldir_gen_file} + INPUT ${qmldir_pregen_file}) +add_custom_command(TARGET ${vtk_guisupportqtquick_target} + POST_BUILD + COMMAND ${CMAKE_COMMAND} -E copy_if_different + ${qmldir_gen_file} ${vtk_qml_module_path}/qmldir + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + # Commented out because generator expreesions not supported in CMake < 3.20 for byproducts + # BYPRODUCTS ${vtk_qml_module_path}/qmldir + COMMENT "Generating qmldir file" + VERBATIM + ) + +# Generate the qmltypes file for the VTK Qml plugin +# First, find the qmlplugindump executable +get_target_property(qt_core_location Qt${vtk_qt_major_version}::Core LOCATION) +get_filename_component(qt_bin_dir "${qt_core_location}" PATH) +if(APPLE) + get_filename_component(qt_bin_dir "${qt_bin_dir}" PATH) +endif() +find_program(QMLPLUGINDUMP_EXECUTABLE qmlplugindump + HINTS "${qt_bin_dir}" + DOC "QmlPlugindump executable location" + ) +mark_as_advanced(QMLPLUGINDUMP_EXECUTABLE) +if(NOT QMLPLUGINDUMP_EXECUTABLE) + message(WARNING "qmlplugindump executable not found.\n" + "It is required to generate the qmltypes file" + " for VTK Qml plugin.") +endif() + +set(qmltypes_file + "${vtk_qml_module_path}/${_vtk_build_PACKAGE}.qmltypes" + ) +set(qmltypes_create_copy_command FALSE) +if(QMLPLUGINDUMP_EXECUTABLE) + get_target_property(qt_config Qt${vtk_qt_major_version}::Core IMPORTED_CONFIGURATIONS) + if(("DEBUG" IN_LIST qt_config) AND ("RELEASE" IN_LIST qt_config)) + MESSAGE (WARNING "Qt5 is configured in both Debug and Release modes." + " Due to Qt issue 47774 (https://bugreports.qt.io/browse/QTBUG-47774)," + " skipping generation of qmltypes file." + " Using the one provided with the source tree instead.") + set(qmltypes_create_copy_command TRUE) + else() + add_custom_command(TARGET ${vtk_guisupportqtquick_target} + POST_BUILD + COMMAND + ${QMLPLUGINDUMP_EXECUTABLE} ${_vtk_build_PACKAGE} + ${VTK_MAJOR_VERSION}.${VTK_MINOR_VERSION} + ${vtk_qml_module_superpath} > ${qmltypes_file} + WORKING_DIRECTORY ${CMAKE_BINARY_DIR} + # BYPRODUCTS ${qmltypes_file} + COMMENT "Generating qmltypes file using qmlplugindump" + VERBATIM + ) + endif() +else() + set(qmltypes_create_copy_command TRUE) +endif() +if(qmltypes_create_copy_command) + add_custom_command(TARGET ${vtk_guisupportqtquick_target} + POST_BUILD + COMMAND + ${CMAKE_COMMAND} -E copy ${CMAKE_CURRENT_SOURCE_DIR}/${_vtk_build_PACKAGE}.qmltypes + ${vtk_qml_module_path} + COMMENT "Copying qmltypes file from the source tree to ${vtk_qml_module_path}" + ) +endif() + +# QML plugin qmldir and qmltypes for install tree +set(vtk_qml_module_tmp_install_path + ${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/qmldirInstall + ) +set(vtk_qml_module_install_path + ${vtk_qml_module_tmp_install_path}/$<CONFIG>/${vtk_qml_module_dir}) +if(WIN32 AND NOT CYGWIN) + # Set VTK_MODULE_PLUGIN_DIR for the qmldir configuration + # Use the runtime (dll) path for plugin on Windows + file(RELATIVE_PATH VTK_MODULE_PLUGIN_DIR + ${CMAKE_INSTALL_PREFIX}/${_vtk_build_PACKAGE} + ${CMAKE_INSTALL_PREFIX}/${_vtk_build_RUNTIME_DESTINATION} + ) +else() + # Set VTK_MODULE_PLUGIN_DIR for the qmldir configuration + file(RELATIVE_PATH VTK_MODULE_PLUGIN_DIR + ${CMAKE_INSTALL_PREFIX}/${_vtk_build_PACKAGE} + ${CMAKE_INSTALL_PREFIX}/${_vtk_build_LIBRARY_DESTINATION} + ) +endif() +set(qmldir_install_pregen_file + "${vtk_qml_module_tmp_install_path}/qmldir.install.pregen") +configure_file( + "${CMAKE_CURRENT_SOURCE_DIR}/qmldir.in" + "${qmldir_install_pregen_file}" + @ONLY + ) +set(qmldir_install_gen_file + "${vtk_qml_module_install_path}/qmldir") +file(GENERATE OUTPUT ${qmldir_install_gen_file} + INPUT ${qmldir_install_pregen_file}) + +install(FILES ${qmltypes_file} + DESTINATION ${vtk_qml_module_install_path} + COMPONENT ${_vtk_build_TARGETS_COMPONENT} + ) + +# Install the QML plugin module +install(DIRECTORY + ${vtk_qml_module_install_path} + DESTINATION "${CMAKE_INSTALL_PREFIX}" + COMPONENT ${_vtk_build_TARGETS_COMPONENT} + ) diff --git a/GUISupport/QtQuick/QQmlVTKPlugin.cxx b/GUISupport/QtQuick/QQmlVTKPlugin.cxx new file mode 100644 index 0000000000000000000000000000000000000000..ad1576552369fce1a897ba939c5172c3731d03bb --- /dev/null +++ b/GUISupport/QtQuick/QQmlVTKPlugin.cxx @@ -0,0 +1,50 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQmlVTKPlugin.cxx + + 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. + +=========================================================================*/ +// vtk includes +#include "QQmlVTKPlugin.h" + +#include "QQuickVTKInteractiveWidget.h" +#include "QQuickVTKRenderItem.h" +#include "QQuickVTKRenderWindow.h" +#include "vtkVersion.h" + +// Qt includes +#include <QQmlEngine> + +//------------------------------------------------------------------------------------------------- +void QQmlVTKPlugin::registerTypes(const char* uri) +{ + Q_ASSERT(QString::compare(uri, "VTK") == 0); + + int major = vtkVersion::GetVTKMajorVersion(); + int minor = vtkVersion::GetVTKMinorVersion(); + + // Register QML metatypes + qmlRegisterType<QQuickVTKRenderWindow>(uri, major, minor, "VTKRenderWindow"); + qmlRegisterType<QQuickVTKRenderItem>(uri, major, minor, "VTKRenderItem"); + qmlRegisterType<QQuickVTKInteractiveWidget>(uri, major, minor, "VTKWidget"); +} + +//------------------------------------------------------------------------------------------------- +void QQmlVTKPlugin::initializeEngine(QQmlEngine* engine, const char* uri) +{ + Q_ASSERT(QString::compare(uri, "VTK") == 0); + + QObject::connect( + engine, &QQmlEngine::destroyed, this, &QQmlVTKPlugin::cleanup, Qt::DirectConnection); +} + +//------------------------------------------------------------------------------------------------- +void QQmlVTKPlugin::cleanup() {} diff --git a/GUISupport/QtQuick/QQmlVTKPlugin.h b/GUISupport/QtQuick/QQmlVTKPlugin.h new file mode 100644 index 0000000000000000000000000000000000000000..fa3e8cd5d673989101430fe7ca95eb2da295c91a --- /dev/null +++ b/GUISupport/QtQuick/QQmlVTKPlugin.h @@ -0,0 +1,107 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQmlVTKPlugin.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. + +=========================================================================*/ +#ifndef QQmlVTKPlugin_h +#define QQmlVTKPlugin_h + +// vtk includes +#include "vtkGUISupportQtQuickModule.h" // for export macro + +// Qt includes +#include <QQmlExtensionPlugin> + +// Forward declarations + +/** + * \class QQmlVTKPlugin + * \brief Plugin class to expose a VTK C++ module to QML applications + * + * QQmlVTKPlugin registers various VTK C++ classes as QML types so that QtQuick applications can + * directly import and use these types from QML. + * + * ## Importing the VTK module in QML + * As part of VTK's compilation process, it would compile and install a \em \b qmldir file that + * provides the module definition and relevant plugin information required by QML to load VTK. To + * load the plugin, set the environment variable + * [QML2_IMPORT_PATH](https://doc.qt.io/qt-5/qtqml-syntax-imports.html#qml-import-path) to the path + * of the directory containing the \em qmldir file. + * + * \code + * # /projects/Import has a sub-directory VTK.9.0/qmldir + * $ export QML2_IMPORT_PATH=/projects/Import + * \endcode + * + * Once the import path is set correctly, the module can be imported in the \em .qml file as + * follows: + * + * \code + * import VTK 9.0 + * \endcode + * + * ## Registered types + * The C++ classes exposed to QML and their associated typenames are as follows: + * + * | VTK C++ class | QML type | + * | :--------------: | :--------------: | + * | QQuickVTKRenderWindow | VTKRenderWindow | + * | QQuickVTKRenderItem | VTKRenderItem | + * | QQuickVTKInteractiveWidget | VTKWidget | + * + * ## Versioning + * The VTK QML module follows the version number of the VTK source tree. For example, if compiled + * against VTK 9.0.x, the VTK module version will be 9.0 + */ +class VTKGUISUPPORTQTQUICK_EXPORT QQmlVTKPlugin : public QQmlExtensionPlugin +{ + Q_OBJECT + typedef QQmlExtensionPlugin Superclass; + + Q_PLUGIN_METADATA(IID "org.kitware.VTK") + +public: + /** + * Constructor + */ + QQmlVTKPlugin() = default; + + /** + * Destructor + */ + virtual ~QQmlVTKPlugin() = default; + + /** + * Register QML types provided by VTK + */ + void registerTypes(const char* uri); + + /** + * Initialize the extension using the QQmlEngine + * + * \sa cleanup + */ + void initializeEngine(QQmlEngine* engine, const char* uri); + +protected Q_SLOTS: + /** + * Destroy any singleton instances that were created during initializeEngine + * + * \sa initializeEngine + */ + void cleanup(); + +private: + Q_DISABLE_COPY(QQmlVTKPlugin); +}; + +#endif // QQmlVTKPlugin_h diff --git a/GUISupport/QtQuick/QQuickVTKInteractiveWidget.cxx b/GUISupport/QtQuick/QQuickVTKInteractiveWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1e436d431396496c19296695f921443ae54c8c85 --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKInteractiveWidget.cxx @@ -0,0 +1,73 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQuickVTKInteractiveWidget.cxx + + 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. + +=========================================================================*/ +#include "QQuickVTKInteractiveWidget.h" + +// vtk includes +#include "vtkAbstractWidget.h" +#include "vtkRenderWindow.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" +#include "vtkWidgetRepresentation.h" + +//------------------------------------------------------------------------------------------------- +QQuickVTKInteractiveWidget::QQuickVTKInteractiveWidget(QObject* parent) + : Superclass(parent) +{ +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractiveWidget::setWidget(vtkAbstractWidget* w) +{ + this->m_widget = w; +} + +//------------------------------------------------------------------------------------------------- +vtkAbstractWidget* QQuickVTKInteractiveWidget::widget() const +{ + return this->m_widget; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractiveWidget::setEnabled(bool e) +{ + if (this->m_enabled == e) + { + return; + } + + this->m_enabled = e; + Q_EMIT this->enabledChanged(this->m_enabled); +} + +//------------------------------------------------------------------------------------------------- +bool QQuickVTKInteractiveWidget::enabled() const +{ + return this->m_enabled; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractiveWidget::sync(vtkRenderer* ren) +{ + if (!ren || !this->m_widget) + { + return; + } + + auto iren = ren->GetRenderWindow()->GetInteractor(); + this->m_widget->SetInteractor(iren); + this->m_widget->SetCurrentRenderer(ren); + this->m_widget->SetEnabled(this->m_enabled); + this->m_widget->SetProcessEvents(this->m_enabled); +} diff --git a/GUISupport/QtQuick/QQuickVTKInteractiveWidget.h b/GUISupport/QtQuick/QQuickVTKInteractiveWidget.h new file mode 100644 index 0000000000000000000000000000000000000000..ba017a04aed8ae3ae5a2e7475307363ac6aaef41 --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKInteractiveWidget.h @@ -0,0 +1,83 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQuickVTKInteractiveWidget.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 QQuickVTKInteractiveWidget + * @brief QObject that manages a VTK interactive widget to ensure that it behaves as per the QtQuick + * threaded render loop. + * + * QQuickVTKInteractiveWidget holds a weak reference to the vtk widget it manages. + */ + +#ifndef QQuickVTKInteractiveWidget_h +#define QQuickVTKInteractiveWidget_h + +// Qt includes +#include <QObject> + +// vtk includes +#include "vtkWeakPointer.h" // For vtkWeakPointer + +#include "vtkGUISupportQtQuickModule.h" // for export macro + +// Forward declarations +class vtkAbstractWidget; +class vtkRenderer; + +class VTKGUISUPPORTQTQUICK_EXPORT QQuickVTKInteractiveWidget : public QObject +{ + Q_OBJECT + typedef QObject Superclass; + + Q_PROPERTY(bool enabled READ enabled WRITE setEnabled NOTIFY enabledChanged); + +public: + QQuickVTKInteractiveWidget(QObject* parent = nullptr); + ~QQuickVTKInteractiveWidget() = default; + + ///@{ + /** + * Set/Get the widget reference + */ + void setWidget(vtkAbstractWidget* w); + vtkAbstractWidget* widget() const; + ///@} + + ///@{ + /** + * Set/Get whether the widget is enabled. + */ + void setEnabled(bool e); + bool enabled() const; + ///@} + +public Q_SLOTS: + virtual void sync(vtkRenderer* ren); + +Q_SIGNALS: + void enabledChanged(bool e); + +protected: + // Helper members + vtkWeakPointer<vtkAbstractWidget> m_widget; + + // Enabled/disabled + bool m_enabled = false; + +private: + QQuickVTKInteractiveWidget(const QQuickVTKInteractiveWidget&) = delete; + void operator=(const QQuickVTKInteractiveWidget) = delete; +}; + +#endif // QQuickVTKInteractiveWidget_h diff --git a/GUISupport/QtQuick/QQuickVTKInteractorAdapter.cxx b/GUISupport/QtQuick/QQuickVTKInteractorAdapter.cxx new file mode 100644 index 0000000000000000000000000000000000000000..b0cc346d3420118d87003539c7126fe8f4fa6a6e --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKInteractorAdapter.cxx @@ -0,0 +1,128 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQuickVTKInteractorAdapter.cxx + + 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. + +=========================================================================*/ +#include "QQuickVTKInteractorAdapter.h" + +// Qt includes +#include <QEvent> +#include <QQuickItem> +#include <QQuickWindow> + +// VTK includes +#include "vtkRenderWindowInteractor.h" + +//------------------------------------------------------------------------------------------------- +QQuickVTKInteractorAdapter::QQuickVTKInteractorAdapter(QObject* parent) + : Superclass(parent) +{ +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::setQQuickWindow(QQuickWindow* win) +{ + m_qwindow = win; +} + +//------------------------------------------------------------------------------------------------- +QPointF QQuickVTKInteractorAdapter::mapEventPosition(QQuickItem* item, const QPointF& localPos) +{ + // Account for the difference in coordinate reference systems. + // Qt uses quadrant IV and VTK uses quadrant I. So the point should be + // translated to the right position along Y axis. + return item->mapToScene(localPos); +} + +//------------------------------------------------------------------------------------------------- +QPointF QQuickVTKInteractorAdapter::mapEventPositionFlipY(QQuickItem* item, const QPointF& localPos) +{ + QPointF mappedPos = QQuickVTKInteractorAdapter::mapEventPosition(item, localPos); + mappedPos.setY(item->window()->height() - mappedPos.y() + 1); + return mappedPos; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::QueueHoverEvent(QQuickItem* item, QHoverEvent* e) +{ + QHoverEvent* newEvent = new QHoverEvent(e->type(), this->mapEventPosition(item, e->posF()), + this->mapEventPosition(item, e->oldPosF()), e->modifiers()); + QueueEvent(newEvent); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::QueueKeyEvent(QQuickItem* item, QKeyEvent* e) +{ + Q_UNUSED(item); + QKeyEvent* newEvent = new QKeyEvent(e->type(), e->key(), e->modifiers(), e->nativeScanCode(), + e->nativeVirtualKey(), e->nativeModifiers(), e->text(), e->isAutoRepeat(), e->count()); + QueueEvent(newEvent); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::QueueFocusEvent(QQuickItem* item, QFocusEvent* e) +{ + Q_UNUSED(item); + QFocusEvent* newEvent = new QFocusEvent(e->type(), e->reason()); + QueueEvent(newEvent); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::QueueMouseEvent(QQuickItem* item, QMouseEvent* e) +{ + QMouseEvent* newEvent = new QMouseEvent(e->type(), this->mapEventPosition(item, e->localPos()), + this->mapEventPosition(item, e->windowPos()), this->mapEventPosition(item, e->screenPos()), + e->button(), e->buttons(), e->modifiers()); + QueueEvent(newEvent); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::QueueGeometryChanged( + const QRectF& newGeometry, const QRectF& oldGeometry) +{ + QResizeEvent* newEvent = + new QResizeEvent(newGeometry.size().toSize(), oldGeometry.size().toSize()); + QueueEvent(newEvent); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::QueueWheelEvent(QQuickItem* item, QWheelEvent* e) +{ + QWheelEvent* newEvent = new QWheelEvent(this->mapEventPosition(item, e->position()), + this->mapEventPosition(item, e->globalPosition()), e->pixelDelta(), e->angleDelta(), + e->buttons(), e->modifiers(), e->phase(), e->inverted(), e->source()); + QueueEvent(newEvent); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::QueueEvent(QEvent* e) +{ + m_queuedEvents << e; + if (m_qwindow) + { + m_qwindow->update(); + } +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKInteractorAdapter::ProcessEvents(vtkRenderWindowInteractor* interactor) +{ + if (interactor) + { + for (QEvent* e : this->m_queuedEvents) + { + ProcessEvent(e, interactor); + } + qDeleteAll(m_queuedEvents); + m_queuedEvents.clear(); + } +} diff --git a/GUISupport/QtQuick/QQuickVTKInteractorAdapter.h b/GUISupport/QtQuick/QQuickVTKInteractorAdapter.h new file mode 100644 index 0000000000000000000000000000000000000000..a21126a677eb6311a8a28b61187c19ce3eace129 --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKInteractorAdapter.h @@ -0,0 +1,92 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQuickVTKInteractorAdapter.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. + +=========================================================================*/ +#ifndef QQuickVTKInteractorAdapter_h +#define QQuickVTKInteractorAdapter_h + +// VTK includes +#include "QVTKInteractorAdapter.h" +#include "vtkGUISupportQtQuickModule.h" // for export macro + +// Qt includes +#include <QList> // for QList +#include <QPointer> // for QPointer + +// Forward declarations +class QEnterEvent; +class QEvent; +class QFocusEvent; +class QHoverEvent; +class QKeyEvent; +class QMouseEvent; +class QQuickItem; +class QQuickWindow; +class QWheelEvent; +class vtkRenderWindowInteractor; +class vtkRenderer; + +/** + * @class QQuickVTKInteractorAdapter + * @brief Intermediate class that handles relaying Qt events to VTK + */ +class VTKGUISUPPORTQTQUICK_EXPORT QQuickVTKInteractorAdapter : public QVTKInteractorAdapter +{ + Q_OBJECT + typedef QVTKInteractorAdapter Superclass; + +public: + QQuickVTKInteractorAdapter(QObject* parent = nullptr); + + void setQQuickWindow(QQuickWindow* win); + + void QueueHoverEvent(QQuickItem* item, QHoverEvent* e); + void QueueKeyEvent(QQuickItem* item, QKeyEvent* e); + void QueueFocusEvent(QQuickItem* item, QFocusEvent* e); + void QueueMouseEvent(QQuickItem* item, QMouseEvent* e); + void QueueGeometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry); + void QueueWheelEvent(QQuickItem* item, QWheelEvent* e); + + void ProcessEvents(vtkRenderWindowInteractor* interactor); + + /* + * Map the event position to VTK display coordinates + * The mapping considers the following: + * - VTK widgets expect display coordinates, not viewport/local coordinates + * - vtkRenderWindowInteractor flips Y before processing the event. + * Because of the inherent flip in the superclass, the mapping does not flip Y implicitly. + * To map and flip Y, use mapEventPositionFlipY. + * + * \sa mapEventPositionFlipY + */ + static QPointF mapEventPosition(QQuickItem* item, const QPointF& localPos); + + /* + * Map the event position to VTK display coordinates and flip the Y axis to switch the point from + * the Qt coordinate reference system to VTK's. + * + * \sa mapEventPosition + */ + static QPointF mapEventPositionFlipY(QQuickItem* item, const QPointF& localPos); + +protected: + void QueueEvent(QEvent* e); + +private: + QPointer<QQuickWindow> m_qwindow; + QList<QEvent*> m_queuedEvents; + + Q_DISABLE_COPY(QQuickVTKInteractorAdapter) +}; + +#endif // QQuickVTKInteractorAdapter_h diff --git a/GUISupport/QtQuick/QQuickVTKRenderItem.cxx b/GUISupport/QtQuick/QQuickVTKRenderItem.cxx new file mode 100644 index 0000000000000000000000000000000000000000..1ac3d32ed5da4ce413e2857d45f43ed324130675 --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKRenderItem.cxx @@ -0,0 +1,290 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQuickVTKRenderItem.cxx + + 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. + +=========================================================================*/ +#include "QQuickVTKRenderItem.h" + +// vtk includes +#include "QQuickVTKInteractiveWidget.h" +#include "QQuickVTKInteractorAdapter.h" +#include "QQuickVTKRenderWindow.h" +#include "vtkImageData.h" +#include "vtkRenderWindow.h" + +// Qt includes +#include <QQuickWindow> + +//------------------------------------------------------------------------------------------------- +QQuickVTKRenderItem::QQuickVTKRenderItem(QQuickItem* parent) + : Superclass(parent) +{ + // Accept mouse events + setAcceptHoverEvents(true); + setAcceptedMouseButtons(Qt::AllButtons); + setFlag(QQuickItem::ItemIsFocusScope); + setFlag(QQuickItem::ItemHasContents); + + QObject::connect( + this, &QQuickItem::windowChanged, this, &QQuickVTKRenderItem::handleWindowChanged); +} + +//------------------------------------------------------------------------------------------------- +QQuickVTKRenderWindow* QQuickVTKRenderItem::renderWindow() const +{ + return this->m_renderWindow; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::setRenderWindow(QQuickVTKRenderWindow* w) +{ + if (this->m_renderWindow == w) + { + return; + } + + if (this->m_renderWindow) + { + this->m_renderWindow->renderWindow()->RemoveRenderer(this->m_renderer); + } + + this->m_renderWindow = w; + if (this->m_renderWindow && this->m_renderWindow->renderWindow()) + { + this->m_renderWindow->renderWindow()->AddRenderer(this->m_renderer); + } + + this->m_renderWindow->render(); +} + +//------------------------------------------------------------------------------------------------- +vtkRenderer* QQuickVTKRenderItem::renderer() const +{ + return this->m_renderer; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::setViewport(const QRectF& rect) +{ + if (!this->m_renderWindow) + { + return; + } + double viewport[4]; + this->m_renderWindow->mapToViewport(rect, viewport); + m_renderer->SetViewport(viewport); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::sync() +{ + if (!this->isVisible()) + { + return; + } + + if (!this->m_renderWindow) + { + return; + } + + // Forward the synchronize call to the window + this->m_renderWindow->sync(); + + // Explicitly set the viewport for each render window + // This is done after the window sync to ensure that the window size is set up. + QRectF rect(0, 0, this->width(), this->height()); + rect = this->mapRectToScene(rect); + this->setViewport(rect); + + // Now synchronize all the widgets + for (auto it = this->m_widgets.constBegin(); it < this->m_widgets.constEnd(); ++it) + { + (*it)->sync(this->renderer()); + } +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::paint() +{ + if (!this->isVisible()) + { + return; + } + if (!this->m_renderWindow) + { + return; + } + + // Forward the paint call to the window + this->m_renderWindow->paint(); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::cleanup() +{ + if (!this->isVisible()) + { + return; + } + if (!this->m_renderWindow) + { + return; + } + + this->m_renderer->ReleaseGraphicsResources(this->m_renderWindow->renderWindow()); + // Forward the cleanup call to the window + this->m_renderWindow->cleanup(); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::handleWindowChanged(QQuickWindow* w) +{ + if (this->window()) + { + QObject::disconnect( + this->window(), &QQuickWindow::beforeSynchronizing, this, &QQuickVTKRenderItem::sync); + QObject::disconnect( + window(), &QQuickWindow::beforeRendering, this, &QQuickVTKRenderItem::paint); + QObject::disconnect( + window(), &QQuickWindow::sceneGraphInvalidated, this, &QQuickVTKRenderItem::cleanup); + } + + if (w) + { + QObject::connect(w, &QQuickWindow::beforeSynchronizing, this, &QQuickVTKRenderItem::sync, + Qt::DirectConnection); + QObject::connect( + w, &QQuickWindow::beforeRendering, this, &QQuickVTKRenderItem::paint, Qt::DirectConnection); + QObject::connect(w, &QQuickWindow::sceneGraphInvalidated, this, &QQuickVTKRenderItem::cleanup, + Qt::DirectConnection); + } +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) +{ + if (!this->renderWindow()) + { + return; + } + this->renderWindow()->interactorAdapter()->QueueGeometryChanged(newGeometry, oldGeometry); + + Superclass::geometryChanged(newGeometry, oldGeometry); +} + +//------------------------------------------------------------------------------------------------- +bool QQuickVTKRenderItem::event(QEvent* ev) +{ + if (!ev) + { + return false; + } + + switch (ev->type()) + { + case QEvent::HoverEnter: + case QEvent::HoverLeave: + case QEvent::HoverMove: + { + this->renderWindow()->interactorAdapter()->QueueHoverEvent( + this, static_cast<QHoverEvent*>(ev)); + break; + } + case QEvent::KeyPress: + case QEvent::KeyRelease: + { + this->renderWindow()->interactorAdapter()->QueueKeyEvent(this, static_cast<QKeyEvent*>(ev)); + break; + } + case QEvent::FocusIn: + case QEvent::FocusOut: + { + this->renderWindow()->interactorAdapter()->QueueFocusEvent( + this, static_cast<QFocusEvent*>(ev)); + break; + } + case QEvent::MouseMove: + case QEvent::MouseButtonPress: + case QEvent::MouseButtonRelease: + case QEvent::MouseButtonDblClick: + { + this->renderWindow()->interactorAdapter()->QueueMouseEvent( + this, static_cast<QMouseEvent*>(ev)); + break; + } +#ifndef QT_NO_WHEELEVENT + case QEvent::Wheel: + { + this->renderWindow()->interactorAdapter()->QueueWheelEvent( + this, static_cast<QWheelEvent*>(ev)); + break; + } +#endif + default: + { + return this->Superclass::event(ev); + } + } + + ev->accept(); + return true; +} + +//------------------------------------------------------------------------------------------------- +vtkSmartPointer<vtkImageData> QQuickVTKRenderItem::captureScreenshot() +{ + if (!this->renderWindow()) + { + return nullptr; + } + return this->renderWindow()->captureScreenshot(this->m_renderer->GetViewport()); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::addWidget(QQuickVTKInteractiveWidget* w) +{ + this->m_widgets.push_back(w); + this->update(); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::removeWidget(QQuickVTKInteractiveWidget* w) +{ + this->m_widgets.removeOne(w); + this->update(); +} + +//------------------------------------------------------------------------------------------------- +QQuickVTKInteractiveWidget* QQuickVTKRenderItem::widgetByName(QString name) const +{ + for (auto it = this->m_widgets.constBegin(); it < this->m_widgets.constEnd(); ++it) + { + if ((*it)->objectName().compare(name) == 0) + { + return (*it); + } + } + return nullptr; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderItem::removeWidgetByName(QString name) +{ + auto w = this->widgetByName(name); + if (!w) + { + return; + } + + this->removeWidget(w); +} diff --git a/GUISupport/QtQuick/QQuickVTKRenderItem.h b/GUISupport/QtQuick/QQuickVTKRenderItem.h new file mode 100644 index 0000000000000000000000000000000000000000..e139cbfbe1c4b496e9727047528c5efe494c364a --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKRenderItem.h @@ -0,0 +1,244 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQuickVTKRenderItem.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 QQuickVTKRenderItem + * @brief [QQuickItem] subclass to render a VTK scene in a QtQuick/QML application. + * + * QQuickVTKRenderItem extends [QQuickItem] so that a VTK visualization pipeline can be rendererd + * within the rect of the item. + * + * This item is exported to the QML layer via the QQmlVTKPlugin under the module VTK. It is + * registered as a type \b VTKRenderItem. The QQuickVTKRenderItem manages a vtkRenderer internally + * that is rendered as a viewport inside the render window provided by QQuickVTKRenderWindow. + * + * Typical usage for QQuickVTKRenderItem 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 + * } + * } + * @endcode + * + * The corresponding C++ class that sets up the VTK pipeline would look like the following: + * + * @code + * QQmlApplicaQtionEngine engine; + * engine.load(QUrl("qrc:///<qmlfile>.qml")); + * + * QObject* topLevel = engine.rootObjects().value(0); + * QQuickWindow* window = qobject_cast<QQuickWindow*>(topLevel); + * + * window->show(); + * + * // Fetch the QQuick window using the standard object name set up in the constructor + * QQuickVTKRenderItem* qquickvtkItem = topLevel->findChild<QQuickVTKRenderItem*>("ConeView"); + * + * // Create a cone pipeline and add it to the view + * vtkNew<vtkActor> actor; + * vtkNew<vtkPolyDataMapper> mapper; + * vtkNew<vtkConeSource> cone; + * mapper->SetInputConnection(cone->GetOutputPort()); + * actor->SetMapper(mapper); + * qquickvtkItem->renderer()->AddActor(actor); + * qquickvtkItem->renderer()->ResetCamera(); + * qquickvtkItem->renderer()->SetBackground(0.5, 0.5, 0.7); + * qquickvtkItem->renderer()->SetBackground2(0.7, 0.7, 0.7); + * qquickvtkItem->renderer()->SetGradientBackground(true); + * qquickvtkItem->update(); + * @endcode + * + * ## 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. + * + * ## Interactive vtk widgets + * + * QQuickVTKRenderItem also supports interactive vtk widgets with QtQuick's threaded render loop via + * the QQuickVTKInteractiveWidget class. + * + * [QQuickItem]: https://doc.qt.io/qt-5/qquickitem.html + * [QQuickWindow]: https://doc.qt.io/qt-5/qquickwindow.html + */ + +#ifndef QQuickVTKRenderItem_h +#define QQuickVTKRenderItem_h + +// Qt includes +#include <QOpenGLFunctions> // For QOpenGLFunctions +#include <QQuickItem> + +// vtk includes +#include "vtkNew.h" // For vtkNew +#include "vtkRenderer.h" // For vtkRenderer + +#include "vtkGUISupportQtQuickModule.h" // for export macro + +// Forward declarations +class QHoverEvent; +class QKeyEvent; +class QMouseEvent; +class QQuickVTKInteractiveWidget; +class QQuickVTKRenderWindow; +class vtkImageData; + +class VTKGUISUPPORTQTQUICK_EXPORT QQuickVTKRenderItem + : public QQuickItem + , protected QOpenGLFunctions +{ + Q_OBJECT + typedef QQuickItem Superclass; + + Q_PROPERTY(QQuickVTKRenderWindow* renderWindow READ renderWindow WRITE setRenderWindow) + +public: + QQuickVTKRenderItem(QQuickItem* parent = nullptr); + ~QQuickVTKRenderItem() = default; + + ///@{ + /** + * Set/Get the render window for the item + */ + QQuickVTKRenderWindow* renderWindow() const; + virtual void setRenderWindow(QQuickVTKRenderWindow* w); + ///@} + + /** + * Get access to the renderer + */ + vtkRenderer* renderer() const; + + /** + * Capture a screenshot of the view + * + * \returns Image data containing the view capture. + * \sa QQuickVTKRenderWindow::captureScreenshot + */ + virtual vtkSmartPointer<vtkImageData> captureScreenshot(); + + ///@{ + /** + * Add/Remove widgets to/from the view + */ + virtual void addWidget(QQuickVTKInteractiveWidget* w); + virtual void removeWidget(QQuickVTKInteractiveWidget* w); + ///@} + + ///@{ + /** + * Get/Remove widgets from the view by their object name + */ + virtual QQuickVTKInteractiveWidget* widgetByName(QString name) const; + virtual void removeWidgetByName(QString name); + ///@} + +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(); + + /** + * 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 beforeRendering 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(); + +protected Q_SLOTS: + virtual void handleWindowChanged(QQuickWindow* w); + +protected: + // Helper members + QQuickVTKRenderWindow* m_renderWindow = nullptr; + vtkNew<vtkRenderer> m_renderer; + + QVector<QQuickVTKInteractiveWidget*> m_widgets; + + /** + * Set the viewport for this item + */ + virtual void setViewport(const QRectF& rect); + + // Event handlers + void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) override; + bool event(QEvent* ev) override; + +private: + QQuickVTKRenderItem(const QQuickVTKRenderItem&) = delete; + void operator=(const QQuickVTKRenderItem) = delete; +}; + +#endif // QQuickVTKRenderItem_h diff --git a/GUISupport/QtQuick/QQuickVTKRenderWindow.cxx b/GUISupport/QtQuick/QQuickVTKRenderWindow.cxx new file mode 100644 index 0000000000000000000000000000000000000000..4280858189ba2b98d80b749f93b937ab3e079fbf --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKRenderWindow.cxx @@ -0,0 +1,299 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: QQuickVTKRenderWindow.cxx + + 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. + +=========================================================================*/ +#include "QQuickVTKRenderWindow.h" + +// vtk includes +#include "QQuickVTKInteractorAdapter.h" +#include "QVTKInteractor.h" +#include "vtkGenericOpenGLRenderWindow.h" +#include "vtkImageData.h" +#include "vtkInteractorStyleTrackballCamera.h" +#include "vtkOpenGLState.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkWindowToImageFilter.h" + +// Qt includes +#include <QQuickWindow> + +//------------------------------------------------------------------------------------------------- +QQuickVTKRenderWindow::QQuickVTKRenderWindow(QQuickItem* parent) + : Superclass(parent) +{ + vtkNew<vtkGenericOpenGLRenderWindow> renWin; + this->setRenderWindow(renWin); + this->m_interactorAdapter = new QQuickVTKInteractorAdapter(this); + QObject::connect( + this, &QQuickItem::windowChanged, this, &QQuickVTKRenderWindow::handleWindowChanged); + + // Set a standard object name + this->setObjectName("QQuickVTKRenderWindow"); +} + +//------------------------------------------------------------------------------------------------- +QQuickVTKRenderWindow::~QQuickVTKRenderWindow() +{ + this->m_renderWindow = nullptr; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::sync() +{ + if (!this->isVisible()) + { + return; + } + + if (!this->m_renderWindow) + { + return; + } + + QSize windowSize = window()->size() * window()->devicePixelRatio(); + this->m_renderWindow->SetSize(windowSize.width(), windowSize.height()); + if (auto iren = this->m_renderWindow->GetInteractor()) + { + iren->SetSize(windowSize.width(), windowSize.height()); + m_interactorAdapter->ProcessEvents(iren); + } +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::paint() +{ + if (!this->isVisible()) + { + return; + } + + if (!this->m_renderWindow) + { + // no render window set, just fill with white. + QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions(); + f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f); + f->glClear(GL_COLOR_BUFFER_BIT); + return; + } + + auto iren = this->m_renderWindow->GetInteractor(); + if (!this->m_initialized) + { + initializeOpenGLFunctions(); + if (iren) + { + iren->Initialize(); + } + this->m_renderWindow->SetMapped(true); + this->m_renderWindow->SetIsCurrent(true); + + // Since the context is being setup, call OpenGLInitContext + this->m_renderWindow->SetForceMaximumHardwareLineWidth(1); + this->m_renderWindow->SetOwnContext(true); + this->m_renderWindow->OpenGLInitContext(); + + m_initialized = true; + } + + auto ostate = this->m_renderWindow->GetState(); + ostate->Reset(); + ostate->Push(); + // By default, Qt sets the depth function to GL_LESS but VTK expects GL_LEQUAL + ostate->vtkglDepthFunc(GL_LEQUAL); + + this->m_renderWindow->SetReadyForRendering(true); + if (iren) + { + iren->Render(); + } + else + { + this->m_renderWindow->Render(); + } + + if (this->m_screenshotScheduled) + { + this->m_screenshotFilter->SetInput(this->m_renderWindow); + this->m_screenshotFilter->SetReadFrontBuffer(false); + this->m_screenshotFilter->SetInputBufferTypeToRGB(); + this->m_screenshotFilter->Update(); + this->m_screenshotScheduled = false; + } + this->m_renderWindow->SetReadyForRendering(false); + + ostate->Pop(); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::cleanup() +{ + if (this->m_renderWindow) + { + this->m_renderWindow->ReleaseGraphicsResources(this->m_renderWindow); + } +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::handleWindowChanged(QQuickWindow* w) +{ + this->m_interactorAdapter->setQQuickWindow(w); + if (w) + { + // Do not clear the scenegraph before the QML rendering + // to preserve the VTK render + w->setClearBeforeRendering(false); + // This allows the cleanup method to be called on the render thread + w->setPersistentSceneGraph(false); + } +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::setRenderWindow(vtkRenderWindow* renWin) +{ + auto gwin = vtkGenericOpenGLRenderWindow::SafeDownCast(renWin); + if (renWin != nullptr && gwin == nullptr) + { + qDebug() << "QQuickVTKRenderWindow requires a `vtkGenericOpenGLRenderWindow`. `" + << renWin->GetClassName() << "` is not supported."; + } + this->setRenderWindow(gwin); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::setRenderWindow(vtkGenericOpenGLRenderWindow* renWin) +{ + if (this->m_renderWindow == renWin) + { + return; + } + + this->m_renderWindow = renWin; + this->m_initialized = false; + + if (this->m_renderWindow) + { + this->m_renderWindow->SetMultiSamples(0); + this->m_renderWindow->SetReadyForRendering(false); + this->m_renderWindow->SetFrameBlitModeToBlitToHardware(); + vtkNew<QVTKInteractor> iren; + iren->SetRenderWindow(this->m_renderWindow); + + // now set the default style + vtkNew<vtkInteractorStyleTrackballCamera> style; + iren->SetInteractorStyle(style); + + this->m_renderWindow->SetReadyForRendering(false); + } +} + +//------------------------------------------------------------------------------------------------- +vtkRenderWindow* QQuickVTKRenderWindow::renderWindow() const +{ + return this->m_renderWindow; +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::mapToViewport(const QRectF& rect, double viewport[4]) +{ + viewport[0] = rect.topLeft().x(); + viewport[1] = rect.topLeft().y(); + viewport[2] = rect.bottomRight().x(); + viewport[3] = rect.bottomRight().y(); + + if (this->m_renderWindow) + { + int* windowSize = this->m_renderWindow->GetSize(); + if (windowSize && windowSize[0] != 0 && windowSize[1] != 0) + { + viewport[0] = viewport[0] / (windowSize[0] - 1.0); + viewport[1] = viewport[1] / (windowSize[1] - 1.0); + viewport[2] = viewport[2] / (windowSize[0] - 1.0); + viewport[3] = viewport[3] / (windowSize[1] - 1.0); + } + } + + // Change to quadrant I (vtk) from IV (Qt) + double tmp = 1.0 - viewport[1]; + viewport[1] = 1.0 - viewport[3]; + viewport[3] = tmp; + + for (int i = 0; i < 3; ++i) + { + viewport[i] = viewport[i] > 0.0 ? viewport[i] : 0.0; + viewport[i] = viewport[i] > 1.0 ? 1.0 : viewport[i]; + } +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) +{ + m_interactorAdapter->QueueGeometryChanged(newGeometry, oldGeometry); + + Superclass::geometryChanged(newGeometry, oldGeometry); +} + +//------------------------------------------------------------------------------------------------- +QPointer<QQuickVTKInteractorAdapter> QQuickVTKRenderWindow::interactorAdapter() const +{ + return this->m_interactorAdapter; +} + +//------------------------------------------------------------------------------------------------- +vtkSmartPointer<vtkImageData> QQuickVTKRenderWindow::captureScreenshot() +{ + double viewport[4] = { 0, 0, 1, 1 }; + return this->captureScreenshot(viewport); +} + +//------------------------------------------------------------------------------------------------- +vtkSmartPointer<vtkImageData> QQuickVTKRenderWindow::captureScreenshot(double* viewport) +{ + if (!this->window()) + { + return nullptr; + } + this->m_screenshotScheduled = true; + this->m_screenshotFilter->SetViewport(viewport); + this->renderNow(); + return this->m_screenshotFilter->GetOutput(); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::renderNow() +{ + if (!this->window()) + { + return; + } + // Schedule a scenegraph update + this->window()->update(); + // Wait for the update to complete + QEventLoop loop; + QObject::connect(this->window(), &QQuickWindow::afterRendering, &loop, &QEventLoop::quit); + loop.exec(); +} + +//------------------------------------------------------------------------------------------------- +void QQuickVTKRenderWindow::render() +{ + if (this->window()) + { + this->window()->update(); + } +} + +//------------------------------------------------------------------------------------------------- +bool QQuickVTKRenderWindow::isInitialized() const +{ + return this->m_initialized; +} diff --git a/GUISupport/QtQuick/QQuickVTKRenderWindow.h b/GUISupport/QtQuick/QQuickVTKRenderWindow.h new file mode 100644 index 0000000000000000000000000000000000000000..a0922b76a2bb1882f81382fbd61c15060db03e6a --- /dev/null +++ b/GUISupport/QtQuick/QQuickVTKRenderWindow.h @@ -0,0 +1,251 @@ +/*========================================================================= + + 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 + * + * 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 <QOpenGLFunctions> // For QOpenGLFunctions +#include <QPointer> // For QPointer +#include <QQuickItem> + +#include "vtkGUISupportQtQuickModule.h" // for export macro + +// Forward declarations +class QEvent; +class QQuickVTKInteractorAdapter; +class QQuickWindow; +class QWheelEvent; +class vtkGenericOpenGLRenderWindow; +class vtkImageData; +class vtkRenderWindow; +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/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. + */ + void setRenderWindow(vtkRenderWindow* renWin); + 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<QQuickVTKInteractorAdapter> 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<vtkImageData> captureScreenshot(); + virtual vtkSmartPointer<vtkImageData> 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(); + + /** + * 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 beforeRendering 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<QQuickVTKInteractorAdapter> m_interactorAdapter; + vtkSmartPointer<vtkGenericOpenGLRenderWindow> m_renderWindow; + bool m_initialized = false; + + // Screenshot stuff + bool m_screenshotScheduled = false; + vtkNew<vtkWindowToImageFilter> m_screenshotFilter; + + // Event handlers + void geometryChanged(const QRectF& newGeometry, const QRectF& oldGeometry) override; + +private: + QQuickVTKRenderWindow(const QQuickVTKRenderWindow&) = delete; + void operator=(const QQuickVTKRenderWindow) = delete; +}; + +#endif // QQuickVTKRenderWindow_h diff --git a/GUISupport/QtQuick/Testing/CMakeLists.txt b/GUISupport/QtQuick/Testing/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..49a7f4d6968a22f3e7ffd5a85674c4350a6c5598 --- /dev/null +++ b/GUISupport/QtQuick/Testing/CMakeLists.txt @@ -0,0 +1 @@ +add_subdirectory(Cxx) diff --git a/GUISupport/QtQuick/Testing/Cxx/CMakeLists.txt b/GUISupport/QtQuick/Testing/Cxx/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..24430fb9e652e819ec9b48126c9c3371d4d6080e --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/CMakeLists.txt @@ -0,0 +1,30 @@ +find_package ("Qt${vtk_qt_major_version}" + COMPONENTS Quick Qml + REQUIRED + ) + +set (CMAKE_AUTOMOC ON) +set (CMAKE_AUTOUIC ON) +set (CMAKE_AUTORCC ON) + +vtk_add_test_cxx (vtkGUISupportQtQuickCxxTests tests + TestQQuickVTKRenderItem.cxx + TestQQuickVTKRenderItemWidget.cxx + TestQQuickVTKRenderWindow.cxx + ) + +vtk_test_cxx_executable(vtkGUISupportQtQuickCxxTests tests + TestQQuickVTK.qrc + ) + +target_link_libraries(vtkGUISupportQtQuickCxxTests + PRIVATE + "Qt${vtk_qt_major_version}::Quick" + "Qt${vtk_qt_major_version}::Qml" + ) + +set_tests_properties( + VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderItem + VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderItemWidget + VTK::GUISupportQtQuickCxx-TestQQuickVTKRenderWindow + PROPERTIES ENVIRONMENT "QML2_IMPORT_PATH=${CMAKE_BINARY_DIR}/$<CONFIG>") diff --git a/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTK.qrc b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTK.qrc new file mode 100644 index 0000000000000000000000000000000000000000..f881f539f277f92d676f9421e739cf59e8cc36fc --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTK.qrc @@ -0,0 +1,7 @@ +<!DOCTYPE RCC><RCC version="1.0"> + <qresource prefix="/"> + <file>TestQQuickVTKRenderItem.qml</file> + <file>TestQQuickVTKRenderItemWidget.qml</file> + <file>TestQQuickVTKRenderWindow.qml</file> + </qresource> +</RCC> diff --git a/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItem.cxx b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItem.cxx new file mode 100644 index 0000000000000000000000000000000000000000..260f4ceb37e5de1af1a1a14b10c9e388fd90c03a --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItem.cxx @@ -0,0 +1,107 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: TestQQuickVTKRenderItem.cxx + + 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. + +=========================================================================*/ + +// Description +// Tests QQuickVTKRenderItem + +#include "QQuickVTKRenderItem.h" +#include "QQuickVTKRenderWindow.h" +#include "vtkActor.h" +#include "vtkConeSource.h" +#include "vtkGenericOpenGLRenderWindow.h" +#include "vtkNew.h" +#include "vtkPNGWriter.h" +#include "vtkPolyDataMapper.h" +#include "vtkRenderer.h" +#include "vtkTestUtilities.h" +#include "vtkTesting.h" +#include "vtkWindowToImageFilter.h" + +#include <QApplication> +#include <QDebug> +#include <QQmlApplicationEngine> +#include <QQuickWindow> +#include <QTimer> +#include <QUrl> + +int TestQQuickVTKRenderItem(int argc, char* argv[]) +{ + cout << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl; + + QApplication app(argc, argv); + + QQmlApplicationEngine engine; + qDebug() << "QML2_IMPORT_PATH:" << engine.importPathList(); + engine.load(QUrl("qrc:///TestQQuickVTKRenderItem.qml")); + + QObject* topLevel = engine.rootObjects().value(0); + QQuickWindow* window = qobject_cast<QQuickWindow*>(topLevel); + + window->show(); + + // Fetch the QQuick window using the standard object name set up in the constructor + QQuickVTKRenderItem* qquickvtkItem = topLevel->findChild<QQuickVTKRenderItem*>("ConeView"); + + // Create a cone pipeline and add it to the view + vtkNew<vtkActor> actor; + vtkNew<vtkPolyDataMapper> mapper; + vtkNew<vtkConeSource> cone; + mapper->SetInputConnection(cone->GetOutputPort()); + actor->SetMapper(mapper); + qquickvtkItem->renderer()->AddActor(actor); + qquickvtkItem->renderer()->ResetCamera(); + qquickvtkItem->renderer()->SetBackground(0.5, 0.5, 0.7); + qquickvtkItem->renderer()->SetBackground2(0.7, 0.7, 0.7); + qquickvtkItem->renderer()->SetGradientBackground(true); + qquickvtkItem->update(); + + vtkNew<vtkTesting> vtktesting; + vtktesting->AddArguments(argc, argv); + if (vtktesting->IsInteractiveModeSpecified()) + { + return app.exec(); + } + + // Wait a little for the application and window to be set up properly + QEventLoop loop; + QTimer::singleShot(100, &loop, SLOT(quit())); + loop.exec(); + + // Capture a screenshot of the item + vtkSmartPointer<vtkImageData> im = qquickvtkItem->captureScreenshot(); + + std::string validName = std::string(vtktesting->GetValidImageFileName()); + std::string::size_type slashPos = validName.rfind('/'); + if (slashPos != std::string::npos) + { + validName = validName.substr(slashPos + 1); + } + std::string tmpDir = vtktesting->GetTempDirectory(); + std::string vImage = tmpDir + "/" + validName; + vtkNew<vtkPNGWriter> w; + w->SetInputData(im); + w->SetFileName(vImage.c_str()); + w->Write(); + + int retVal = vtktesting->RegressionTest(vImage, 10); + + switch (retVal) + { + case vtkTesting::FAILED: + case vtkTesting::NOT_RUN: + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItem.qml b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItem.qml new file mode 100644 index 0000000000000000000000000000000000000000..48d1cbc4fb57fc94701752f85b955cc1d780fb8e --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItem.qml @@ -0,0 +1,74 @@ +// 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 + } + + // menubar with two menus + menuBar: MenuBar { + Menu { + title: qsTr("File") + MenuItem { + text: qsTr("&Quit") + onTriggered: Qt.quit() + } + } + Menu { + title: qsTr("Edit") + } + } + + // Content area + + // a rectangle in the middle of the content area + Rectangle { + width: 100 + height: 100 + color: "blue" + border.color: "red" + border.width: 5 + radius: 10 + } + Text { + id: label + color: "white" + wrapMode: Text.WordWrap + text: "Custom QML\nrectangle &\ntext" + anchors.right: parent.right + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: 10 + width: 100 + } + + VTKRenderWindow { + id: vtkwindow + width: 400 + height: 400 + } + + VTKRenderItem { + objectName: "ConeView" + x: 200 + y: 200 + width: 200 + height: 200 + renderWindow: vtkwindow + focus: true + } +} diff --git a/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItemWidget.cxx b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItemWidget.cxx new file mode 100644 index 0000000000000000000000000000000000000000..49f652d29f48f119808160512ca6cf957b0f55cb --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItemWidget.cxx @@ -0,0 +1,216 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: TestQQuickVTKRenderItemWidget.cxx + + 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. + +=========================================================================*/ + +// Description +// Tests QQuickVTKRenderItem + +#include "QQuickVTKInteractiveWidget.h" +#include "QQuickVTKRenderItem.h" +#include "QQuickVTKRenderWindow.h" +#include "vtkActor.h" +#include "vtkAppendPolyData.h" +#include "vtkCamera.h" +#include "vtkClipPolyData.h" +#include "vtkCommand.h" +#include "vtkConeSource.h" +#include "vtkGenericOpenGLRenderWindow.h" +#include "vtkGlyph3D.h" +#include "vtkImplicitPlaneRepresentation.h" +#include "vtkImplicitPlaneWidget2.h" +#include "vtkInteractorEventRecorder.h" +#include "vtkNew.h" +#include "vtkPNGWriter.h" +#include "vtkPlane.h" +#include "vtkPolyData.h" +#include "vtkPolyDataMapper.h" +#include "vtkProperty.h" +#include "vtkRenderWindowInteractor.h" +#include "vtkRenderer.h" +#include "vtkSphereSource.h" +#include "vtkTestUtilities.h" +#include "vtkTesting.h" +#include "vtkWindowToImageFilter.h" + +#include <QApplication> +#include <QDebug> +#include <QQmlApplicationEngine> +#include <QQuickWindow> +#include <QTimer> +#include <QUrl> + +class TestQQuickVTKRenderItemWidgetCallback : public vtkCommand +{ +public: + static TestQQuickVTKRenderItemWidgetCallback* New() + { + return new TestQQuickVTKRenderItemWidgetCallback; + } + void Execute(vtkObject* caller, unsigned long, void*) override + { + vtkImplicitPlaneWidget2* planeWidget = reinterpret_cast<vtkImplicitPlaneWidget2*>(caller); + vtkImplicitPlaneRepresentation* rep = + reinterpret_cast<vtkImplicitPlaneRepresentation*>(planeWidget->GetRepresentation()); + rep->GetPlane(this->Plane); + this->Actor->VisibilityOn(); + } + TestQQuickVTKRenderItemWidgetCallback() + : Plane(nullptr) + , Actor(nullptr) + { + } + vtkPlane* Plane; + vtkActor* Actor; +}; + +int TestQQuickVTKRenderItemWidget(int argc, char* argv[]) +{ + cout << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl; + + QApplication app(argc, argv); + + QQmlApplicationEngine engine; + qDebug() << "QML2_IMPORT_PATH:" << engine.importPathList(); + engine.load(QUrl("qrc:///TestQQuickVTKRenderItemWidget.qml")); + + QObject* topLevel = engine.rootObjects().value(0); + QQuickWindow* window = qobject_cast<QQuickWindow*>(topLevel); + + window->show(); + + // Fetch the QQuick window using the standard object name set up in the constructor + QQuickVTKRenderWindow* qquickvtkWindow = + topLevel->findChild<QQuickVTKRenderWindow*>("QQuickVTKRenderWindow"); + + // Fetch the QQuick item using the object name set up in the qml file + QQuickVTKRenderItem* coneItem = topLevel->findChild<QQuickVTKRenderItem*>("ConeView"); + // Create a cone pipeline and add it to the view + vtkNew<vtkActor> actor; + vtkNew<vtkPolyDataMapper> mapper; + vtkNew<vtkConeSource> cone; + mapper->SetInputConnection(cone->GetOutputPort()); + actor->SetMapper(mapper); + coneItem->renderer()->AddActor(actor); + coneItem->renderer()->SetBackground(0.5, 0.5, 0.7); + coneItem->renderer()->SetBackground2(0.7, 0.7, 0.7); + coneItem->renderer()->SetGradientBackground(true); + coneItem->update(); + + // Fetch the QQuick item using the object name set up in the qml file + QQuickVTKRenderItem* widgetItem = topLevel->findChild<QQuickVTKRenderItem*>("WidgetView"); + // Create a mace out of filters. + // + vtkNew<vtkSphereSource> sphere; + vtkNew<vtkGlyph3D> glyph; + glyph->SetInputConnection(sphere->GetOutputPort()); + glyph->SetSourceConnection(cone->GetOutputPort()); + glyph->SetVectorModeToUseNormal(); + glyph->SetScaleModeToScaleByVector(); + glyph->SetScaleFactor(0.25); + + // The sphere and spikes are appended into a single polydata. + // This just makes things simpler to manage. + vtkNew<vtkAppendPolyData> apd; + apd->AddInputConnection(glyph->GetOutputPort()); + apd->AddInputConnection(sphere->GetOutputPort()); + + vtkNew<vtkPolyDataMapper> maceMapper; + maceMapper->SetInputConnection(apd->GetOutputPort()); + + vtkNew<vtkActor> maceActor; + maceActor->SetMapper(maceMapper); + maceActor->VisibilityOn(); + + // This portion of the code clips the mace with the vtkPlanes + // implicit function. The clipped region is colored green. + vtkNew<vtkPlane> plane; + vtkNew<vtkClipPolyData> clipper; + clipper->SetInputConnection(apd->GetOutputPort()); + clipper->SetClipFunction(plane); + clipper->InsideOutOn(); + + vtkNew<vtkPolyDataMapper> selectMapper; + selectMapper->SetInputConnection(clipper->GetOutputPort()); + + vtkNew<vtkActor> selectActor; + selectActor->SetMapper(selectMapper); + selectActor->GetProperty()->SetColor(0, 1, 0); + selectActor->VisibilityOff(); + selectActor->SetScale(1.01, 1.01, 1.01); + + // The SetInteractor method is how 3D widgets are associated with the render + // window interactor. Internally, SetInteractor sets up a bunch of callbacks + // using the Command/Observer mechanism (AddObserver()). + vtkNew<TestQQuickVTKRenderItemWidgetCallback> myCallback; + myCallback->Plane = plane; + myCallback->Actor = selectActor; + + vtkNew<vtkImplicitPlaneRepresentation> rep; + vtkNew<vtkImplicitPlaneWidget2> planeWidget; + planeWidget->SetRepresentation(rep); + planeWidget->AddObserver(vtkCommand::InteractionEvent, myCallback); + + QQuickVTKInteractiveWidget* qquickVTKWidget = new QQuickVTKInteractiveWidget(window); + qquickVTKWidget->setWidget(planeWidget); + qquickVTKWidget->setEnabled(true); + + widgetItem->renderer()->AddActor(maceActor); + widgetItem->renderer()->AddActor(selectActor); + widgetItem->addWidget(qquickVTKWidget); + widgetItem->update(); + + // Wait a little for the application and window to be set up properly + QEventLoop loop; + QTimer::singleShot(100, &loop, SLOT(quit())); + loop.exec(); + + // Once the application is up, adjust the camera, widget reps, etc. + widgetItem->renderer()->ResetCamera(); + rep->SetPlaceFactor(1.25); + rep->PlaceWidget(glyph->GetOutput()->GetBounds()); + widgetItem->renderer()->GetActiveCamera()->Azimuth(20); + + vtkNew<vtkTesting> vtktesting; + vtktesting->AddArguments(argc, argv); + if (vtktesting->IsInteractiveModeSpecified()) + { + return app.exec(); + } + + // Capture a screenshot of the item + vtkSmartPointer<vtkImageData> im = qquickvtkWindow->captureScreenshot(); + + std::string validName = std::string(vtktesting->GetValidImageFileName()); + std::string::size_type slashPos = validName.rfind('/'); + if (slashPos != std::string::npos) + { + validName = validName.substr(slashPos + 1); + } + std::string tmpDir = vtktesting->GetTempDirectory(); + std::string vImage = tmpDir + "/" + validName; + vtkNew<vtkPNGWriter> w; + w->SetInputData(im); + w->SetFileName(vImage.c_str()); + w->Write(); + + int retVal = vtktesting->RegressionTest(vImage, 10); + + switch (retVal) + { + case vtkTesting::FAILED: + case vtkTesting::NOT_RUN: + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItemWidget.qml b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItemWidget.qml new file mode 100644 index 0000000000000000000000000000000000000000..0a24e709755fad18101ee8ebc800599bc9b6b1eb --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderItemWidget.qml @@ -0,0 +1,89 @@ +// import related modules +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.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: 800 + height: 800 + color: palette.window + + SystemPalette { + id: palette + colorGroup: SystemPalette.Active + } + + // menubar with two menus + menuBar: MenuBar { + Menu { + title: qsTr("File") + MenuItem { + text: qsTr("&Quit") + onTriggered: Qt.quit() + } + } + Menu { + title: qsTr("Edit") + } + } + + // Content area + + // a rectangle in the middle of the content area + Rectangle { + width: 100 + height: 100 + color: "blue" + border.color: "red" + border.width: 5 + radius: 10 + } + Text { + id: label + color: "white" + wrapMode: Text.WordWrap + text: "Custom QML\nrectangle &\ntext" + anchors.right: parent.right + anchors.left: parent.left + anchors.top: parent.top + anchors.margins: 10 + width: 100 + } + + VTKRenderWindow { + id: vtkwindow + anchors.fill: parent + } + + RowLayout { + anchors.fill: parent + + VTKRenderItem { + objectName: "ConeView" + Layout.column: 0 + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumWidth: 100 + Layout.preferredWidth: 200 + renderWindow: vtkwindow + focus: true + } + VTKRenderItem { + objectName: "WidgetView" + Layout.column: 1 + Layout.fillWidth: true + Layout.fillHeight: true + Layout.minimumWidth: 100 + Layout.preferredWidth: 400 + renderWindow: vtkwindow + focus: true + } + } +} diff --git a/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderWindow.cxx b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderWindow.cxx new file mode 100644 index 0000000000000000000000000000000000000000..a35467d2d12ed5c61f057bfa5838b2c596207a9e --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderWindow.cxx @@ -0,0 +1,174 @@ +/*========================================================================= + + Program: Visualization Toolkit + Module: TestQQuickVTKRenderWindow.cxx + + 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. + +=========================================================================*/ + +// Description +// Tests QQuickVTKRenderWindow/QQuickVTKRenderItem + +#include "QQuickVTKRenderItem.h" +#include "QQuickVTKRenderWindow.h" +#include "vtkActor.h" +#include "vtkColorTransferFunction.h" +#include "vtkConeSource.h" +#include "vtkGenericOpenGLRenderWindow.h" +#include "vtkGlyph3DMapper.h" +#include "vtkNew.h" +#include "vtkPNGWriter.h" +#include "vtkPiecewiseFunction.h" +#include "vtkPolyDataMapper.h" +#include "vtkProperty.h" +#include "vtkRenderer.h" +#include "vtkSmartVolumeMapper.h" +#include "vtkSphereSource.h" +#include "vtkTestUtilities.h" +#include "vtkTesting.h" +#include "vtkVolume.h" +#include "vtkVolumeProperty.h" +#include "vtkWindowToImageFilter.h" +#include "vtkXMLImageDataReader.h" + +#include <QApplication> +#include <QDebug> +#include <QQmlApplicationEngine> +#include <QQuickWindow> +#include <QTimer> +#include <QUrl> + +int TestQQuickVTKRenderWindow(int argc, char* argv[]) +{ + cout << "CTEST_FULL_OUTPUT (Avoid ctest truncation of output)" << endl; + + QApplication app(argc, argv); + + QQmlApplicationEngine engine; + qDebug() << "QML2_IMPORT_PATH:" << engine.importPathList(); + engine.load(QUrl("qrc:///TestQQuickVTKRenderWindow.qml")); + + QObject* topLevel = engine.rootObjects().value(0); + QQuickWindow* window = qobject_cast<QQuickWindow*>(topLevel); + + window->show(); + + // Fetch the QQuick window using the standard object name set up in the constructor + QQuickVTKRenderItem* geomItem = topLevel->findChild<QQuickVTKRenderItem*>("GeomView"); + + // Create a cone pipeline and add it to the view + vtkNew<vtkActor> actor; + vtkNew<vtkPolyDataMapper> mapper; + vtkNew<vtkConeSource> cone; + mapper->SetInputConnection(cone->GetOutputPort()); + actor->SetMapper(mapper); + geomItem->renderer()->AddActor(actor); + geomItem->renderer()->ResetCamera(); + // geomItem->renderer()->SetBackground(0.5, 0.5, 0.7); + geomItem->renderer()->SetBackground2(0.7, 0.7, 0.7); + // geomItem->renderer()->SetGradientBackground(true); + geomItem->update(); + + // Now the volume view + QQuickVTKRenderItem* volumeItem = topLevel->findChild<QQuickVTKRenderItem*>("VolumeView"); + + // Create a volume pipeline and add it to the view + vtkNew<vtkSmartVolumeMapper> volumeMapper; + vtkNew<vtkXMLImageDataReader> reader; + const char* volumeFile = vtkTestUtilities::ExpandDataFileName(argc, argv, "Data/vase_1comp.vti"); + reader->SetFileName(volumeFile); + volumeMapper->SetInputConnection(reader->GetOutputPort()); + delete[] volumeFile; + double scalarRange[2]; + volumeMapper->GetInput()->GetScalarRange(scalarRange); + volumeMapper->SetAutoAdjustSampleDistances(1); + volumeMapper->SetBlendModeToComposite(); + vtkNew<vtkPiecewiseFunction> scalarOpacity; + scalarOpacity->AddPoint(scalarRange[0], 0.0); + scalarOpacity->AddPoint(scalarRange[1], 0.09); + vtkNew<vtkVolumeProperty> volumeProperty; + volumeProperty->ShadeOff(); + volumeProperty->SetInterpolationType(VTK_LINEAR_INTERPOLATION); + volumeProperty->SetScalarOpacity(scalarOpacity); + vtkSmartPointer<vtkColorTransferFunction> colorTransferFunction = + volumeProperty->GetRGBTransferFunction(0); + colorTransferFunction->RemoveAllPoints(); + colorTransferFunction->AddRGBPoint(scalarRange[0], 0.6, 0.4, 0.1); + // colorTransferFunction->AddRGBPoint(scalarRange[1], 0.2, 0.1, 0.3); + vtkSmartPointer<vtkVolume> volume = vtkSmartPointer<vtkVolume>::New(); + volume->SetMapper(volumeMapper); + volume->SetProperty(volumeProperty); + volumeItem->renderer()->AddVolume(volume); + volumeItem->renderer()->ResetCamera(); + // volumeItem->renderer()->SetBackground(0.5, 0.5, 0.7); + volumeItem->renderer()->SetBackground(0, 0, 1); + // volumeItem->renderer()->SetBackground2(0.7, 0.7, 0.7); + volumeItem->update(); + + // Now the glyph view + QQuickVTKRenderItem* glyphItem = topLevel->findChild<QQuickVTKRenderItem*>("GlyphView"); + + // Create the glyph pipeline + vtkNew<vtkSphereSource> sphere; + vtkNew<vtkGlyph3DMapper> glyphMapper; + vtkNew<vtkConeSource> squad; + glyphMapper->SetInputConnection(sphere->GetOutputPort()); + glyphMapper->SetSourceConnection(squad->GetOutputPort()); + glyphMapper->SetOrientationArray("Normals"); + vtkNew<vtkActor> glyphActor; + glyphActor->SetMapper(glyphMapper); + glyphActor->GetProperty()->SetDiffuseColor(0.5, 1.0, 0.8); + glyphItem->renderer()->AddActor(glyphActor); + glyphItem->renderer()->SetBackground(0.5, 0.5, 0.7); + glyphItem->renderer()->ResetCamera(); + glyphItem->update(); + + // Now the testing + vtkNew<vtkTesting> vtktesting; + vtktesting->AddArguments(argc, argv); + if (vtktesting->IsInteractiveModeSpecified()) + { + return app.exec(); + } + + // Wait a little for the application and window to be set up properly + QEventLoop loop; + QTimer::singleShot(100, &loop, SLOT(quit())); + loop.exec(); + + // Capture a screenshot of the window + // Fetch the QQuick window using the standard object name set up in the constructor + QQuickVTKRenderWindow* qquickvtkWindow = + topLevel->findChild<QQuickVTKRenderWindow*>("QQuickVTKRenderWindow"); + vtkSmartPointer<vtkImageData> im = qquickvtkWindow->captureScreenshot(); + + std::string validName = std::string(vtktesting->GetValidImageFileName()); + std::string::size_type slashPos = validName.rfind('/'); + if (slashPos != std::string::npos) + { + validName = validName.substr(slashPos + 1); + } + std::string tmpDir = vtktesting->GetTempDirectory(); + std::string vImage = tmpDir + "/" + validName; + vtkNew<vtkPNGWriter> w; + w->SetInputData(im); + w->SetFileName(vImage.c_str()); + w->Write(); + + int retVal = vtktesting->RegressionTest(vImage, 10); + + switch (retVal) + { + case vtkTesting::FAILED: + case vtkTesting::NOT_RUN: + return EXIT_FAILURE; + } + return EXIT_SUCCESS; +} diff --git a/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderWindow.qml b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderWindow.qml new file mode 100644 index 0000000000000000000000000000000000000000..0f3ae83ab8f9b7678660852dba0ee6a02d804f2b --- /dev/null +++ b/GUISupport/QtQuick/Testing/Cxx/TestQQuickVTKRenderWindow.qml @@ -0,0 +1,79 @@ +// import related modules +import QtQuick 2.15 +import QtQuick.Controls 2.15 +import QtQuick.Layouts 1.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: 800 + height: 600 + color: palette.window + + SystemPalette { + id: palette + colorGroup: SystemPalette.Active + } + + // menubar with two menus + menuBar: MenuBar { + Menu { + title: qsTr("File") + MenuItem { + text: qsTr("&Quit") + onTriggered: Qt.quit() + } + } + Menu { + title: qsTr("Edit") + } + } + + // Content area + // Create a VTK render window to represent the opengl context for all vtk items in this app + VTKRenderWindow { + id: vtkwindow + anchors.fill: parent + } + + SplitView { + anchors.fill: parent + orientation: Qt.Horizontal + + VTKRenderItem { + objectName: "VolumeView" + SplitView.fillHeight: true + SplitView.fillWidth: true + SplitView.minimumHeight: 100 + SplitView.minimumWidth: 100 + SplitView.preferredHeight: 200 + SplitView.preferredWidth: 200 + renderWindow: vtkwindow + } + + ColumnLayout { + SplitView.fillHeight: true + SplitView.fillWidth: true + SplitView.minimumWidth: 200 + SplitView.preferredWidth: 200 + VTKRenderItem { + objectName: "GlyphView" + renderWindow: vtkwindow + focus: true + Layout.fillHeight: true + Layout.fillWidth: true + } + VTKRenderItem { + objectName: "GeomView" + renderWindow: vtkwindow + Layout.fillHeight: true + Layout.fillWidth: true + } + } + } +} diff --git a/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderItem.png.sha512 b/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderItem.png.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..9883fcc2e9aefa4c48113d1732dcdb4754147548 --- /dev/null +++ b/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderItem.png.sha512 @@ -0,0 +1 @@ +9c2b68d8a43af9f3d5b0fd6ac9f32c60491fb94d9dab595d2d6e670b6627a38a2e9614e4a484d75bd33d623fc7835d023aeafd236a54964069f5c445e8161855 diff --git a/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderItemWidget.png.sha512 b/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderItemWidget.png.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..a8f108dbbe544c16a37cc53a9dee1c655716b9ba --- /dev/null +++ b/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderItemWidget.png.sha512 @@ -0,0 +1 @@ +cc5a009273ed6a1fa6f3485a7d6b80bcda445eff1883cdb7f7dd72696308c81f88a778a8cf0494030af5e231f9ed13d1b6e852a4ba6365d90abdab65075250a0 diff --git a/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderWindow.png.sha512 b/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderWindow.png.sha512 new file mode 100644 index 0000000000000000000000000000000000000000..91e412f287041064b1e6c85f2e261a0500f48d58 --- /dev/null +++ b/GUISupport/QtQuick/Testing/Data/Baseline/TestQQuickVTKRenderWindow.png.sha512 @@ -0,0 +1 @@ +232d2ec5e01d5704c630209e575227ba44b1db09c84643a75b6ab975101975a4794252d3b4a8a1d50f7d1a5872090cc88dde3a857c5284d9901bf71718133116 diff --git a/GUISupport/QtQuick/VTK.qmltypes b/GUISupport/QtQuick/VTK.qmltypes new file mode 100644 index 0000000000000000000000000000000000000000..80fca3bba0a63b32e31b4fced94608ffbe9fcd93 --- /dev/null +++ b/GUISupport/QtQuick/VTK.qmltypes @@ -0,0 +1,49 @@ +import QtQuick.tooling 1.2 + +// This file describes the plugin-supplied types contained in the library. +// It is used for QML tooling purposes only. +// +// This file was auto-generated by: +// 'qmlplugindump VTK 9.0 /home/sankhesh/Projects/vtk/bld-release/Release/' + +Module { + dependencies: ["QtQuick 2.0"] + Component { + name: "QQuickVTKInteractiveWidget" + prototype: "QObject" + exports: ["VTKWidget 9.0"] + exportMetaObjectRevisions: [0] + Property { name: "enabled"; type: "bool" } + Signal { + name: "enabledChanged" + Parameter { name: "e"; type: "bool" } + } + Method { + name: "sync" + Parameter { name: "ren"; type: "vtkRenderer"; isPointer: true } + } + } + Component { + name: "QQuickVTKRenderItem" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["VTKRenderItem 9.0"] + exportMetaObjectRevisions: [0] + Property { name: "renderWindow"; type: "QQuickVTKRenderWindow"; isPointer: true } + Method { name: "sync" } + Method { name: "paint" } + Method { name: "cleanup" } + } + Component { + name: "QQuickVTKRenderWindow" + defaultProperty: "data" + prototype: "QQuickItem" + exports: ["VTKRenderWindow 9.0"] + exportMetaObjectRevisions: [0] + Method { name: "sync" } + Method { name: "paint" } + Method { name: "cleanup" } + Method { name: "renderNow" } + Method { name: "render" } + } +} diff --git a/GUISupport/QtQuick/qmldir.in b/GUISupport/QtQuick/qmldir.in new file mode 100644 index 0000000000000000000000000000000000000000..dd5e9cec5800c9004f7a587ce7c9bed5543cf7b4 --- /dev/null +++ b/GUISupport/QtQuick/qmldir.in @@ -0,0 +1,4 @@ +module @_vtk_build_PACKAGE@ +plugin @VTK_MODULE_PLUGIN_NAME@ @VTK_MODULE_PLUGIN_DIR@ +classname QQmlVTKPlugin +typeinfo @_vtk_build_PACKAGE@.qmltypes diff --git a/GUISupport/QtQuick/vtk.module b/GUISupport/QtQuick/vtk.module new file mode 100644 index 0000000000000000000000000000000000000000..f0078ba74a72fb51a5a91b74aac7ba009ff87840 --- /dev/null +++ b/GUISupport/QtQuick/vtk.module @@ -0,0 +1,25 @@ +NAME + VTK::GUISupportQtQuick +LIBRARY_NAME + vtkGUISupportQtQuick +GROUPS + Qt +DEPENDS + VTK::CommonCore + VTK::GUISupportQt + VTK::InteractionWidgets + VTK::RenderingCore + VTK::RenderingOpenGL2 +PRIVATE_DEPENDS + VTK::CommonDataModel + VTK::FiltersExtraction + VTK::InteractionStyle + VTK::opengl +TEST_DEPENDS + VTK::FiltersSources + VTK::IOImage + VTK::IOXML + VTK::RenderingVolumeOpenGL2 + VTK::TestingCore + VTK::TestingRendering +EXCLUDE_WRAP