├── .gitignore ├── images ├── occt-qopenglwidget-sample-wnt.png └── occt-qopenglwidget-sample-x11.png ├── occt-qtquick ├── occt-qtquick.qrc ├── main.cpp ├── main6.qml ├── main5.qml ├── OcctQQuickFramebufferViewer.h ├── CMakeLists.txt └── OcctQQuickFramebufferViewer.cpp ├── CMakeLists.txt ├── occt-qt-tools ├── CMakeLists.txt ├── OcctGlTools.h ├── OcctQtTools.h ├── OcctGlTools.cpp └── OcctQtTools.cpp ├── .github └── workflows │ ├── screenshot.sh │ ├── build_linux_gcc_qt5.yml │ └── build_linux_gcc_qt6.yml ├── occt-qopenglwidget ├── custom.pri.template ├── OcctQMainWindowSample.h ├── main.cpp ├── occt-qopenglwidget-sample.pro ├── OcctQOpenGLWidgetViewer.h ├── CMakeLists.txt ├── OcctQMainWindowSample.cpp └── OcctQOpenGLWidgetViewer.cpp ├── .gitattributes ├── occt-qwidget ├── OcctQMainWindowSample.h ├── main.cpp ├── OcctQWidgetViewer.h ├── CMakeLists.txt ├── OcctQMainWindowSample.cpp └── OcctQWidgetViewer.cpp ├── .clang-format ├── LICENSE.txt ├── ReadMe.md └── adm └── cmake └── FindOpenCASCADE.cmake /.gitignore: -------------------------------------------------------------------------------- 1 | custom.pri 2 | *.pro.user 3 | CMakeLists.txt.user 4 | -------------------------------------------------------------------------------- /images/occt-qopenglwidget-sample-wnt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkv311/occt-samples-qt/HEAD/images/occt-qopenglwidget-sample-wnt.png -------------------------------------------------------------------------------- /images/occt-qopenglwidget-sample-x11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gkv311/occt-samples-qt/HEAD/images/occt-qopenglwidget-sample-x11.png -------------------------------------------------------------------------------- /occt-qtquick/occt-qtquick.qrc: -------------------------------------------------------------------------------- 1 | 2 | 3 | main5.qml 4 | main6.qml 5 | 6 | 7 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.13) 2 | 3 | # common solution for multiple independent projects 4 | project (occt-qt) 5 | set_property (GLOBAL PROPERTY USE_FOLDERS ON) 6 | add_subdirectory (occt-qt-tools) 7 | add_subdirectory (occt-qwidget) 8 | add_subdirectory (occt-qopenglwidget) 9 | add_subdirectory (occt-qtquick) 10 | -------------------------------------------------------------------------------- /occt-qt-tools/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.13) 2 | 3 | # dummy project - just to collect files in folder 4 | project (occt-qt-tools) 5 | add_custom_target (${PROJECT_NAME} SOURCES 6 | OcctQtTools.h 7 | OcctQtTools.cpp 8 | OcctGlTools.h 9 | OcctGlTools.cpp 10 | ../ReadMe.md 11 | ) 12 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Tools") 13 | -------------------------------------------------------------------------------- /.github/workflows/screenshot.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | anApp=$1 4 | anImg=$2 5 | aDelay=$3 6 | 7 | "$anApp" & 8 | anAppPid=$! 9 | 10 | rm -f "$anImg" 11 | scrot -d $aDelay -F "$anImg" 12 | 13 | anImgSize=$(wc -c <"$anImg") 14 | if [ $anImgSize -lt 2000 ]; then 15 | # retry one more time 16 | mv -f "$anImg" "back-first-${anImg}" 17 | scrot -d 1 -F "$anImg" 18 | fi 19 | 20 | kill $anAppPid 21 | -------------------------------------------------------------------------------- /occt-qopenglwidget/custom.pri.template: -------------------------------------------------------------------------------- 1 | CASROOT=c:/occt760-vc14-64/opencascade-7.6.0 2 | 3 | CASROOT_INCLUDES=$${CASROOT}/inc 4 | MY_OCCTINCDIR = $${CASROOT}/inc 5 | MY_OCCTLIBDIR = $${CASROOT}/$${MY_PLATFORM_AND_COMPILER}/lib$${MY_BUILDTYPE} 6 | MY_OCCTBINDIR = $${CASROOT}/$${MY_PLATFORM_AND_COMPILER}/bin$${MY_BUILDTYPE} 7 | 8 | # direct building dependencies 9 | INCLUDEPATH += $${MY_OCCTINCDIR} 10 | LIBS += -L$${MY_OCCTLIBDIR} 11 | 12 | # include DLLs into PATH, including third-party libraries like FreeImage 13 | win32 { 14 | LIBS += -L$${MY_OCCTBINDIR} 15 | 16 | MY_THIRDROOT = "$${CASROOT}/.." 17 | LIBS += -L"$${MY_THIRDROOT}/freeimage-3.17.0-vc14-64/bin" 18 | LIBS += -L"$${MY_THIRDROOT}/freetype-2.5.5-vc14-64/bin" 19 | LIBS += -L"$${MY_THIRDROOT}/ffmpeg-3.3.4-64/bin" 20 | LIBS += -L"$${MY_THIRDROOT}/tbb_2017.0.100/bin/intel64/vc14" 21 | } 22 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.yml eol=lf 2 | *.qml eol=lf 3 | *.htm eol=lf 4 | *.html eol=lf 5 | *.js eol=lf 6 | *.css eol=lf 7 | *.txt eol=text 8 | *.tcl eol=lf 9 | *.cpp eol=lf 10 | *.cxx eol=lf 11 | *.gxx eol=lf 12 | *.hxx eol=lf 13 | *.lxx eol=lf 14 | *.pxx eol=lf 15 | *.igs eol=lf 16 | *.iges eol=lf 17 | *.stp eol=lf 18 | *.step eol=lf 19 | *.brep eol=lf 20 | *.rle eol=lf 21 | *.vrml eol=lf 22 | *.md eol=lf 23 | .gitignore eol=lf 24 | .gitattributes eol=lf 25 | *.wasm binary 26 | *.stl binary 27 | *.7z binary 28 | *.pdf binary 29 | *.png binary 30 | *.jpg binary 31 | *.bmp binary 32 | *.gif binary 33 | *.xwd binary 34 | *.ico binary 35 | -------------------------------------------------------------------------------- /occt-qopenglwidget/OcctQMainWindowSample.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #ifndef _OcctQMainWindowSample_HeaderFile 4 | #define _OcctQMainWindowSample_HeaderFile 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class OcctQOpenGLWidgetViewer; 11 | 12 | //! Main application window. 13 | class OcctQMainWindowSample : public QMainWindow 14 | { 15 | public: 16 | //! Window constructor. 17 | OcctQMainWindowSample(); 18 | 19 | private: 20 | //! Define menu bar with Quit item. 21 | void createMenuBar(); 22 | 23 | //! Define controls over 3D viewer. 24 | void createLayoutOverViewer(); 25 | 26 | //! Advanced method splitting 3D Viewer into sub-views. 27 | void splitSubviews(); 28 | 29 | private: 30 | OcctQOpenGLWidgetViewer* myViewer = nullptr; 31 | }; 32 | 33 | #endif // _OcctQMainWindowSample_HeaderFile 34 | -------------------------------------------------------------------------------- /occt-qwidget/OcctQMainWindowSample.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #ifndef _OcctQMainWindowSample_HeaderFile 4 | #define _OcctQMainWindowSample_HeaderFile 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | class OcctQWidgetViewer; 11 | 12 | //! Main window for sample application holding OCCT 3D Viewer. 13 | class OcctQMainWindowSample : public QMainWindow 14 | { 15 | public: 16 | //! Window constructor. 17 | OcctQMainWindowSample(); 18 | 19 | private: 20 | //! Define menu bar with Quit item. 21 | void createMenuBar(); 22 | 23 | //! Define controls over 3D viewer. 24 | void createLayoutOverViewer(); 25 | 26 | //! Advanced method splitting 3D Viewer into sub-views. 27 | void splitSubviews(); 28 | 29 | private: 30 | OcctQWidgetViewer* myViewer = nullptr; 31 | }; 32 | 33 | #endif // _OcctQMainWindowSample_HeaderFile 34 | -------------------------------------------------------------------------------- /occt-qwidget/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #include "OcctQMainWindowSample.h" 4 | 5 | #include "../occt-qt-tools/OcctQtTools.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | int main(int theNbArgs, char** theArgVec) 14 | { 15 | // before creating QApplication: define platform plugin to load (e.g. xcb on Linux) 16 | // and graphic driver (e.g. desktop OpenGL with desired profile/surface) 17 | OcctQtTools::qtGlPlatformSetup(); 18 | QApplication aQApp(theNbArgs, theArgVec); 19 | 20 | QCoreApplication::setApplicationName("OCCT Qt/QWidget Viewer sample"); 21 | QCoreApplication::setOrganizationName("OpenCASCADE"); 22 | QCoreApplication::setApplicationVersion(OCC_VERSION_STRING_EXT); 23 | 24 | OcctQMainWindowSample aMainWindow; 25 | aMainWindow.resize(aMainWindow.sizeHint()); 26 | aMainWindow.show(); 27 | return aQApp.exec(); 28 | } 29 | -------------------------------------------------------------------------------- /occt-qopenglwidget/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #include "OcctQMainWindowSample.h" 4 | 5 | #include "../occt-qt-tools/OcctQtTools.h" 6 | 7 | #include 8 | #include 9 | #include 10 | 11 | #include 12 | 13 | int main(int theNbArgs, char** theArgVec) 14 | { 15 | // before creating QApplication: define platform plugin to load (e.g. xcb on Linux) 16 | // and graphic driver (e.g. desktop OpenGL with desired profile/surface) 17 | OcctQtTools::qtGlPlatformSetup(); 18 | QApplication aQApp(theNbArgs, theArgVec); 19 | 20 | QCoreApplication::setApplicationName("OCCT Qt/QOpenGLWidget Viewer sample"); 21 | QCoreApplication::setOrganizationName("OpenCASCADE"); 22 | QCoreApplication::setApplicationVersion(OCC_VERSION_STRING_EXT); 23 | 24 | OcctQMainWindowSample aMainWindow; 25 | aMainWindow.resize(aMainWindow.sizeHint()); 26 | aMainWindow.show(); 27 | return aQApp.exec(); 28 | } 29 | -------------------------------------------------------------------------------- /.clang-format: -------------------------------------------------------------------------------- 1 | # clang-format configuration file trying to apply OCCT coding style 2 | BasedOnStyle: Microsoft 3 | # 4 | # Style options 5 | AllowAllParametersOfDeclarationOnNextLine: false 6 | AllowAllArgumentsOnNextLine: false 7 | AlignAfterOpenBracket: Align 8 | AlignConsecutiveAssignments: Consecutive 9 | AlignConsecutiveDeclarations: Consecutive 10 | AlignTrailingComments: true 11 | AllowShortFunctionsOnASingleLine: Inline 12 | AlwaysBreakTemplateDeclarations: Yes 13 | BinPackArguments: false 14 | BinPackParameters: false 15 | BreakBeforeBinaryOperators: NonAssignment 16 | BreakBeforeTernaryOperators: true 17 | ColumnLimit: 120 18 | ContinuationIndentWidth: 2 19 | #ConstructorInitializerIndentWidth: 0 20 | IndentCaseLabels: true 21 | IndentPPDirectives: BeforeHash 22 | IndentWidth: 2 23 | IndentWrappedFunctionNames: true 24 | PackConstructorInitializers: Never 25 | PointerAlignment: Left 26 | ReferenceAlignment: Left 27 | SeparateDefinitionBlocks: Always 28 | SortIncludes: false 29 | UseTab: Never 30 | # 31 | # OCCT specific settings 32 | StatementMacros: 33 | - Standard_FALLTHROUGH 34 | - Standard_DEPRECATED 35 | TypenameMacros: 36 | - Handle 37 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2023 Kirill Gavrilov 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining a copy 4 | of this software and associated documentation files (the "Software"), to deal 5 | in the Software without restriction, including without limitation the rights 6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 7 | copies of the Software, and to permit persons to whom the Software is 8 | furnished to do so, subject to the following conditions: 9 | 10 | The above copyright notice and this permission notice shall be included in all 11 | copies or substantial portions of the Software. 12 | 13 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 14 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 15 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 16 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 17 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 18 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 19 | SOFTWARE. 20 | -------------------------------------------------------------------------------- /occt-qopenglwidget/occt-qopenglwidget-sample.pro: -------------------------------------------------------------------------------- 1 | TEMPLATE = app 2 | QT += widgets 3 | greaterThan(QT_MAJOR_VERSION, 5) { QT += openglwidgets } 4 | CONFIG += console 5 | 6 | # source code of the sample 7 | HEADERS = OcctQMainWindowSample.h \ 8 | OcctQOpenGLWidgetViewer.h \ 9 | ../occt-qt-tools/OcctQtTools.h 10 | ../occt-qt-tools/OcctGlTools.h 11 | SOURCES = main.cpp \ 12 | OcctQMainWindowSample.cpp \ 13 | OcctQOpenGLWidgetViewer.cpp \ 14 | ../occt-qt-tools/OcctQtTools.cpp \ 15 | ../occt-qt-tools/OcctGlTools.cpp 16 | OTHER_FILES = ../LICENSE.md\ 17 | ../ReadMe.md \ 18 | custom.pri.template 19 | 20 | # target configuration 21 | MY_TARGET_ABI = $${QMAKE_TARGET.arch} 22 | mac { MY_TARGET_ABI = $${QMAKE_APPLE_DEVICE_ARCHS} } 23 | 24 | MY_PLATFORM = platform 25 | win32 { MY_PLATFORM = win64 26 | } else:mac { MY_PLATFORM = mac 27 | } else:linux { MY_PLATFORM = lin 28 | } else:unix { MY_PLATFORM = unix 29 | } else { warning (Unknown platform. "$$MY_PLATFORM" is used) } 30 | 31 | MY_COMPILER = compiler 32 | clang { MY_COMPILER = clang 33 | } else:gcc { MY_COMPILER = gcc 34 | } else:win32-msvc2015 { MY_COMPILER = vc14 35 | } else:win32-msvc2017 { MY_COMPILER = vc14 36 | } else:win32-msvc { MY_COMPILER = vc14 37 | } else { warning (Unknown compiler. "$$MY_COMPILER" is used) } 38 | MY_BUILDTYPE = 39 | CONFIG(debug, debug|release) { MY_BUILDTYPE = d } 40 | MY_PLATFORM_AND_COMPILER = $$MY_PLATFORM/$$MY_COMPILER 41 | 42 | # opengl32 and user32 libs required respectively for wglGetCurrentDC() and WindowFromDC() 43 | win32:LIBS += -lopengl32 -luser32 44 | 45 | # paths to OCCT should be set by custom.pri, see custom.pri.template as example 46 | exists($$PWD/custom.pri) { include($$PWD/custom.pri) } 47 | 48 | # OCCT libraries to link 49 | LIBS += -lTKernel -lTKGeomBase -lTKGeomAlgo -lTKG2d -lTKV3d -lTKG3d -lTKHLR -lTKService -lTKMath -lTKBRep -lTKTopAlgo -lTKOpenGl -lTKPrim -lTKShHealing -lTKMesh 50 | -------------------------------------------------------------------------------- /.github/workflows/build_linux_gcc_qt5.yml: -------------------------------------------------------------------------------- 1 | name: Build (Linux/GCC/Qt5) 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | Ubuntu: 11 | runs-on: ubuntu-24.04 12 | steps: 13 | - name: Clone Tree 14 | uses: actions/checkout@v1 15 | with: 16 | fetch-depth: 1 17 | - name: Install Dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y \ 21 | g++ cmake ninja-build \ 22 | libocct-draw-dev libocct-visualization-dev libocct-ocaf-dev libocct-modeling-data-dev \ 23 | libocct-modeling-algorithms-dev libocct-foundation-dev libocct-data-exchange-dev \ 24 | libgl-dev libegl-dev \ 25 | tcllib tklib tcl-dev tk-dev \ 26 | libtbb-dev \ 27 | qtbase5-dev qtdeclarative5-dev qtquickcontrols2-5-dev \ 28 | qml-module-qtquick-controls2 qml-module-qtquick-templates2 qml-module-qtquick-dialogs \ 29 | xvfb scrot 30 | - name: Configure project 31 | run: | 32 | mkdir "build" 33 | cmake -G Ninja -S . -B "./build" -D QT_VERSION=Qt5 34 | - name: Build project 35 | run: | 36 | cmake --build "./build" --config Release 37 | - name: Run tests 38 | run: | 39 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qwidget/occt-qwidget-sample ./build/occt-qwidget.png 5 40 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qopenglwidget/occt-qopenglwidget-sample ./build/occt-qopenglwidget.png 5 41 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qtquick/occt-qtquick-sample ./build/occt-qtquick.png 5 42 | - name: Upload artifacts 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: occt-qt5 46 | path: ./build/*.png 47 | -------------------------------------------------------------------------------- /.github/workflows/build_linux_gcc_qt6.yml: -------------------------------------------------------------------------------- 1 | name: Build (Linux/GCC/Qt6) 2 | on: 3 | push: 4 | branches: [ master ] 5 | pull_request: 6 | branches: [ master ] 7 | workflow_dispatch: 8 | 9 | jobs: 10 | Ubuntu: 11 | runs-on: ubuntu-24.04 12 | steps: 13 | - name: Clone Tree 14 | uses: actions/checkout@v1 15 | with: 16 | fetch-depth: 1 17 | - name: Install Dependencies 18 | run: | 19 | sudo apt-get update 20 | sudo apt-get install -y \ 21 | g++ cmake ninja-build \ 22 | libocct-draw-dev libocct-visualization-dev libocct-ocaf-dev libocct-modeling-data-dev \ 23 | libocct-modeling-algorithms-dev libocct-foundation-dev libocct-data-exchange-dev \ 24 | libgl-dev libegl-dev \ 25 | tcllib tklib tcl-dev tk-dev \ 26 | libtbb-dev \ 27 | qt6-base-dev qt6-declarative-dev \ 28 | qml6-module-qtquick-window qml6-module-qtquick-controls qml6-module-qtquick-templates qml6-module-qtquick-dialogs qml6-module-qtqml-workerscript \ 29 | xvfb scrot 30 | - name: Configure project 31 | run: | 32 | mkdir "build" 33 | cmake -G Ninja -S . -B "./build" -D QT_VERSION=Qt6 34 | - name: Build project 35 | run: | 36 | cmake --build "./build" --config Release 37 | - name: Run tests 38 | run: | 39 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qwidget/occt-qwidget-sample ./build/occt-qwidget.png 5 40 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qopenglwidget/occt-qopenglwidget-sample ./build/occt-qopenglwidget.png 5 41 | xvfb-run --server-args="-screen 0 800x600x24" ./.github/workflows/screenshot.sh ./build/occt-qtquick/occt-qtquick-sample ./build/occt-qtquick.png 5 42 | - name: Upload artifacts 43 | uses: actions/upload-artifact@v4 44 | with: 45 | name: occt-qt6 46 | path: ./build/*.png 47 | -------------------------------------------------------------------------------- /occt-qt-tools/OcctGlTools.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Kirill Gavrilov 2 | 3 | #ifndef _OcctGlTools_HeaderFile 4 | #define _OcctGlTools_HeaderFile 5 | 6 | #include 7 | #include 8 | 9 | class OpenGl_Context; 10 | 11 | //! Auxiliary wrapper to avoid OpenGL macros collisions between Qt and OCCT headers. 12 | class OcctGlTools 13 | { 14 | public: 15 | //! Class making DevicePixelRatio() configurable. 16 | class OcctNeutralWindow : public Aspect_NeutralWindow 17 | { 18 | public: 19 | //! Empty constructor. 20 | OcctNeutralWindow() {} 21 | 22 | //! Return device pixel ratio (logical to backing store scale factor). 23 | virtual double DevicePixelRatio() const override { return myPixelRatio; } 24 | 25 | //! Set device pixel ratio. 26 | void SetDevicePixelRatio(double theRatio) { myPixelRatio = theRatio; } 27 | private: 28 | double myPixelRatio = 1.0; 29 | }; 30 | public: 31 | //! Return GL context. 32 | static Handle(OpenGl_Context) GetGlContext(const Handle(V3d_View)& theView); 33 | 34 | //! Return active native window bound to OpenGL context. 35 | static Aspect_Drawable GetGlNativeWindow(Aspect_Drawable theNativeWin); 36 | 37 | //! Initialize native window for OCCT 3D Viewer. 38 | static bool InitializeGlWindow(const Handle(V3d_View)& theView, 39 | const Aspect_Drawable theNativeWin, 40 | const Graphic3d_Vec2i& theSize, 41 | const double thePixelRatio); 42 | 43 | //! Wrap FBO created by QOpenGLFramebufferObject to OCCT 3D Viewer target. 44 | static bool InitializeGlFbo(const Handle(V3d_View)& theView); 45 | 46 | //! Cleanup up global GL state after Qt before redrawing OCCT Viewer. 47 | static void ResetGlStateBeforeOcct(const Handle(V3d_View)& theView); 48 | 49 | //! Cleanup up global GL state after OCCT before redrawing Qt. 50 | //! Alternative to QQuickOpenGLUtils::resetOpenGLState(). 51 | static void ResetGlStateAfterOcct(const Handle(V3d_View)& theView); 52 | }; 53 | 54 | #endif // _OcctGlTools_HeaderFile 55 | -------------------------------------------------------------------------------- /occt-qtquick/main.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #include "OcctQQuickFramebufferViewer.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | 15 | int main(int theNbArgs, char** theArgVec) 16 | { 17 | //Message::DefaultMessenger()->Printers().First()->SetTraceLevel(Message_Trace); 18 | 19 | // before creating QApplication: define platform plugin to load (e.g. xcb on Linux) 20 | // and graphic driver (e.g. desktop OpenGL with desired profile/surface) 21 | OcctQtTools::qtGlPlatformSetup(); 22 | 23 | // Qt by default will attempt offloading rendering 24 | // into a separate working thread (QSGRenderThread) on some systems, 25 | // which requires addition of multi-threading synchronization mechanism 26 | // when dealing with OCCT 3D Viewer from GUI thread. 27 | // Uncomment following lines if these complexities are undesired 28 | // to ask Qt managing rendering from GUI thread. 29 | /*OSD_Environment aQsgLoop("QSG_RENDER_LOOP"); 30 | if (aQsgLoop.Value().IsEmpty()) 31 | { 32 | aQsgLoop.SetValue("basic"); 33 | aQsgLoop.Build(); 34 | }*/ 35 | 36 | QApplication aQApp(theNbArgs, theArgVec); 37 | 38 | QCoreApplication::setApplicationName("OCCT Qt/QtQuick Viewer sample"); 39 | QCoreApplication::setOrganizationName("OpenCASCADE"); 40 | QCoreApplication::setApplicationVersion(OCC_VERSION_STRING_EXT); 41 | 42 | qmlRegisterType("OcctQQuickFramebufferViewer", 1, 0, "OcctQQuickFramebufferViewer"); 43 | 44 | QQmlApplicationEngine aQmlEngine; 45 | aQmlEngine.rootContext()->setContextProperty("QT_VERSION_STR", QString(QT_VERSION_STR)); 46 | aQmlEngine.rootContext()->setContextProperty("OCC_VERSION_STRING_EXT", QString(OCC_VERSION_STRING_EXT)); 47 | #if (QT_VERSION_MAJOR >= 6) 48 | aQmlEngine.load(QUrl(QStringLiteral("qrc:/main6.qml"))); 49 | #else 50 | aQmlEngine.load(QUrl(QStringLiteral("qrc:/main5.qml"))); 51 | #endif 52 | return aQApp.exec(); 53 | } 54 | -------------------------------------------------------------------------------- /occt-qtquick/main6.qml: -------------------------------------------------------------------------------- 1 | // sample for Qt6 2 | import QtQuick 3 | import QtQuick.Controls 4 | import QtQuick.Dialogs 5 | 6 | import OcctQQuickFramebufferViewer 7 | 8 | // Sample application window 9 | ApplicationWindow { 10 | id: wnd_root 11 | title: Qt.application.name 12 | visible: true 13 | 14 | minimumWidth: 200 15 | minimumHeight: 200 16 | width: 720 17 | height: 480 18 | 19 | // OCCT 3D Viewer item 20 | OcctQQuickFramebufferViewer { 21 | id: occt_view 22 | anchors.fill: parent 23 | focus: true // to accept keyboard events 24 | } 25 | 26 | // Main menu bar 27 | MenuBar { 28 | Menu { 29 | title: qsTr("&File") 30 | MenuItem { 31 | text: qsTr("&Quit") 32 | onTriggered: Qt.quit(); 33 | } 34 | } 35 | } 36 | 37 | // Viewer background color slider 38 | Slider { 39 | anchors.bottom: parent.bottom 40 | anchors.bottomMargin: 10 41 | anchors.left: parent.left 42 | anchors.leftMargin: 20 43 | anchors.right: btn_about.left 44 | anchors.rightMargin: 20 45 | from: 0 46 | value: 0 47 | to: 255 48 | onMoved: occt_view.backgroundColor = Qt.rgba(value/255.0, value/255.0, value/255.0); 49 | } 50 | 51 | // About button 52 | Rectangle { 53 | id: btn_about 54 | anchors.bottom: parent.bottom 55 | anchors.bottomMargin: 5 56 | anchors.right: parent.right 57 | anchors.rightMargin: 5 58 | radius: 10 59 | width: 100 60 | height: 50 61 | property var mainColor: "gray" 62 | color: mainColor 63 | opacity: 0.5 64 | border.color: "black" 65 | Text { 66 | text: qsTr("About") 67 | anchors.centerIn: parent 68 | } 69 | 70 | MouseArea { 71 | anchors.fill: parent 72 | onClicked: dlg_about.open() 73 | hoverEnabled: true 74 | onEntered: btn_about.color = "cyan" 75 | onExited: btn_about.color = btn_about.mainColor 76 | } 77 | } 78 | 79 | // About sample dialog 80 | MessageDialog { 81 | id: dlg_about 82 | title: qsTr("About Sample") 83 | text: qsTr("OCCT 3D Viewer sample embedded into QtQuick/QML.") 84 | informativeText: "Open CASCADE Technology v." + OCC_VERSION_STRING_EXT + "\n" 85 | + "Qt v." + QT_VERSION_STR + "\n" 86 | + "\nOpenGL info:\n" + occt_view.glInfo; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /occt-qtquick/main5.qml: -------------------------------------------------------------------------------- 1 | // sample for Qt5 2 | import QtQuick 2.7 3 | import QtQuick.Controls 2.2 4 | import QtQuick.Dialogs 1.2 5 | 6 | import OcctQQuickFramebufferViewer 1.0 7 | 8 | // Sample application window 9 | ApplicationWindow { 10 | id: wnd_root 11 | title: Qt.application.name 12 | visible: true 13 | 14 | minimumWidth: 200 15 | minimumHeight: 200 16 | width: 720 17 | height: 480 18 | 19 | // OCCT 3D Viewer item 20 | OcctQQuickFramebufferViewer { 21 | id: occt_view 22 | anchors.fill: parent 23 | focus: true // to accept keyboard events 24 | } 25 | 26 | // Main menu bar (added to Qt 5.10, QtQuick.Controls 2.3) 27 | /*MenuBar { 28 | Menu { 29 | title: qsTr("&File") 30 | MenuItem { 31 | text: qsTr("&Quit") 32 | onTriggered: Qt.quit(); 33 | } 34 | } 35 | }*/ 36 | 37 | // Viewer background color slider 38 | Slider { 39 | anchors.bottom: parent.bottom 40 | anchors.bottomMargin: 10 41 | anchors.left: parent.left 42 | anchors.leftMargin: 20 43 | anchors.right: btn_about.left 44 | anchors.rightMargin: 20 45 | from: 0 46 | value: 0 47 | to: 255 48 | onMoved: occt_view.backgroundColor = Qt.rgba(value/255.0, value/255.0, value/255.0); 49 | } 50 | 51 | // About button 52 | Rectangle { 53 | id: btn_about 54 | anchors.bottom: parent.bottom 55 | anchors.bottomMargin: 5 56 | anchors.right: parent.right 57 | anchors.rightMargin: 5 58 | radius: 10 59 | width: 100 60 | height: 50 61 | property var mainColor: "gray" 62 | color: mainColor 63 | opacity: 0.5 64 | border.color: "black" 65 | Text { 66 | text: qsTr("About") 67 | anchors.centerIn: parent 68 | } 69 | 70 | MouseArea { 71 | anchors.fill: parent 72 | onClicked: dlg_about.open() 73 | hoverEnabled: true 74 | onEntered: btn_about.color = "cyan" 75 | onExited: btn_about.color = btn_about.mainColor 76 | } 77 | } 78 | 79 | // About sample dialog 80 | MessageDialog { 81 | id: dlg_about 82 | title: qsTr("About Sample") 83 | text: qsTr("OCCT 3D Viewer sample embedded into QtQuick/QML.") 84 | informativeText: "Open CASCADE Technology v." + OCC_VERSION_STRING_EXT + "\n" 85 | + "Qt v." + QT_VERSION_STR + "\n" 86 | + "\nOpenGL info:\n" + occt_view.glInfo; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /occt-qopenglwidget/OcctQOpenGLWidgetViewer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Kirill Gavrilov 2 | 3 | #ifndef _OcctQOpenGLWidgetViewer_HeaderFile 4 | #define _OcctQOpenGLWidgetViewer_HeaderFile 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class AIS_ViewCube; 16 | 17 | //! OpenGL Qt widget holding OCCT 3D View. 18 | //! 19 | //! OCCT 3D Viewer will reuse OpenGL context created by QOpenGLWidget; 20 | //! widgets on top will be blended by Qt naturally. 21 | //! 22 | //! Inheritance from AIS_ViewController is used to translate 23 | //! user input events (mouse, keyboard, window resize, etc.) 24 | //! to 3D Viewer (panning, rotation, zooming, etc.). 25 | class OcctQOpenGLWidgetViewer : public QOpenGLWidget, public AIS_ViewController 26 | { 27 | Q_OBJECT 28 | public: 29 | //! Main constructor. 30 | OcctQOpenGLWidgetViewer(QWidget* theParent = nullptr); 31 | 32 | //! Destructor. 33 | virtual ~OcctQOpenGLWidgetViewer(); 34 | 35 | //! Return Viewer. 36 | const Handle(V3d_Viewer)& Viewer() const { return myViewer; } 37 | 38 | //! Return View. 39 | const Handle(V3d_View)& View() const { return myView; } 40 | 41 | //! Return AIS context. 42 | const Handle(AIS_InteractiveContext)& Context() const { return myContext; } 43 | 44 | //! Return OpenGL info. 45 | const QString& getGlInfo() const { return myGlInfo; } 46 | 47 | //! Minimal widget size. 48 | virtual QSize minimumSizeHint() const override { return QSize(200, 200); } 49 | 50 | //! Default widget size. 51 | virtual QSize sizeHint() const override { return QSize(720, 480); } 52 | 53 | public: 54 | #if (OCC_VERSION_HEX >= 0x070700) 55 | //! Handle subview focus change. 56 | virtual void OnSubviewChanged(const Handle(AIS_InteractiveContext)&, 57 | const Handle(V3d_View)&, 58 | const Handle(V3d_View)& theNewView) override; 59 | #endif 60 | protected: // OpenGL events 61 | virtual void initializeGL() override; 62 | virtual void paintGL() override; 63 | // virtual void resizeGL(int , int ) override; 64 | 65 | protected: // user input events 66 | virtual bool event(QEvent* theEvent) override; 67 | virtual void closeEvent(QCloseEvent* theEvent) override; 68 | virtual void keyPressEvent(QKeyEvent* theEvent) override; 69 | virtual void mousePressEvent(QMouseEvent* theEvent) override; 70 | virtual void mouseReleaseEvent(QMouseEvent* theEvent) override; 71 | virtual void mouseMoveEvent(QMouseEvent* theEvent) override; 72 | virtual void wheelEvent(QWheelEvent* theEvent) override; 73 | 74 | private: 75 | //! Dump OpenGL info. 76 | void dumpGlInfo(bool theIsBasic, bool theToPrint); 77 | 78 | //! Request widget paintGL() event. 79 | void updateView(); 80 | 81 | //! Handle view redraw. 82 | virtual void handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView) override; 83 | 84 | private: 85 | Handle(V3d_Viewer) myViewer; 86 | Handle(V3d_View) myView; 87 | Handle(AIS_InteractiveContext) myContext; 88 | Handle(AIS_ViewCube) myViewCube; 89 | 90 | Handle(V3d_View) myFocusView; 91 | 92 | QString myGlInfo; 93 | bool myHasTouchInput = false; 94 | }; 95 | 96 | #endif // _OcctQOpenGLWidgetViewer_HeaderFile 97 | -------------------------------------------------------------------------------- /occt-qwidget/OcctQWidgetViewer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #ifndef _OcctQWidgetViewer_HeaderFile 4 | #define _OcctQWidgetViewer_HeaderFile 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | class AIS_ViewCube; 16 | 17 | //! OpenGL Qt widget holding OCCT 3D View. 18 | //! 19 | //! OCCT 3D Viewer will create its own OpenGL context for native window of QWidget; 20 | //! widgets on top should have not semitransparent background, otherwise Qt will be unable to blend them correctly. 21 | //! 22 | //! Inheritance from AIS_ViewController is used to translate 23 | //! user input events (mouse, keyboard, window resize, etc.) 24 | //! to 3D Viewer (panning, rotation, zooming, etc.). 25 | class OcctQWidgetViewer : public QWidget, public AIS_ViewController 26 | { 27 | Q_OBJECT 28 | public: 29 | //! Main constructor. 30 | OcctQWidgetViewer(QWidget* theParent = nullptr); 31 | 32 | //! Destructor. 33 | virtual ~OcctQWidgetViewer(); 34 | 35 | //! Return Viewer. 36 | const Handle(V3d_Viewer)& Viewer() const { return myViewer; } 37 | 38 | //! Return View. 39 | const Handle(V3d_View)& View() const { return myView; } 40 | 41 | //! Return AIS context. 42 | const Handle(AIS_InteractiveContext)& Context() const { return myContext; } 43 | 44 | //! Return OpenGL info. 45 | const QString& getGlInfo() const { return myGlInfo; } 46 | 47 | //! Minimal widget size. 48 | virtual QSize minimumSizeHint() const override { return QSize(200, 200); } 49 | 50 | //! Default widget size. 51 | virtual QSize sizeHint() const override { return QSize(720, 480); } 52 | 53 | public: 54 | #if (OCC_VERSION_HEX >= 0x070700) 55 | //! Handle subview focus change. 56 | virtual void OnSubviewChanged(const Handle(AIS_InteractiveContext)&, 57 | const Handle(V3d_View)&, 58 | const Handle(V3d_View)& theNewView) override; 59 | #endif 60 | protected: // drawing events 61 | //! Initial OpenGL setup. 62 | void initializeGL(); 63 | 64 | //! Redraw the widget (3D Viewer). 65 | virtual void paintEvent(QPaintEvent* theEvent) override; 66 | 67 | //! Resize the widget (3D Viewer). 68 | virtual void resizeEvent(QResizeEvent* theEvent) override; 69 | 70 | //! Important - prevent Qt to try drawing in this widget. 71 | virtual QPaintEngine* paintEngine() const override { return nullptr; } 72 | 73 | protected: // user input events 74 | virtual bool event(QEvent* theEvent) override; 75 | virtual void closeEvent(QCloseEvent* theEvent) override; 76 | virtual void keyPressEvent(QKeyEvent* theEvent) override; 77 | virtual void mousePressEvent(QMouseEvent* theEvent) override; 78 | virtual void mouseReleaseEvent(QMouseEvent* theEvent) override; 79 | virtual void mouseMoveEvent(QMouseEvent* theEvent) override; 80 | virtual void wheelEvent(QWheelEvent* theEvent) override; 81 | 82 | private: 83 | //! Dump OpenGL info. 84 | void dumpGlInfo(bool theIsBasic, bool theToPrint); 85 | 86 | //! Request widget paintGL() event. 87 | void updateView(); 88 | 89 | //! Handle view redraw. 90 | virtual void handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView) override; 91 | 92 | private: 93 | Handle(V3d_Viewer) myViewer; 94 | Handle(V3d_View) myView; 95 | Handle(AIS_InteractiveContext) myContext; 96 | Handle(AIS_ViewCube) myViewCube; 97 | 98 | Handle(V3d_View) myFocusView; 99 | 100 | QString myGlInfo; 101 | bool myIsCoreProfile = true; 102 | bool myHasTouchInput = false; 103 | }; 104 | 105 | #endif // _OcctQWidgetViewer_HeaderFile 106 | -------------------------------------------------------------------------------- /occt-qt-tools/OcctQtTools.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #ifndef _OcctQtTools_HeaderFile 4 | #define _OcctQtTools_HeaderFile 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | 16 | class OpenGl_Caps; 17 | class V3d_View; 18 | 19 | //! Auxiliary tools between Qt and OCCT definitions. 20 | class OcctQtTools 21 | { 22 | public: //! @name Qt application pre-setup for OCCT 3D Viewer integration 23 | 24 | //! Perform global Qt platform setup - to be called before QApplication creation. 25 | //! Defines platform plugin to load (e.g. xcb on Linux) 26 | //! and graphic driver (e.g. desktop OpenGL with desired profile/surface). 27 | static void qtGlPlatformSetup(); 28 | 29 | //! Define default Qt surface format for GL context. 30 | static QSurfaceFormat qtGlSurfaceFormat(QSurfaceFormat::OpenGLContextProfile theProfile = QSurfaceFormat::NoProfile, 31 | bool theToDebug = false); 32 | 33 | //! Fill in OCCT GL caps from Qt surface format. 34 | static void qtGlCapsFromSurfaceFormat(OpenGl_Caps& theCaps, const QSurfaceFormat& theFormat); 35 | 36 | public: //! @name common conversion tools 37 | 38 | //! Map QColor into Quantity_Color. 39 | static Quantity_Color qtColorToOcct(const QColor& theColor); 40 | 41 | //! Map Quantity_Color into QColor. 42 | static QColor qtColorFromOcct(const Quantity_Color& theColor); 43 | 44 | //! Map QColor into Quantity_ColorRGBA. 45 | static Quantity_ColorRGBA qtColorToOcctRgba(const QColor& theColor); 46 | 47 | //! Map Quantity_ColorRGBA into QColor. 48 | static QColor qtColorFromOcctRgba(const Quantity_ColorRGBA& theColor); 49 | 50 | //! Map QString into TCollection_AsciiString (UTF-8). 51 | static TCollection_AsciiString qtStringToOcct(const QString& theText); 52 | 53 | //! Map TCollection_AsciiString (UTF-8) into QString. 54 | static QString qtStringFromOcct(const TCollection_AsciiString& theText); 55 | 56 | //! Map QString into TCollection_ExtendedString (UTF-16). 57 | static TCollection_ExtendedString qtStringToOcctExt(const QString& theText); 58 | 59 | //! Map TCollection_ExtendedString (UTF-16) into QString. 60 | static QString qtStringFromOcctExt(const TCollection_ExtendedString& theText); 61 | 62 | public: //! @name methods for message logs 63 | 64 | //! Map QtMsgType into Message_Gravity. 65 | static Message_Gravity qtMsgTypeToGravity(QtMsgType theType); 66 | 67 | //! Callback for qInstallMessageHandler() redirecting Qt messages to OCCT messenger. 68 | static void qtMessageHandlerToOcct(QtMsgType theType, 69 | const QMessageLogContext& theCtx, 70 | const QString& theMsg); 71 | 72 | public: //! @name methods for wrapping Qt input events into Aspect_WindowInputListener events 73 | 74 | //! Queue Qt mouse hover event to OCCT listener. 75 | static bool qtHandleHoverEvent(Aspect_WindowInputListener& theListener, 76 | const Handle(V3d_View)& theView, 77 | const QHoverEvent* theEvent); 78 | 79 | //! Queue Qt mouse event to OCCT listener. 80 | static bool qtHandleMouseEvent(Aspect_WindowInputListener& theListener, 81 | const Handle(V3d_View)& theView, 82 | const QMouseEvent* theEvent); 83 | 84 | //! Queue Qt mouse wheel event to OCCT listener. 85 | static bool qtHandleWheelEvent(Aspect_WindowInputListener& theListener, 86 | const Handle(V3d_View)& theView, 87 | const QWheelEvent* theEvent); 88 | 89 | //! Queue Qt touch event to OCCT listener. 90 | static bool qtHandleTouchEvent(Aspect_WindowInputListener& theListener, 91 | const Handle(V3d_View)& theView, 92 | const QTouchEvent* theEvent); 93 | 94 | //! Map Qt buttons bitmask to virtual keys. 95 | static Aspect_VKeyMouse qtMouseButtons2VKeys(Qt::MouseButtons theButtons); 96 | 97 | //! Map Qt mouse modifiers bitmask to virtual keys. 98 | static Aspect_VKeyFlags qtMouseModifiers2VKeys(Qt::KeyboardModifiers theModifiers); 99 | 100 | //! Map Qt key to virtual key. 101 | static Aspect_VKey qtKey2VKey(int theKey); 102 | 103 | }; 104 | 105 | #endif // _OcctQtTools_HeaderFile 106 | -------------------------------------------------------------------------------- /occt-qtquick/OcctQQuickFramebufferViewer.h: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #ifndef _OcctQQuickFramebufferViewer_HeaderFile 4 | #define _OcctQQuickFramebufferViewer_HeaderFile 5 | 6 | #include "../occt-qt-tools/OcctQtTools.h" 7 | 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | class AIS_ViewCube; 18 | 19 | //! OpenGL QtQuick framebuffer control holding OCCT 3D View. 20 | //! 21 | //! OCCT 3D Viewer will reuse OpenGL context created by QtQuick; 22 | //! controls on top will be blended by Qt naturally. 23 | //! 24 | //! Inheritance from AIS_ViewController is used to translate 25 | //! user input events (mouse, keyboard, window resize, etc.) 26 | //! to 3D Viewer (panning, rotation, zooming, etc.). 27 | class OcctQQuickFramebufferViewer : public QQuickFramebufferObject, public AIS_ViewController 28 | { 29 | Q_OBJECT 30 | 31 | // QML properties 32 | Q_PROPERTY(QColor backgroundColor READ getBackgroundColor WRITE setBackgroundColor) 33 | Q_PROPERTY(QString glInfo READ getGlInfo NOTIFY glInfoChanged) 34 | public: 35 | //! Main constructor. 36 | OcctQQuickFramebufferViewer(QQuickItem* theParent = nullptr); 37 | 38 | //! Destructor. 39 | virtual ~OcctQQuickFramebufferViewer(); 40 | 41 | //! Return Viewer. 42 | const Handle(V3d_Viewer)& Viewer() const { return myViewer; } 43 | 44 | //! Return View. 45 | const Handle(V3d_View)& View() const { return myView; } 46 | 47 | //! Return AIS context. 48 | const Handle(AIS_InteractiveContext)& Context() const { return myContext; } 49 | 50 | public: // QML accessors 51 | //! Return OpenGL info. 52 | const QString& getGlInfo() const { return myGlInfo; } 53 | 54 | //! Return background color. 55 | QColor getBackgroundColor() { return myGlBackColor.first; } 56 | 57 | //! Set background color. 58 | void setBackgroundColor(const QColor& theColor) 59 | { 60 | myGlBackColor = std::make_pair(true, theColor); 61 | update(); 62 | } 63 | 64 | signals: 65 | void glInfoChanged(); 66 | void glCriticalError(QString theMsg); 67 | 68 | protected: 69 | //! OpenGL renderer interface. 70 | class Renderer : public QQuickFramebufferObject::Renderer 71 | { 72 | public: 73 | Renderer(OcctQQuickFramebufferViewer* theViewer); 74 | virtual ~Renderer(); 75 | private: 76 | virtual QOpenGLFramebufferObject* createFramebufferObject(const QSize& theSize) override; 77 | virtual void synchronize(QQuickFramebufferObject* theItem) override; 78 | virtual void render() override; 79 | 80 | private: 81 | OcctQQuickFramebufferViewer* myViewer = nullptr; 82 | }; 83 | 84 | virtual QQuickFramebufferObject::Renderer* createRenderer() const override; 85 | //virtual void releaseResources() override; 86 | 87 | void initializeGL(QOpenGLFramebufferObject* theFbo); 88 | void synchronize(QOpenGLFramebufferObject* theFbo); 89 | void render(QOpenGLFramebufferObject* theFbo); 90 | 91 | protected: // user input events 92 | virtual bool event(QEvent* theEvent) override; 93 | virtual void keyPressEvent(QKeyEvent* theEvent) override; 94 | virtual void mousePressEvent(QMouseEvent* theEvent) override; 95 | virtual void mouseReleaseEvent(QMouseEvent* theEvent) override; 96 | virtual void mouseMoveEvent(QMouseEvent* theEvent) override; 97 | virtual void wheelEvent(QWheelEvent* theEvent) override; 98 | virtual void hoverEnterEvent(QHoverEvent* theEvent) override; 99 | virtual void hoverMoveEvent(QHoverEvent* theEvent) override; 100 | virtual void hoverLeaveEvent(QHoverEvent* theEvent) override; 101 | //virtual void touchEvent(QTouchEvent* theEvent) override; 102 | 103 | private: 104 | //! Dump OpenGL info. 105 | void dumpGlInfo(bool theIsBasic, bool theToPrint); 106 | 107 | //! Request 3D viewer redrawing from GUI thread. 108 | void updateView(); 109 | 110 | //! Handle view redraw. 111 | virtual void handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView) override; 112 | 113 | private: 114 | Handle(V3d_Viewer) myViewer; 115 | Handle(V3d_View) myView; 116 | Handle(AIS_InteractiveContext) myContext; 117 | Handle(AIS_ViewCube) myViewCube; 118 | 119 | Standard_Mutex myViewerMutex; 120 | 121 | std::pair myGlBackColor = std::make_pair(false, QColor(0, 0, 0)); 122 | 123 | QString myGlInfo; 124 | bool myHasTouchInput = false; 125 | }; 126 | 127 | #endif // _OcctQQuickFramebufferViewer_HeaderFile 128 | -------------------------------------------------------------------------------- /occt-qwidget/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.13) 2 | 3 | project (occt-qwidget-sample) 4 | 5 | set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../adm/cmake" ${CMAKE_MODULE_PATH}) 6 | set (APP_VERSION_MAJOR 1) 7 | set (APP_VERSION_MINOR 0) 8 | 9 | # compiler flags 10 | set (CMAKE_CXX_STANDARD 11) 11 | if (MSVC) 12 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise /EHa /MP") 13 | string (REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 14 | add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DUNICODE) 15 | else() 16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fPIC") 17 | add_definitions (-DOCC_CONVERT_SIGNALS) 18 | endif() 19 | 20 | # increase compiler warnings level (-W3 for MSVC, -Wextra for GCC) 21 | if (MSVC) 22 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 23 | string (REGEX REPLACE "/W[0-4]" "/W3" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 24 | else() 25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3") 26 | endif() 27 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") 29 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 30 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshorten-64-to-32") 31 | endif() 32 | if (BUILD_SHARED_LIBS) 33 | if (APPLE) 34 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}") 35 | elseif (NOT WIN32) 36 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}") 37 | endif() 38 | endif() 39 | endif() 40 | 41 | # Find dependencies 42 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.") 43 | if (MSVC) 44 | set (3RDPARTY_DLL_DIRS "" CACHE STRING "Paths to external DLLs separated by semicolon (FreeImage, FreeType, etc.)") 45 | endif() 46 | 47 | # Find OpenGL 48 | if (NOT WIN32 AND NOT APPLE) 49 | set (OpenGL_GL_PREFERENCE "GLVND") 50 | endif() 51 | find_package (OpenGL REQUIRED) 52 | 53 | # Find Qt 54 | set (QT_VERSION "Qt5" CACHE STRING "Qt major version to use") 55 | set_property (CACHE QT_VERSION PROPERTY STRINGS Qt5 Qt6) 56 | set (CMAKE_AUTOUIC ON) 57 | set (CMAKE_AUTOMOC ON) 58 | set (CMAKE_AUTORCC ON) 59 | set_property (GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP "Generated Files/Moc") 60 | set_property (GLOBAL PROPERTY AUTORCC_SOURCE_GROUP "Generated Files/Resources") 61 | find_package (${QT_VERSION} COMPONENTS Widgets REQUIRED) 62 | message (STATUS "Using ${QT_VERSION} from \"${${QT_VERSION}_DIR}\"") 63 | 64 | # Find Open CASCADE Technology 65 | find_package (OpenCASCADE REQUIRED) 66 | if (NOT OpenCASCADE_FOUND) 67 | message (FATAL_ERROR "could not find OpenCASCADE, please set OpenCASCADE_DIR variable" ) 68 | else() 69 | message (STATUS "Using OpenCASCADE from \"${OpenCASCADE_INSTALL_PREFIX}\"" ) 70 | message (STATUS "OpenCASCADE_INCLUDE_DIR=${OpenCASCADE_INCLUDE_DIR}") 71 | message (STATUS "OpenCASCADE_LIBRARY_DIR=${OpenCASCADE_LIBRARY_DIR}") 72 | include_directories(${OpenCASCADE_INCLUDE_DIR}) 73 | link_directories (${OpenCASCADE_LIBRARY_DIR}) 74 | endif() 75 | set (OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel) 76 | 77 | # main project target 78 | add_executable (${PROJECT_NAME} 79 | ../occt-qt-tools/OcctQtTools.h 80 | ../occt-qt-tools/OcctQtTools.cpp 81 | ../occt-qt-tools/OcctGlTools.h 82 | main.cpp 83 | OcctQMainWindowSample.h 84 | OcctQMainWindowSample.cpp 85 | OcctQWidgetViewer.h 86 | OcctQWidgetViewer.cpp 87 | ) 88 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Qt Widgets") 89 | target_link_libraries (${PROJECT_NAME} PRIVATE ${QT_VERSION}::Widgets ${OpenCASCADE_LIBS} ${OPENGL_LIBRARIES}) 90 | 91 | # auxiliary development environment 92 | if (MSVC) 93 | set (X_COMPILER_BITNESS x64) 94 | get_target_property (QtCore_location ${QT_VERSION}::Core LOCATION) 95 | get_filename_component (QT_BINARY_DIR ${QtCore_location} DIRECTORY) 96 | set (QT_PLUGINS_DIR) 97 | if (EXISTS "${QT_BINARY_DIR}/../plugins") 98 | set (QT_PLUGINS_DIR "${QT_BINARY_DIR}/../plugins") 99 | endif() 100 | 101 | get_target_property (aTKernelRel "TKernel" IMPORTED_LOCATION_RELEASE) 102 | get_target_property (aTKernelDbg "TKernel" IMPORTED_LOCATION_DEBUG) 103 | get_filename_component (OpenCASCADE_BINARY_DIR_RELEASE ${aTKernelRel} DIRECTORY) 104 | get_filename_component (OpenCASCADE_BINARY_DIR_DEBUG ${aTKernelDbg} DIRECTORY) 105 | if (NOT EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}" AND EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}") 106 | set (OpenCASCADE_BINARY_DIR_DEBUG "${OpenCASCADE_BINARY_DIR_RELEASE}") 107 | elseif (NOT EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}" AND EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}") 108 | set (OpenCASCADE_BINARY_DIR_RELEASE "${OpenCASCADE_BINARY_DIR_DEBUG}") 109 | endif() 110 | 111 | set_target_properties (${PROJECT_NAME} PROPERTIES 112 | VS_DEBUGGER_ENVIRONMENT "\ 113 | PATH=%PATH%;$,${OpenCASCADE_BINARY_DIR_DEBUG},${OpenCASCADE_BINARY_DIR_RELEASE}>;${3RDPARTY_DLL_DIRS};${QT_BINARY_DIR}\n\ 114 | QT_PLUGIN_PATH=${QT_PLUGINS_DIR}" 115 | ) 116 | endif() 117 | -------------------------------------------------------------------------------- /occt-qopenglwidget/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.13) 2 | 3 | project (occt-qopenglwidget-sample) 4 | 5 | set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../adm/cmake" ${CMAKE_MODULE_PATH}) 6 | set (APP_VERSION_MAJOR 1) 7 | set (APP_VERSION_MINOR 0) 8 | 9 | # compiler flags 10 | set (CMAKE_CXX_STANDARD 11) 11 | if (MSVC) 12 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise /EHa /MP") 13 | string (REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 14 | add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DUNICODE) 15 | else() 16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fPIC") 17 | add_definitions (-DOCC_CONVERT_SIGNALS) 18 | endif() 19 | 20 | # increase compiler warnings level (-W3 for MSVC, -Wextra for GCC) 21 | if (MSVC) 22 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 23 | string (REGEX REPLACE "/W[0-4]" "/W3" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 24 | else() 25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3") 26 | endif() 27 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") 29 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 30 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshorten-64-to-32") 31 | endif() 32 | if (BUILD_SHARED_LIBS) 33 | if (APPLE) 34 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}") 35 | elseif (NOT WIN32) 36 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}") 37 | endif() 38 | endif() 39 | endif() 40 | 41 | # Find dependencies 42 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.") 43 | if (MSVC) 44 | set (3RDPARTY_DLL_DIRS "" CACHE STRING "Paths to external DLLs separated by semicolon (FreeImage, FreeType, etc.)") 45 | endif() 46 | 47 | # Find OpenGL 48 | if (NOT WIN32 AND NOT APPLE) 49 | set (OpenGL_GL_PREFERENCE "GLVND") 50 | endif() 51 | find_package (OpenGL REQUIRED) 52 | 53 | # Find Qt 54 | set (QT_VERSION "Qt5" CACHE STRING "Qt major version to use") 55 | set_property (CACHE QT_VERSION PROPERTY STRINGS Qt5 Qt6) 56 | set (CMAKE_AUTOUIC ON) 57 | set (CMAKE_AUTOMOC ON) 58 | set (CMAKE_AUTORCC ON) 59 | set_property (GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP "Generated Files/Moc") 60 | set_property (GLOBAL PROPERTY AUTORCC_SOURCE_GROUP "Generated Files/Resources") 61 | if ("${QT_VERSION}" STREQUAL "Qt6") 62 | set (QT_COMPONENT_OPENGLWIDGETS "OpenGLWidgets") 63 | else() 64 | set (QT_COMPONENT_OPENGLWIDGETS "Widgets") 65 | endif() 66 | find_package (${QT_VERSION} COMPONENTS ${QT_COMPONENT_OPENGLWIDGETS} REQUIRED) 67 | message (STATUS "Using ${QT_VERSION} from \"${${QT_VERSION}_DIR}\"") 68 | 69 | # Find Open CASCADE Technology 70 | find_package (OpenCASCADE REQUIRED) 71 | if (NOT OpenCASCADE_FOUND) 72 | message (FATAL_ERROR "could not find OpenCASCADE, please set OpenCASCADE_DIR variable" ) 73 | else() 74 | message (STATUS "Using OpenCASCADE from \"${OpenCASCADE_INSTALL_PREFIX}\"" ) 75 | message (STATUS "OpenCASCADE_INCLUDE_DIR=${OpenCASCADE_INCLUDE_DIR}") 76 | message (STATUS "OpenCASCADE_LIBRARY_DIR=${OpenCASCADE_LIBRARY_DIR}") 77 | include_directories(${OpenCASCADE_INCLUDE_DIR}) 78 | link_directories (${OpenCASCADE_LIBRARY_DIR}) 79 | endif() 80 | set (OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel) 81 | 82 | # main project target 83 | add_executable (${PROJECT_NAME} 84 | ../occt-qt-tools/OcctQtTools.h 85 | ../occt-qt-tools/OcctQtTools.cpp 86 | ../occt-qt-tools/OcctGlTools.h 87 | ../occt-qt-tools/OcctGlTools.cpp 88 | main.cpp 89 | OcctQMainWindowSample.h 90 | OcctQMainWindowSample.cpp 91 | OcctQOpenGLWidgetViewer.h 92 | OcctQOpenGLWidgetViewer.cpp 93 | ) 94 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Qt Widgets") 95 | target_link_libraries (${PROJECT_NAME} PRIVATE ${QT_VERSION}::${QT_COMPONENT_OPENGLWIDGETS} ${OpenCASCADE_LIBS} ${OPENGL_LIBRARIES}) 96 | 97 | # auxiliary development environment 98 | if (MSVC) 99 | set (X_COMPILER_BITNESS x64) 100 | get_target_property (QtCore_location ${QT_VERSION}::Core LOCATION) 101 | get_filename_component (QT_BINARY_DIR ${QtCore_location} DIRECTORY) 102 | set (QT_PLUGINS_DIR) 103 | if (EXISTS "${QT_BINARY_DIR}/../plugins") 104 | set (QT_PLUGINS_DIR "${QT_BINARY_DIR}/../plugins") 105 | endif() 106 | 107 | get_target_property (aTKernelRel "TKernel" IMPORTED_LOCATION_RELEASE) 108 | get_target_property (aTKernelDbg "TKernel" IMPORTED_LOCATION_DEBUG) 109 | get_filename_component (OpenCASCADE_BINARY_DIR_RELEASE ${aTKernelRel} DIRECTORY) 110 | get_filename_component (OpenCASCADE_BINARY_DIR_DEBUG ${aTKernelDbg} DIRECTORY) 111 | if (NOT EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}" AND EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}") 112 | set (OpenCASCADE_BINARY_DIR_DEBUG "${OpenCASCADE_BINARY_DIR_RELEASE}") 113 | elseif (NOT EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}" AND EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}") 114 | set (OpenCASCADE_BINARY_DIR_RELEASE "${OpenCASCADE_BINARY_DIR_DEBUG}") 115 | endif() 116 | 117 | set_target_properties (${PROJECT_NAME} PROPERTIES 118 | VS_DEBUGGER_ENVIRONMENT "\ 119 | PATH=%PATH%;$,${OpenCASCADE_BINARY_DIR_DEBUG},${OpenCASCADE_BINARY_DIR_RELEASE}>;${3RDPARTY_DLL_DIRS};${QT_BINARY_DIR}\n\ 120 | QT_PLUGIN_PATH=${QT_PLUGINS_DIR}" 121 | ) 122 | endif() 123 | -------------------------------------------------------------------------------- /occt-qtquick/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required (VERSION 3.13) 2 | 3 | project (occt-qtquick-sample) 4 | 5 | set (CMAKE_MODULE_PATH "${CMAKE_CURRENT_SOURCE_DIR}/../adm/cmake" ${CMAKE_MODULE_PATH}) 6 | set (APP_VERSION_MAJOR 1) 7 | set (APP_VERSION_MINOR 0) 8 | 9 | # compiler flags 10 | set (CMAKE_CXX_STANDARD 11) 11 | if (MSVC) 12 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /fp:precise /EHa /MP") 13 | string (REGEX REPLACE "/EHsc" "" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 14 | add_definitions (-D_CRT_SECURE_NO_WARNINGS -D_CRT_NONSTDC_NO_DEPRECATE -DUNICODE) 15 | else() 16 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fexceptions -fPIC") 17 | add_definitions (-DOCC_CONVERT_SIGNALS) 18 | endif() 19 | 20 | # increase compiler warnings level (-W3 for MSVC, -Wextra for GCC) 21 | if (MSVC) 22 | if (CMAKE_CXX_FLAGS MATCHES "/W[0-4]") 23 | string (REGEX REPLACE "/W[0-4]" "/W3" CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS}") 24 | else() 25 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /W3") 26 | endif() 27 | elseif (CMAKE_COMPILER_IS_GNUCC OR CMAKE_COMPILER_IS_GNUCXX OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 28 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra") 29 | if ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 30 | set (CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wshorten-64-to-32") 31 | endif() 32 | if (BUILD_SHARED_LIBS) 33 | if (APPLE) 34 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}") 35 | elseif (NOT WIN32) 36 | set (CMAKE_SHARED_LINKER_FLAGS "-lm ${CMAKE_SHARED_LINKER_FLAGS}") 37 | endif() 38 | endif() 39 | endif() 40 | 41 | # Find dependencies 42 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.") 43 | if (MSVC) 44 | set (3RDPARTY_DLL_DIRS "" CACHE STRING "Paths to external DLLs separated by semicolon (FreeImage, FreeType, etc.)") 45 | endif() 46 | 47 | # Find OpenGL 48 | if (NOT WIN32 AND NOT APPLE) 49 | set (OpenGL_GL_PREFERENCE "GLVND") 50 | endif() 51 | find_package (OpenGL REQUIRED) 52 | 53 | # Find Qt 54 | set (QT_VERSION "Qt5" CACHE STRING "Qt major version to use") 55 | set_property (CACHE QT_VERSION PROPERTY STRINGS Qt5 Qt6) 56 | set (CMAKE_AUTOUIC ON) 57 | set (CMAKE_AUTOMOC ON) 58 | set (CMAKE_AUTORCC ON) 59 | set_property (GLOBAL PROPERTY AUTOMOC_SOURCE_GROUP "Generated Files/Moc") 60 | set_property (GLOBAL PROPERTY AUTORCC_SOURCE_GROUP "Generated Files/Resources") 61 | set (Qt_COMPONENTS Core Gui Qml Quick QuickControls2 Widgets) 62 | set (Qt_LIBS) 63 | foreach (aLibIter ${Qt_COMPONENTS}) 64 | list (APPEND Qt_LIBS "${QT_VERSION}::${aLibIter}") 65 | endforeach() 66 | find_package (${QT_VERSION} COMPONENTS ${Qt_COMPONENTS} REQUIRED) 67 | message (STATUS "Using ${QT_VERSION} from \"${${QT_VERSION}_DIR}\"") 68 | 69 | # Find Open CASCADE Technology 70 | find_package (OpenCASCADE REQUIRED) 71 | if (NOT OpenCASCADE_FOUND) 72 | message (FATAL_ERROR "could not find OpenCASCADE, please set OpenCASCADE_DIR variable" ) 73 | else() 74 | message (STATUS "Using OpenCASCADE from \"${OpenCASCADE_INSTALL_PREFIX}\"" ) 75 | message (STATUS "OpenCASCADE_INCLUDE_DIR=${OpenCASCADE_INCLUDE_DIR}") 76 | message (STATUS "OpenCASCADE_LIBRARY_DIR=${OpenCASCADE_LIBRARY_DIR}") 77 | include_directories(${OpenCASCADE_INCLUDE_DIR}) 78 | link_directories (${OpenCASCADE_LIBRARY_DIR}) 79 | endif() 80 | set (OpenCASCADE_LIBS TKRWMesh TKBinXCAF TKBin TKBinL TKOpenGl TKXCAF TKVCAF TKCAF TKV3d TKHLR TKMesh TKService TKShHealing TKPrim TKTopAlgo TKGeomAlgo TKBRep TKGeomBase TKG3d TKG2d TKMath TKLCAF TKCDF TKernel) 81 | 82 | # main project target 83 | add_executable (${PROJECT_NAME} 84 | ../occt-qt-tools/OcctQtTools.h 85 | ../occt-qt-tools/OcctQtTools.cpp 86 | ../occt-qt-tools/OcctGlTools.h 87 | ../occt-qt-tools/OcctGlTools.cpp 88 | main.cpp 89 | main5.qml 90 | main6.qml 91 | OcctQQuickFramebufferViewer.h 92 | OcctQQuickFramebufferViewer.cpp 93 | occt-qtquick.qrc 94 | ) 95 | set_target_properties (${PROJECT_NAME} PROPERTIES FOLDER "Qt Quick") 96 | target_link_libraries (${PROJECT_NAME} PRIVATE 97 | ${Qt_LIBS} 98 | ${OpenCASCADE_LIBS} 99 | ${OPENGL_LIBRARIES} 100 | ) 101 | 102 | # auxiliary development environment 103 | if (MSVC) 104 | set (X_COMPILER_BITNESS x64) 105 | get_target_property (QtCore_location ${QT_VERSION}::Core LOCATION) 106 | get_filename_component (QT_BINARY_DIR ${QtCore_location} DIRECTORY) 107 | set (QT_PLUGINS_DIR) 108 | if (EXISTS "${QT_BINARY_DIR}/../plugins") 109 | set (QT_PLUGINS_DIR "${QT_BINARY_DIR}/../plugins") 110 | endif() 111 | 112 | get_target_property (aTKernelRel "TKernel" IMPORTED_LOCATION_RELEASE) 113 | get_target_property (aTKernelDbg "TKernel" IMPORTED_LOCATION_DEBUG) 114 | get_filename_component (OpenCASCADE_BINARY_DIR_RELEASE ${aTKernelRel} DIRECTORY) 115 | get_filename_component (OpenCASCADE_BINARY_DIR_DEBUG ${aTKernelDbg} DIRECTORY) 116 | if (NOT EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}" AND EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}") 117 | set (OpenCASCADE_BINARY_DIR_DEBUG "${OpenCASCADE_BINARY_DIR_RELEASE}") 118 | elseif (NOT EXISTS "${OpenCASCADE_BINARY_DIR_RELEASE}" AND EXISTS "${OpenCASCADE_BINARY_DIR_DEBUG}") 119 | set (OpenCASCADE_BINARY_DIR_RELEASE "${OpenCASCADE_BINARY_DIR_DEBUG}") 120 | endif() 121 | 122 | set_target_properties (${PROJECT_NAME} PROPERTIES 123 | VS_DEBUGGER_ENVIRONMENT "\ 124 | PATH=%PATH%;$,${OpenCASCADE_BINARY_DIR_DEBUG},${OpenCASCADE_BINARY_DIR_RELEASE}>;${3RDPARTY_DLL_DIRS};${QT_BINARY_DIR}\n\ 125 | QT_PLUGIN_PATH=${QT_PLUGINS_DIR}" 126 | ) 127 | endif() 128 | -------------------------------------------------------------------------------- /ReadMe.md: -------------------------------------------------------------------------------- 1 | OCCT 3D Viewer setup within Qt 2 | ============================== 3 | 4 | The samples in this repository demonstrate Open CASCADE Technology (OCCT) 3D Viewer setup 5 | within Qt GUI framework on Windows and Linux platforms. 6 | 7 | ![sample screenshot](/images/occt-qopenglwidget-sample-wnt.png) 8 | 9 | ## Building 10 | 11 | Use *CMake* for building the project. 12 | See `CMakeLists.txt` in the folder defining building rules. 13 | Use `cmake-gui` or command-line interface for configuring project dependencies (Qt, OCCT). 14 | Building has been checked within OCCT 7.7.0. 15 | 16 | Make sure to copy necessary dependencies (*OCCT*, *FreeType*, *FreeImage*, etc. - depending on *OCCT* building configuration) 17 | into sample folder or configuring environment (`PATH`, `LD_LIBRARY_PATH`). 18 | 19 | *Legacy: some samples contains qmake project definition like `occt-qopenglwidget-sample.pro`,* 20 | *which could be used as alternative to CMake.* 21 | *Create `custom.pri` from `custom.pri.template` with filled in paths to OCCT.* 22 | 23 | ## OCCT Qt tools 24 | 25 | Pseudo-project within `occt-qt-tools` subfolder provides source code for gluing between Qt and OCCT: 26 | - `OcctQtTools` 27 | - conversion between common Qt and OCCT structures like colors and strings; 28 | - Qt application setup for embedding 3D viewer; 29 | - Qt input events conversion into OCCT 3D Viewer events. 30 | - `OcctGlTools` - common tools (independent from Qt) for wrapping externally created OpenGL context to setup OCCT 3D Viewer. 31 | 32 | Each Qt sample in the list below is defined independently 33 | so that it could be easily extracted to define your own application based on selected Qt module 34 | without bloating project with unnecessary code and dependencies. 35 | However, this leads to some code duplication in samples, which `OcctQtTools` tries to reduce. 36 | 37 | ## OCCT QWidget sample 38 | 39 | Project within `occt-qwidget` subfolder shows OCCT 3D viewer setup within 40 | basic `QWidget` from native window handle (`QWidget::setAttribute(Qt::WA_NativeWindow)` attribute). 41 | 42 | Within this approach, OCCT will be responsible to initialize OpenGL / OpenGL ES context on its own (Qt will be unaware of it). 43 | Unlike `QOpenGLWidget` sample, the widgets on top of `QWidget` holding 3D Viewer should have solid background color, 44 | as Qt will be unable to blend widgets together on its own. 45 | 46 | *Notice: `QOpenGLWidget` (see next) is a preferred way for integrating OpenGL viewer into Qt Widgets application.* 47 | *This `QWidget` sample demonstrates a working approach commonly used by applications based on Qt3/Qt4 and other GUI frameworks.* 48 | 49 | ## OCCT QOpenGLWidget sample 50 | 51 | Project within `occt-qopenglwidget` subfolder shows OCCT 3D viewer setup 52 | from *OpenGL* context created by `QOpenGLWidget` within Qt Widgets application. 53 | 54 | The approach with `QOpenGLWidget` requires careful gluing layer for Qt and OCCT 3D Viewer to share common OpenGL context. 55 | Unlike `QWidget` sample, the widgets on top of `QOpenGLWidget` holding 3D Viewer might have semitransparent background color, 56 | as Qt will be able to blend widgets together on its own. 57 | 58 | ![sample screenshot](/images/occt-qopenglwidget-sample-x11.png) 59 | 60 | ## OCCT QtQuick/QML sample 61 | 62 | Project within `occt-qtquick` subfolder shows OCCT 3D viewer setup 63 | from *OpenGL* context created by `QQuickFramebufferObject` within Qt5 QtQuick/QML application. 64 | 65 | The approach with `QQuickFramebufferObject` requires careful gluing layer for Qt and OCCT 3D Viewer to share common OpenGL context. 66 | 67 | ## Common tips 68 | 69 | ### QSGRenderThread 70 | 71 | `QtQuick` by default will attempt offloading rendering into a separate working thread (`QSGRenderThread`) on some systems, 72 | which might improve application GUI performance in some cases. 73 | 74 | This, however, requires addition of multithreading synchronization mechanism when dealing with OCCT 3D Viewer from GUI thread. 75 | Uncomment lines setting `QSG_RENDER_LOOP` environment variable, 76 | if these complexities are undesired for your application to ask Qt managing rendering from GUI thread. 77 | 78 | It is still responsibility of application logic to offload expensive operations (e.g. not GUI and rendering) 79 | into a separate threads - see next tip. 80 | 81 | ### Background thread for expensive operations 82 | 83 | CAD applications usually perform many operations that might take considerable time. 84 | When called from the main (or rendering) threads, these operations will make GUI and 3D Viewer unresponsive. 85 | Apart from poor user experience, some system might suggest to terminate hanged applications. 86 | 87 | The preferred approach is to offload into a background thread operations like: 88 | - Blocking operations (reading from files, network); 89 | - Import/export of models; 90 | - Modeling operations (Booleans and others); 91 | - Other expensive algorithms (like `BRepMesh`). 92 | 93 | ### Message log 94 | 95 | Provide `Message_Printer` implementation to redirect messages coming from OCCT algorithms to end user (GUI) 96 | or to application logs (for developers). 97 | 98 | Define your own handler for Qt messages via `qInstallMessageHandler`; 99 | redirect these messages to `Message::DefaultMessenger()`, to GUI or to application logs. 100 | 101 | ### Exception handling 102 | 103 | OCCT algorithms may raise C++ exceptions. 104 | Don't forget to put algorithms into `try`/`catch` blocks to prevent GUI crashes. 105 | 106 | Override `QApplication::notify()` to catch `Standard_Failure` exceptions 107 | for gracefully handling internal/algorithmic errors to end user without crashing application. 108 | 109 | Use `OSD::SetSignal()` to further protect application from crashes due to internal OCCT errors, 110 | although this mechanism may hinder such OCCT bugs and should be used with caution. 111 | -------------------------------------------------------------------------------- /occt-qopenglwidget/OcctQMainWindowSample.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #include "OcctQMainWindowSample.h" 4 | 5 | #include "OcctQOpenGLWidgetViewer.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // ================================================================ 20 | // Function : OcctQMainWindowSample 21 | // ================================================================ 22 | OcctQMainWindowSample::OcctQMainWindowSample() 23 | { 24 | // 3D Viewer widget as a central widget 25 | myViewer = new OcctQOpenGLWidgetViewer(); 26 | setCentralWidget(myViewer); 27 | 28 | // menu bar 29 | createMenuBar(); 30 | 31 | // some controls on top of 3D Viewer 32 | createLayoutOverViewer(); 33 | } 34 | 35 | // ================================================================ 36 | // Function : createMenuBar 37 | // ================================================================ 38 | void OcctQMainWindowSample::createMenuBar() 39 | { 40 | QMenuBar* aMenuBar = new QMenuBar(); 41 | QMenu* aMenuWindow = aMenuBar->addMenu("&File"); 42 | #if (OCC_VERSION_HEX >= 0x070700) 43 | { 44 | QAction* anActionSplit = new QAction(aMenuWindow); 45 | anActionSplit->setText("Split Views"); 46 | aMenuWindow->addAction(anActionSplit); 47 | connect(anActionSplit, &QAction::triggered, [this]() { splitSubviews(); }); 48 | } 49 | #endif 50 | { 51 | QAction* anActionQuit = new QAction(aMenuWindow); 52 | anActionQuit->setText("Quit"); 53 | aMenuWindow->addAction(anActionQuit); 54 | connect(anActionQuit, &QAction::triggered, [this]() { close(); }); 55 | } 56 | setMenuBar(aMenuBar); 57 | } 58 | 59 | // ================================================================ 60 | // Function : createLayoutOverViewer 61 | // ================================================================ 62 | void OcctQMainWindowSample::createLayoutOverViewer() 63 | { 64 | QVBoxLayout* aLayout = new QVBoxLayout(myViewer); 65 | aLayout->setDirection(QBoxLayout::BottomToTop); 66 | aLayout->setAlignment(Qt::AlignBottom); 67 | { 68 | // button displaying message window with OpenGL info 69 | QPushButton* aQuitBtn = new QPushButton("About"); 70 | aLayout->addWidget(aQuitBtn); 71 | connect(aQuitBtn, &QPushButton::clicked, [this]() { 72 | QMessageBox::information(0, 73 | "About Sample", 74 | QString() + "OCCT 3D Viewer sample embedded into Qt Widgets.\n\n" 75 | + "Open CASCADE Technology v." OCC_VERSION_STRING_EXT "\n" 76 | + "Qt v." QT_VERSION_STR "\n\n" + "OpenGL info:\n" + myViewer->getGlInfo()); 77 | }); 78 | } 79 | { 80 | // slider changing viewer background color 81 | 82 | // the widgets on top of OCCT 3D Viewer (implemented as QOpenGLWidget) might have transparent background 83 | QWidget* aSliderBox = new QWidget(); 84 | aSliderBox->setStyleSheet("QWidget { background-color: rgba(0, 0, 0, 0); }"); 85 | 86 | QHBoxLayout* aSliderLayout = new QHBoxLayout(aSliderBox); 87 | { 88 | QLabel* aSliderLabel = new QLabel("Background"); 89 | aSliderLabel->setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 0); color: white; }"); 90 | aSliderLabel->setGeometry(50, 50, 50, 50); 91 | aSliderLabel->adjustSize(); 92 | aSliderLayout->addWidget(aSliderLabel); 93 | } 94 | { 95 | QSlider* aSlider = new QSlider(Qt::Horizontal); 96 | aSlider->setRange(0, 255); 97 | aSlider->setSingleStep(1); 98 | aSlider->setPageStep(15); 99 | aSlider->setTickInterval(15); 100 | aSlider->setTickPosition(QSlider::TicksRight); 101 | aSlider->setValue(0); 102 | aSliderLayout->addWidget(aSlider); 103 | connect(aSlider, &QSlider::valueChanged, [this](int theValue) { 104 | const float aVal = theValue / 255.0f; 105 | const Quantity_Color aColor(aVal, aVal, aVal, Quantity_TOC_sRGB); 106 | #if (OCC_VERSION_HEX >= 0x070700) 107 | for (const Handle(V3d_View)& aSubviewIter : myViewer->View()->Subviews()) 108 | { 109 | aSubviewIter->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical); 110 | aSubviewIter->Invalidate(); 111 | } 112 | #endif 113 | // myViewer->View()->SetBackgroundColor(aColor); 114 | myViewer->View()->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical); 115 | myViewer->View()->Invalidate(); 116 | myViewer->update(); 117 | }); 118 | } 119 | 120 | aLayout->addWidget(aSliderBox); 121 | } 122 | } 123 | 124 | // ================================================================ 125 | // Function : splitSubviews 126 | // ================================================================ 127 | void OcctQMainWindowSample::splitSubviews() 128 | { 129 | #if (OCC_VERSION_HEX >= 0x070700) 130 | if (!myViewer->View()->Subviews().IsEmpty()) 131 | { 132 | // remove subviews 133 | myViewer->View()->View()->SetSubviewComposer(false); 134 | NCollection_Sequence aSubviews = myViewer->View()->Subviews(); 135 | for (const Handle(V3d_View)& aSubviewIter : aSubviews) 136 | aSubviewIter->Remove(); 137 | 138 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, myViewer->View()); 139 | } 140 | else 141 | { 142 | // create two subviews splitting window horizontally 143 | myViewer->View()->View()->SetSubviewComposer(true); 144 | 145 | Handle(V3d_View) aSubView1 = new V3d_View(myViewer->Viewer()); 146 | aSubView1->SetImmediateUpdate(false); 147 | aSubView1->SetWindow(myViewer->View(), 148 | Graphic3d_Vec2d(0.5, 1.0), 149 | Aspect_TOTP_LEFT_UPPER, 150 | Graphic3d_Vec2d(0.0, 0.0)); 151 | 152 | Handle(V3d_View) aSubView2 = new V3d_View(myViewer->Viewer()); 153 | aSubView2->SetImmediateUpdate(false); 154 | aSubView2->SetWindow(myViewer->View(), 155 | Graphic3d_Vec2d(0.5, 1.0), 156 | Aspect_TOTP_LEFT_UPPER, 157 | Graphic3d_Vec2d(0.5, 0.0)); 158 | 159 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, aSubView1); 160 | } 161 | myViewer->View()->Invalidate(); 162 | myViewer->update(); 163 | #endif 164 | } 165 | -------------------------------------------------------------------------------- /occt-qwidget/OcctQMainWindowSample.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #include "OcctQMainWindowSample.h" 4 | 5 | #include "OcctQWidgetViewer.h" 6 | 7 | #include 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | // ================================================================ 20 | // Function : OcctQMainWindowSample 21 | // ================================================================ 22 | OcctQMainWindowSample::OcctQMainWindowSample() 23 | { 24 | // 3D Viewer widget as a central widget 25 | myViewer = new OcctQWidgetViewer(); 26 | setCentralWidget(myViewer); 27 | 28 | // menu bar 29 | createMenuBar(); 30 | 31 | // some controls on top of 3D Viewer 32 | createLayoutOverViewer(); 33 | } 34 | 35 | // ================================================================ 36 | // Function : createMenuBar 37 | // ================================================================ 38 | void OcctQMainWindowSample::createMenuBar() 39 | { 40 | QMenuBar* aMenuBar = new QMenuBar(); 41 | QMenu* aMenuWindow = aMenuBar->addMenu("&File"); 42 | #if (OCC_VERSION_HEX >= 0x070700) 43 | { 44 | QAction* anActionSplit = new QAction(aMenuWindow); 45 | anActionSplit->setText("Split Views"); 46 | aMenuWindow->addAction(anActionSplit); 47 | connect(anActionSplit, &QAction::triggered, [this]() { splitSubviews(); }); 48 | } 49 | #endif 50 | { 51 | QAction* anActionQuit = new QAction(aMenuWindow); 52 | anActionQuit->setText("Quit"); 53 | aMenuWindow->addAction(anActionQuit); 54 | connect(anActionQuit, &QAction::triggered, [this]() { close(); }); 55 | } 56 | setMenuBar(aMenuBar); 57 | } 58 | 59 | // ================================================================ 60 | // Function : createLayoutOverViewer 61 | // ================================================================ 62 | void OcctQMainWindowSample::createLayoutOverViewer() 63 | { 64 | QVBoxLayout* aLayout = new QVBoxLayout(myViewer); 65 | aLayout->setDirection(QBoxLayout::BottomToTop); 66 | aLayout->setAlignment(Qt::AlignBottom); 67 | { 68 | // button displaying message window with OpenGL info 69 | QPushButton* aQuitBtn = new QPushButton("About"); 70 | aLayout->addWidget(aQuitBtn); 71 | connect(aQuitBtn, &QPushButton::clicked, [this]() { 72 | QMessageBox::information(0, 73 | "About Sample", 74 | QString() + "OCCT 3D Viewer sample embedded into Qt Widgets.\n\n" 75 | + "Open CASCADE Technology v." OCC_VERSION_STRING_EXT "\n" 76 | + "Qt v." QT_VERSION_STR "\n\n" + "OpenGL info:\n" + myViewer->getGlInfo()); 77 | }); 78 | } 79 | { 80 | // slider changing viewer background color 81 | 82 | // make sure widget on top of OCCT 3D Viewer (implemented as QWidget and not QOpenGLWidget) 83 | // is filled with color - semitransparent widgets will have artifacts 84 | QWidget* aSliderBox = new QWidget(); 85 | aSliderBox->setStyleSheet("QWidget { background-color: black; }"); 86 | 87 | QHBoxLayout* aSliderLayout = new QHBoxLayout(aSliderBox); 88 | { 89 | QLabel* aSliderLabel = new QLabel("Background"); 90 | aSliderLabel->setStyleSheet("QLabel { background-color: rgba(0, 0, 0, 0); color: white; }"); 91 | aSliderLabel->setGeometry(50, 50, 50, 50); 92 | aSliderLabel->adjustSize(); 93 | aSliderLayout->addWidget(aSliderLabel); 94 | } 95 | { 96 | QSlider* aSlider = new QSlider(Qt::Horizontal); 97 | aSlider->setRange(0, 255); 98 | aSlider->setSingleStep(1); 99 | aSlider->setPageStep(15); 100 | aSlider->setTickInterval(15); 101 | aSlider->setTickPosition(QSlider::TicksRight); 102 | aSlider->setValue(0); 103 | aSliderLayout->addWidget(aSlider); 104 | connect(aSlider, &QSlider::valueChanged, [this](int theValue) { 105 | const float aVal = theValue / 255.0f; 106 | const Quantity_Color aColor(aVal, aVal, aVal, Quantity_TOC_sRGB); 107 | #if (OCC_VERSION_HEX >= 0x070700) 108 | for (const Handle(V3d_View)& aSubviewIter : myViewer->View()->Subviews()) 109 | { 110 | aSubviewIter->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical); 111 | aSubviewIter->Invalidate(); 112 | } 113 | #endif 114 | // myViewer->View()->SetBackgroundColor(aColor); 115 | myViewer->View()->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical); 116 | myViewer->View()->Invalidate(); 117 | myViewer->update(); 118 | }); 119 | } 120 | 121 | aLayout->addWidget(aSliderBox); 122 | } 123 | } 124 | 125 | // ================================================================ 126 | // Function : splitSubviews 127 | // ================================================================ 128 | void OcctQMainWindowSample::splitSubviews() 129 | { 130 | #if (OCC_VERSION_HEX >= 0x070700) 131 | if (!myViewer->View()->Subviews().IsEmpty()) 132 | { 133 | // remove subviews 134 | myViewer->View()->View()->SetSubviewComposer(false); 135 | NCollection_Sequence aSubviews = myViewer->View()->Subviews(); 136 | for (const Handle(V3d_View)& aSubviewIter : aSubviews) 137 | aSubviewIter->Remove(); 138 | 139 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, myViewer->View()); 140 | } 141 | else 142 | { 143 | // create two subviews splitting window horizontally 144 | myViewer->View()->View()->SetSubviewComposer(true); 145 | 146 | Handle(V3d_View) aSubView1 = new V3d_View(myViewer->Viewer()); 147 | aSubView1->SetImmediateUpdate(false); 148 | aSubView1->SetWindow(myViewer->View(), 149 | Graphic3d_Vec2d(0.5, 1.0), 150 | Aspect_TOTP_LEFT_UPPER, 151 | Graphic3d_Vec2d(0.0, 0.0)); 152 | 153 | Handle(V3d_View) aSubView2 = new V3d_View(myViewer->Viewer()); 154 | aSubView2->SetImmediateUpdate(false); 155 | aSubView2->SetWindow(myViewer->View(), 156 | Graphic3d_Vec2d(0.5, 1.0), 157 | Aspect_TOTP_LEFT_UPPER, 158 | Graphic3d_Vec2d(0.5, 0.0)); 159 | 160 | myViewer->OnSubviewChanged(myViewer->Context(), nullptr, aSubView1); 161 | } 162 | myViewer->View()->Invalidate(); 163 | myViewer->update(); 164 | #endif 165 | } 166 | -------------------------------------------------------------------------------- /occt-qt-tools/OcctGlTools.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Kirill Gavrilov 2 | 3 | #ifdef _WIN32 4 | #include 5 | #endif 6 | 7 | #include "OcctGlTools.h" 8 | 9 | #include 10 | #include 11 | #include 12 | #include 13 | #include 14 | 15 | // Exporting this symbol from .exe with value=1 will direct to NVIDIA GPU on Optimus systems 16 | //__declspec(dllexport) DWORD NvOptimusEnablement = 1; 17 | // Exporting this symbol from .exe with value=1 will direct to faster GPU on AMD PowerXpress systems 18 | //__declspec(dllexport) int AmdPowerXpressRequestHighPerformance = 1; 19 | 20 | //! OpenGL FBO subclass for wrapping FBO created by Qt using GL_RGBA8 21 | //! texture format instead of GL_SRGB8_ALPHA8. 22 | //! This FBO is set to OpenGl_Context::SetDefaultFrameBuffer() as a final target. 23 | //! Subclass calls OpenGl_Context::SetFrameBufferSRGB() with sRGB=false flag, 24 | //! which asks OCCT to disable GL_FRAMEBUFFER_SRGB and apply sRGB gamma correction manually. 25 | class OcctQtFrameBuffer : public OpenGl_FrameBuffer 26 | { 27 | DEFINE_STANDARD_RTTI_INLINE(OcctQtFrameBuffer, OpenGl_FrameBuffer) 28 | public: 29 | //! Empty constructor. 30 | OcctQtFrameBuffer() {} 31 | 32 | //! Make this FBO active in context. 33 | virtual void BindBuffer(const Handle(OpenGl_Context)& theGlCtx) override 34 | { 35 | OpenGl_FrameBuffer::BindBuffer(theGlCtx); 36 | theGlCtx->SetFrameBufferSRGB(true, false); 37 | } 38 | 39 | //! Make this FBO as drawing target in context. 40 | virtual void BindDrawBuffer(const Handle(OpenGl_Context)& theGlCtx) override 41 | { 42 | OpenGl_FrameBuffer::BindDrawBuffer(theGlCtx); 43 | theGlCtx->SetFrameBufferSRGB(true, false); 44 | } 45 | 46 | //! Make this FBO as reading source in context. 47 | virtual void BindReadBuffer(const Handle(OpenGl_Context)& theGlCtx) override 48 | { 49 | OpenGl_FrameBuffer::BindReadBuffer(theGlCtx); 50 | } 51 | }; 52 | 53 | // ================================================================ 54 | // Function : GetGlContext 55 | // ================================================================ 56 | Handle(OpenGl_Context) OcctGlTools::GetGlContext(const Handle(V3d_View)& theView) 57 | { 58 | Handle(OpenGl_View) aGlView = Handle(OpenGl_View)::DownCast(theView->View()); 59 | return aGlView->GlWindow()->GetGlContext(); 60 | } 61 | 62 | // ================================================================ 63 | // Function : GetGlNativeWindow 64 | // ================================================================ 65 | Aspect_Drawable OcctGlTools::GetGlNativeWindow(Aspect_Drawable theNativeWin) 66 | { 67 | Aspect_Drawable aNativeWin = (Aspect_Drawable)theNativeWin; 68 | #ifdef _WIN32 69 | HDC aWglDevCtx = wglGetCurrentDC(); 70 | HWND aWglWin = WindowFromDC(aWglDevCtx); 71 | aNativeWin = (Aspect_Drawable)aWglWin; 72 | #endif 73 | return aNativeWin; 74 | } 75 | 76 | // ================================================================ 77 | // Function : InitializeGlWindow 78 | // ================================================================ 79 | bool OcctGlTools::InitializeGlWindow(const Handle(V3d_View)& theView, 80 | const Aspect_Drawable theNativeWin, 81 | const Graphic3d_Vec2i& theSize, 82 | const double thePixelRatio) 83 | { 84 | const Aspect_Drawable aNativeWin = GetGlNativeWindow(theNativeWin); 85 | 86 | Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast(theView->Viewer()->Driver()); 87 | Handle(OpenGl_Context) aGlCtx = new OpenGl_Context(); 88 | if (!aGlCtx->Init(!aDriver->Options().contextCompatible)) 89 | { 90 | Message::SendFail() << "Error: OpenGl_Context is unable to wrap OpenGL context"; 91 | return false; 92 | } 93 | 94 | Handle(OcctNeutralWindow) aWindow = Handle(OcctNeutralWindow)::DownCast(theView->Window()); 95 | if (aWindow.IsNull()) 96 | { 97 | aWindow = new OcctNeutralWindow(); 98 | aWindow->SetVirtual(true); 99 | } 100 | aWindow->SetNativeHandle(aNativeWin); 101 | aWindow->SetSize(theSize.x(), theSize.y()); 102 | aWindow->SetDevicePixelRatio(thePixelRatio); 103 | theView->SetWindow(aWindow, aGlCtx->RenderingContext()); 104 | theView->MustBeResized(); 105 | theView->Invalidate(); 106 | #if (OCC_VERSION_HEX >= 0x070700) 107 | for (const Handle(V3d_View)& aSubviewIter : theView->Subviews()) 108 | { 109 | aSubviewIter->MustBeResized(); 110 | aSubviewIter->Invalidate(); 111 | } 112 | #endif 113 | return true; 114 | } 115 | 116 | // ================================================================ 117 | // Function : InitializeGlFbo 118 | // ================================================================ 119 | bool OcctGlTools::InitializeGlFbo(const Handle(V3d_View)& theView) 120 | { 121 | Handle(OpenGl_Context) aGlCtx = OcctGlTools::GetGlContext(theView); 122 | Handle(OpenGl_FrameBuffer) aDefaultFbo = aGlCtx->DefaultFrameBuffer(); 123 | if (aDefaultFbo.IsNull()) 124 | { 125 | aDefaultFbo = new OcctQtFrameBuffer(); 126 | aGlCtx->SetDefaultFrameBuffer(aDefaultFbo); 127 | } 128 | if (!aDefaultFbo->InitWrapper(aGlCtx)) 129 | { 130 | aDefaultFbo.Nullify(); 131 | Message::DefaultMessenger()->Send("Default FBO wrapper creation failed", Message_Fail); 132 | return false; 133 | } 134 | 135 | Graphic3d_Vec2i aViewSizeOld; 136 | const Graphic3d_Vec2i aViewSizeNew = aDefaultFbo->GetVPSize(); 137 | Handle(OcctNeutralWindow) aWindow = Handle(OcctNeutralWindow)::DownCast(theView->Window()); 138 | aWindow->Size(aViewSizeOld.x(), aViewSizeOld.y()); 139 | if (aViewSizeNew != aViewSizeOld) 140 | { 141 | aWindow->SetSize(aViewSizeNew.x(), aViewSizeNew.y()); 142 | theView->MustBeResized(); 143 | theView->Invalidate(); 144 | #if (OCC_VERSION_HEX >= 0x070700) 145 | for (const Handle(V3d_View)& aSubviewIter : theView->Subviews()) 146 | { 147 | aSubviewIter->MustBeResized(); 148 | aSubviewIter->Invalidate(); 149 | aDefaultFbo->SetupViewport(aGlCtx); 150 | } 151 | #endif 152 | } 153 | return true; 154 | } 155 | 156 | // ================================================================ 157 | // Function : ResetGlStateBeforeOcct 158 | // ================================================================ 159 | void OcctGlTools::ResetGlStateBeforeOcct(const Handle(V3d_View)& theView) 160 | { 161 | Handle(OpenGl_Context) aGlCtx = GetGlContext(theView); 162 | if (aGlCtx.IsNull()) 163 | return; 164 | 165 | if (aGlCtx->core20fwd != nullptr) 166 | { 167 | // shouldn't be a problem in most cases, but make sure to unbind active GLSL program 168 | aGlCtx->core20fwd->glUseProgram(0); 169 | } 170 | 171 | // Qt leaves GL_BLEND enabled after drawing semitransparent elements, 172 | // but OCCT doesn't reset its state before drawing opaque objects. 173 | // Disable also texture bindings left by Qt. 174 | aGlCtx->core11fwd->glBindTexture(GL_TEXTURE_2D, 0); 175 | aGlCtx->core11fwd->glDisable(GL_BLEND); 176 | if (aGlCtx->core11ffp != nullptr) 177 | { 178 | aGlCtx->core11fwd->glDisable(GL_ALPHA_TEST); 179 | aGlCtx->core11fwd->glDisable(GL_TEXTURE_2D); 180 | } 181 | } 182 | 183 | // ================================================================ 184 | // Function : ResetGlStateAfterOcct 185 | // ================================================================ 186 | void OcctGlTools::ResetGlStateAfterOcct(const Handle(V3d_View)& theView) 187 | { 188 | Handle(OpenGl_Context) aGlCtx = GetGlContext(theView); 189 | if (aGlCtx.IsNull()) 190 | return; 191 | 192 | // Qt expects default OpenGL pack/unpack alignment setup, 193 | // while OCCT manages it dynamically; 194 | // without resetting alignment setup, Qt will draw 195 | // some textures corrupted (like fonts) 196 | aGlCtx->core11fwd->glPixelStorei(GL_PACK_ALIGNMENT, 4); 197 | aGlCtx->core11fwd->glPixelStorei(GL_UNPACK_ALIGNMENT, 4); 198 | 199 | if (aGlCtx->core15fwd != nullptr) 200 | { 201 | // Qt expects first texture object to be bound, 202 | // but OCCT might leave another object bound within multi-texture mapping 203 | aGlCtx->core15fwd->glActiveTexture(GL_TEXTURE0); 204 | } 205 | } 206 | -------------------------------------------------------------------------------- /adm/cmake/FindOpenCASCADE.cmake: -------------------------------------------------------------------------------- 1 | # This script finds OpenCASCADE Technology libraries. 2 | # The script requires: 3 | # OpenCASCADE_DIR - root OCCT folder or folder with CMake configuration files 4 | # 5 | # Script will define the following variables on success: 6 | # OpenCASCADE_FOUND - package is successfully found 7 | # OpenCASCADE_INCLUDE_DIR - directory with headers 8 | # OpenCASCADE_LIBRARY_DIR - directory with libraries for linker 9 | # OpenCASCADE_BINARY_DIR - directory with DLLs 10 | # OpenCASCADE_RESOURCE_DIR - directory with resource files 11 | include(FindPackageHandleStandardArgs) 12 | 13 | # MY_PLATFORM variable 14 | math (EXPR MY_BITNESS "32 + 32*(${CMAKE_SIZEOF_VOID_P}/8)") 15 | if (WIN32) 16 | set (MY_PLATFORM "win${MY_BITNESS}") 17 | elseif(APPLE) 18 | set (MY_PLATFORM "mac") 19 | else() 20 | set (MY_PLATFORM "lin") 21 | endif() 22 | 23 | # MY_PLATFORM_AND_COMPILER variable 24 | if (MSVC) 25 | if (MSVC90) 26 | set (MY_COMPILER vc9) 27 | elseif (MSVC10) 28 | set (MY_COMPILER vc10) 29 | elseif (MSVC11) 30 | set (MY_COMPILER vc11) 31 | elseif (MSVC12) 32 | set (MY_COMPILER vc12) 33 | elseif (MSVC14) 34 | set (MY_COMPILER vc14) 35 | else() 36 | set (MY_COMPILER vc15) 37 | message (WARNING "Unknown msvc version. $$MY_COMPILER is used") 38 | endif() 39 | elseif (DEFINED CMAKE_COMPILER_IS_GNUCC) 40 | set (MY_COMPILER gcc) 41 | elseif (DEFINED CMAKE_COMPILER_IS_GNUCXX) 42 | set (MY_COMPILER gcc) 43 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang") 44 | set (MY_COMPILER clang) 45 | elseif ("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Intel") 46 | set (MY_COMPILER icc) 47 | else() 48 | set (MY_COMPILER ${CMAKE_GENERATOR}) 49 | string (REGEX REPLACE " " "" COMPILER ${MY_COMPILER}) 50 | endif() 51 | set (MY_PLATFORM_AND_COMPILER "${MY_PLATFORM}/${MY_COMPILER}") 52 | message (STATUS "MY_PLATFORM_AND_COMPILER=${MY_PLATFORM_AND_COMPILER}") 53 | 54 | set (OpenCASCADE_DIR "" CACHE PATH "Path to Open CASCADE libraries.") 55 | 56 | # default paths 57 | set (OpenCASCADE_INCLUDE_DIR "${OpenCASCADE_DIR}/inc") 58 | set (OpenCASCADE_LIBRARY_DIR "${OpenCASCADE_DIR}/${MY_PLATFORM_AND_COMPILER}/lib") 59 | set (OpenCASCADE_BINARY_DIR "${OpenCASCADE_DIR}/${MY_PLATFORM_AND_COMPILER}/bin") 60 | set (OpenCASCADE_RESOURCE_DIR "${OpenCASCADE_DIR}/src") 61 | 62 | # complete list of OCCT Toolkits (copy-paste from adm/UDLIST, since installed OCCT does not include UDLIST) 63 | set (OpenCASCADE_TKLIST "") 64 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKernel TKMath) # FoundationClasses 65 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKG2d TKG3d TKGeomBase TKBRep) # ModelingData 66 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKGeomAlgo TKTopAlgo TKPrim TKBO TKBool TKHLR TKFillet TKOffset TKFeat TKMesh TKXMesh TKShHealing) # ModelingAlgorithms 67 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKService TKV3d TKOpenGl TKMeshVS TKIVtk TKD3DHost) # Visualization 68 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKCDF TKLCAF TKCAF TKBinL TKXmlL TKBin TKXml TKStdL TKStd TKTObj TKBinTObj TKXmlTObj TKVCAF) # ApplicationFramework 69 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKXSBase TKSTEPBase TKSTEPAttr TKSTEP209 TKSTEP TKIGES TKXCAF TKXDEIGES TKXDESTEP TKSTL TKVRML TKXmlXCAF TKBinXCAF TKRWMesh) # DataExchange 70 | set (OpenCASCADE_TKLIST ${OpenCASCADE_TKLIST} TKDraw TKViewerTest TKTopTest TKOpenGlTest) # Draw 71 | 72 | # validate location of OCCT libraries and headers 73 | set (OpenCASCADE_INCLUDE_DIR_FOUND) 74 | set (OpenCASCADE_LIBRARY_DIR_FOUND) 75 | set (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND) 76 | set (OpenCASCADE_IMPLIB_SUFFIX ${CMAKE_STATIC_LIBRARY_SUFFIX}) 77 | set (OpenCASCADE_SHAREDLIB_RELEASE_FOUND) 78 | set (OpenCASCADE_SHAREDLIB_DEBUG_FOUND) 79 | if (EXISTS "${OpenCASCADE_INCLUDE_DIR}/Standard.hxx") 80 | set (OpenCASCADE_INCLUDE_DIR_FOUND ON) 81 | endif() 82 | 83 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_STATIC_LIBRARY_SUFFIX}") 84 | set (OpenCASCADE_LIBRARY_DIR_FOUND ON) 85 | elseif (NOT WIN32 AND EXISTS "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}") 86 | set (OpenCASCADE_LIBRARY_DIR_FOUND ON) 87 | set (OpenCASCADE_IMPLIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) 88 | endif() 89 | 90 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_STATIC_LIBRARY_SUFFIX}") 91 | set (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND ON) 92 | elseif (NOT WIN32 AND EXISTS "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}") 93 | set (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND ON) 94 | set (OpenCASCADE_IMPLIB_SUFFIX ${CMAKE_SHARED_LIBRARY_SUFFIX}) 95 | elseif (OpenCASCADE_LIBRARY_DIR_FOUND) 96 | message (STATUS "Only release OpenCASCADE libraries have been found") 97 | endif() 98 | 99 | if (NOT OpenCASCADE_LIBRARY_DIR_FOUND AND OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND) 100 | set (OpenCASCADE_LIBRARY_DIR_FOUND ON) 101 | message (WARNING "Only debug OpenCASCADE libraries have been found") 102 | endif() 103 | 104 | if (WIN32) 105 | if (EXISTS "${OpenCASCADE_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}") 106 | set (OpenCASCADE_SHAREDLIB_RELEASE_FOUND ON) 107 | endif() 108 | if (EXISTS "${OpenCASCADE_BINARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}") 109 | set (OpenCASCADE_SHAREDLIB_DEBUG_FOUND ON) 110 | endif() 111 | else() 112 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}") 113 | set (OpenCASCADE_SHAREDLIB_RELEASE_FOUND ON) 114 | endif() 115 | if (EXISTS "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}TKernel${CMAKE_SHARED_LIBRARY_SUFFIX}") 116 | set (OpenCASCADE_SHAREDLIB_DEBUG_FOUND ON) 117 | endif() 118 | endif() 119 | 120 | if (OpenCASCADE_INCLUDE_DIR_FOUND AND OpenCASCADE_LIBRARY_DIR_FOUND) 121 | set (OpenCASCADE_FOUND ON) 122 | set (OpenCASCADE_INSTALL_PREFIX ${OpenCASCADE_DIR}) 123 | 124 | # Define OCCT toolkits so that CMake can put absolute paths to linker; 125 | # the library existence is not checked here, since modules can be disabled. 126 | foreach (aLibIter ${OpenCASCADE_TKLIST}) 127 | add_library (${aLibIter} SHARED IMPORTED) 128 | 129 | set_property (TARGET ${aLibIter} APPEND PROPERTY IMPORTED_CONFIGURATIONS RELEASE) 130 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_IMPLIB_RELEASE "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${OpenCASCADE_IMPLIB_SUFFIX}") 131 | if (OpenCASCADE_SHAREDLIB_RELEASE_FOUND) 132 | if (WIN32) 133 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_RELEASE "${OpenCASCADE_BINARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}") 134 | else() 135 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_RELEASE "${OpenCASCADE_LIBRARY_DIR}/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}") 136 | endif() 137 | endif() 138 | 139 | if (OpenCASCADE_LIBRARY_DEBUG_DIR_FOUND) 140 | set_property (TARGET ${aLibIter} APPEND PROPERTY IMPORTED_CONFIGURATIONS DEBUG) 141 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_IMPLIB_DEBUG "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${OpenCASCADE_IMPLIB_SUFFIX}") 142 | if (OpenCASCADE_SHAREDLIB_DEBUG_FOUND) 143 | if (WIN32) 144 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_DEBUG "${OpenCASCADE_BINARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}") 145 | else() 146 | set_target_properties (${aLibIter} PROPERTIES IMPORTED_LOCATION_DEBUG "${OpenCASCADE_LIBRARY_DIR}d/${CMAKE_SHARED_LIBRARY_PREFIX}${aLibIter}${CMAKE_SHARED_LIBRARY_SUFFIX}") 147 | endif() 148 | endif() 149 | endif() 150 | endforeach() 151 | else() 152 | # fallback searching for CMake configs 153 | if (NOT "${OpenCASCADE_DIR}" STREQUAL "") 154 | set (anOcctDirBak "${OpenCASCADE_DIR}") 155 | find_package (OpenCASCADE CONFIG QUIET PATHS "${OpenCASCADE_DIR}" NO_DEFAULT_PATH) 156 | set (OpenCASCADE_DIR "${anOcctDirBak}" CACHE PATH "Path to Open CASCADE libraries." FORCE) 157 | else() 158 | find_package (OpenCASCADE CONFIG QUIET) 159 | endif() 160 | endif() 161 | -------------------------------------------------------------------------------- /occt-qwidget/OcctQWidgetViewer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #ifdef _WIN32 4 | // should be included before other headers to avoid missing definitions 5 | #include 6 | #endif 7 | 8 | #include "OcctQWidgetViewer.h" 9 | 10 | #include "../occt-qt-tools/OcctQtTools.h" 11 | #include "../occt-qt-tools/OcctGlTools.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | 26 | #if !defined(__APPLE__) && !defined(_WIN32) && defined(__has_include) 27 | #if __has_include() 28 | #include 29 | #define USE_XW_DISPLAY 30 | #endif 31 | #endif 32 | #ifndef USE_XW_DISPLAY 33 | typedef Aspect_DisplayConnection Xw_DisplayConnection; 34 | #endif 35 | 36 | // ================================================================ 37 | // Function : OcctQWidgetViewer 38 | // ================================================================ 39 | OcctQWidgetViewer::OcctQWidgetViewer(QWidget* theParent) 40 | : QWidget(theParent) 41 | { 42 | Handle(Aspect_DisplayConnection) aDisp = new Xw_DisplayConnection(); 43 | Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver(aDisp, false); 44 | 45 | // create viewer 46 | myViewer = new V3d_Viewer(aDriver); 47 | myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK); 48 | myViewer->SetDefaultLights(); 49 | myViewer->SetLightOn(); 50 | myViewer->ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines); 51 | 52 | // create AIS context 53 | myContext = new AIS_InteractiveContext(myViewer); 54 | 55 | myViewCube = new AIS_ViewCube(); 56 | myViewCube->SetViewAnimation(myViewAnimation); 57 | myViewCube->SetFixedAnimationLoop(false); 58 | myViewCube->SetAutoStartAnimation(true); 59 | myViewCube->TransformPersistence()->SetOffset2d(Graphic3d_Vec2i(100, 150)); 60 | 61 | // note - window will be created later within initializeGL() callback! 62 | myView = myViewer->CreateView(); 63 | myView->SetImmediateUpdate(false); 64 | #ifndef __APPLE__ 65 | myView->ChangeRenderingParams().NbMsaaSamples = 4; // warning - affects performance 66 | #endif 67 | myView->ChangeRenderingParams().ToShowStats = true; 68 | // NOLINTNEXTLINE 69 | myView->ChangeRenderingParams().CollectedStats = (Graphic3d_RenderingParams::PerfCounters)( 70 | Graphic3d_RenderingParams::PerfCounters_FrameRate | Graphic3d_RenderingParams::PerfCounters_Triangles); 71 | 72 | // Qt widget setup 73 | setAttribute(Qt::WA_PaintOnScreen); 74 | setAttribute(Qt::WA_NoSystemBackground); 75 | setAttribute(Qt::WA_NativeWindow); // request native window for this widget to create OpenGL context 76 | setAttribute(Qt::WA_AcceptTouchEvents); // necessary to receive QTouchEvent events 77 | setMouseTracking(true); 78 | setBackgroundRole(QPalette::NoRole); // or NoBackground 79 | setFocusPolicy(Qt::StrongFocus); // set focus policy to threat QContextMenuEvent from keyboard 80 | setUpdatesEnabled(true); 81 | 82 | initializeGL(); 83 | } 84 | 85 | // ================================================================ 86 | // Function : ~OcctQWidgetViewer 87 | // ================================================================ 88 | OcctQWidgetViewer::~OcctQWidgetViewer() 89 | { 90 | Handle(Aspect_DisplayConnection) aDisp = myViewer->Driver()->GetDisplayConnection(); 91 | 92 | // release OCCT viewer 93 | myContext->RemoveAll(false); 94 | myContext.Nullify(); 95 | myView->Remove(); 96 | myView.Nullify(); 97 | myViewer.Nullify(); 98 | 99 | aDisp.Nullify(); 100 | } 101 | 102 | // ================================================================ 103 | // Function : event 104 | // ================================================================ 105 | bool OcctQWidgetViewer::event(QEvent* theEvent) 106 | { 107 | if (myView.IsNull()) 108 | return QWidget::event(theEvent); 109 | 110 | if (theEvent->type() == QEvent::TouchBegin 111 | || theEvent->type() == QEvent::TouchUpdate 112 | || theEvent->type() == QEvent::TouchEnd) 113 | { 114 | theEvent->accept(); 115 | myHasTouchInput = true; 116 | if (OcctQtTools::qtHandleTouchEvent(*this, myView, static_cast(theEvent))) 117 | updateView(); 118 | 119 | return true; 120 | } 121 | 122 | return QWidget::event(theEvent); 123 | } 124 | 125 | // ================================================================ 126 | // Function : closeEvent 127 | // ================================================================ 128 | void OcctQWidgetViewer::closeEvent(QCloseEvent* theEvent) 129 | { 130 | theEvent->accept(); 131 | } 132 | 133 | // ================================================================ 134 | // Function : keyPressEvent 135 | // ================================================================ 136 | void OcctQWidgetViewer::keyPressEvent(QKeyEvent* theEvent) 137 | { 138 | if (myView.IsNull()) 139 | return; 140 | 141 | const Aspect_VKey aKey = OcctQtTools::qtKey2VKey(theEvent->key()); 142 | switch (aKey) 143 | { 144 | case Aspect_VKey_Escape: 145 | { 146 | QApplication::exit(); 147 | return; 148 | } 149 | case Aspect_VKey_F: 150 | { 151 | myView->FitAll(0.01, false); 152 | update(); 153 | theEvent->accept(); 154 | return; 155 | } 156 | } 157 | QWidget::keyPressEvent(theEvent); 158 | } 159 | 160 | // ================================================================ 161 | // Function : mousePressEvent 162 | // ================================================================ 163 | void OcctQWidgetViewer::mousePressEvent(QMouseEvent* theEvent) 164 | { 165 | QWidget::mousePressEvent(theEvent); 166 | if (myView.IsNull()) 167 | return; 168 | 169 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem) 170 | return; // skip mouse events emulated by system from screen touches 171 | 172 | theEvent->accept(); 173 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 174 | updateView(); 175 | } 176 | 177 | // ================================================================ 178 | // Function : mouseReleaseEvent 179 | // ================================================================ 180 | void OcctQWidgetViewer::mouseReleaseEvent(QMouseEvent* theEvent) 181 | { 182 | QWidget::mouseReleaseEvent(theEvent); 183 | if (myView.IsNull()) 184 | return; 185 | 186 | theEvent->accept(); 187 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 188 | updateView(); 189 | } 190 | 191 | // ================================================================ 192 | // Function : mouseMoveEvent 193 | // ================================================================ 194 | void OcctQWidgetViewer::mouseMoveEvent(QMouseEvent* theEvent) 195 | { 196 | QWidget::mouseMoveEvent(theEvent); 197 | if (myView.IsNull()) 198 | return; 199 | 200 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem) 201 | return; // skip mouse events emulated by system from screen touches 202 | 203 | theEvent->accept(); 204 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 205 | updateView(); 206 | } 207 | 208 | // ============================================================================== 209 | // function : wheelEvent 210 | // ============================================================================== 211 | void OcctQWidgetViewer::wheelEvent(QWheelEvent* theEvent) 212 | { 213 | QWidget::wheelEvent(theEvent); 214 | if (myView.IsNull()) 215 | return; 216 | 217 | theEvent->accept(); 218 | #if (OCC_VERSION_HEX >= 0x070700) 219 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) 220 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y()); 221 | #else 222 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y()); 223 | #endif 224 | const Graphic3d_Vec2i aPnt2i(myView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5)); 225 | if (!myView->Subviews().IsEmpty()) 226 | { 227 | Handle(V3d_View) aPickedView = myView->PickSubview(aPnt2i); 228 | if (!aPickedView.IsNull() && aPickedView != myFocusView) 229 | { 230 | // switch input focus to another subview 231 | OnSubviewChanged(myContext, myFocusView, aPickedView); 232 | updateView(); 233 | return; 234 | } 235 | } 236 | #endif 237 | 238 | if (OcctQtTools::qtHandleWheelEvent(*this, myView, theEvent)) 239 | updateView(); 240 | } 241 | 242 | // ======================================================================= 243 | // function : updateView 244 | // ======================================================================= 245 | void OcctQWidgetViewer::updateView() 246 | { 247 | QWidget::update(); 248 | // if (window() != NULL) { window()->update(); } 249 | } 250 | 251 | // ================================================================ 252 | // Function : resizeEvent 253 | // ================================================================ 254 | void OcctQWidgetViewer::resizeEvent(QResizeEvent* ) 255 | { 256 | if (!myView.IsNull()) 257 | myView->MustBeResized(); 258 | } 259 | 260 | // ================================================================ 261 | // Function : handleViewRedraw 262 | // ================================================================ 263 | void OcctQWidgetViewer::handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, const Handle(V3d_View)& theView) 264 | { 265 | AIS_ViewController::handleViewRedraw(theCtx, theView); 266 | if (myToAskNextFrame) 267 | updateView(); // ask more frames for animation 268 | } 269 | 270 | #if (OCC_VERSION_HEX >= 0x070700) 271 | // ================================================================ 272 | // Function : OnSubviewChanged 273 | // ================================================================ 274 | void OcctQWidgetViewer::OnSubviewChanged(const Handle(AIS_InteractiveContext)&, 275 | const Handle(V3d_View)&, 276 | const Handle(V3d_View)& theNewView) 277 | { 278 | myFocusView = theNewView; 279 | } 280 | #endif 281 | 282 | // ================================================================ 283 | // Function : dumpGlInfo 284 | // ================================================================ 285 | void OcctQWidgetViewer::dumpGlInfo(bool theIsBasic, bool theToPrint) 286 | { 287 | TColStd_IndexedDataMapOfStringString aGlCapsDict; 288 | myView->DiagnosticInformation(aGlCapsDict, 289 | theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete); 290 | TCollection_AsciiString anInfo; 291 | for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter(aGlCapsDict); aValueIter.More(); aValueIter.Next()) 292 | { 293 | if (!aValueIter.Value().IsEmpty()) 294 | { 295 | if (!anInfo.IsEmpty()) 296 | anInfo += "\n"; 297 | 298 | anInfo += aValueIter.Key() + ": " + aValueIter.Value(); 299 | } 300 | } 301 | 302 | if (theToPrint) 303 | Message::SendInfo(anInfo); 304 | 305 | myGlInfo = QString::fromUtf8(anInfo.ToCString()); 306 | } 307 | 308 | // ================================================================ 309 | // Function : initializeGL 310 | // ================================================================ 311 | void OcctQWidgetViewer::initializeGL() 312 | { 313 | const QRect aRect = rect(); 314 | const double aDevPixRatio = devicePixelRatioF(); 315 | const Graphic3d_Vec2i aViewSize(Graphic3d_Vec2d(Round((aRect.right() - aRect.left()) * aDevPixRatio), 316 | Round((aRect.bottom() - aRect.top()) * aDevPixRatio))); 317 | 318 | const Aspect_Drawable aNativeWin = (Aspect_Drawable)winId(); 319 | 320 | Handle(OcctGlTools::OcctNeutralWindow) aWindow = Handle(OcctGlTools::OcctNeutralWindow)::DownCast(myView->Window()); 321 | const bool isFirstInit = aWindow.IsNull(); 322 | if (aWindow.IsNull()) 323 | { 324 | aWindow = new OcctGlTools::OcctNeutralWindow(); 325 | aWindow->SetVirtual(true); 326 | } 327 | aWindow->SetNativeHandle(aNativeWin); 328 | aWindow->SetSize(aViewSize.x(), aViewSize.y()); 329 | aWindow->SetDevicePixelRatio(aDevPixRatio); 330 | myView->SetWindow(aWindow); 331 | dumpGlInfo(true, true); 332 | #if (OCC_VERSION_HEX >= 0x070700) 333 | for (const Handle(V3d_View)& aSubviewIter : myView->Subviews()) 334 | { 335 | aSubviewIter->MustBeResized(); 336 | aSubviewIter->Invalidate(); 337 | } 338 | #endif 339 | 340 | if (isFirstInit) 341 | { 342 | myContext->Display(myViewCube, 0, 0, false); 343 | 344 | // dummy shape for testing 345 | TopoDS_Shape aBox = BRepPrimAPI_MakeBox(100.0, 50.0, 90.0).Shape(); 346 | Handle(AIS_Shape) aShape = new AIS_Shape(aBox); 347 | myContext->Display(aShape, AIS_Shaded, 0, false); 348 | } 349 | } 350 | 351 | // ================================================================ 352 | // Function : paintEvent 353 | // ================================================================ 354 | void OcctQWidgetViewer::paintEvent(QPaintEvent* ) 355 | { 356 | if (myView.IsNull() || myView->Window().IsNull()) 357 | return; 358 | 359 | const double aDevPixelRatioOld = myView->Window()->DevicePixelRatio(); 360 | const double aDevPixelRatioNew = devicePixelRatioF(); 361 | if (myView->Window()->NativeHandle() != (Aspect_Drawable)winId()) 362 | { 363 | // workaround window recreation done by Qt on monitor (QScreen) disconnection 364 | Message::SendWarning() << "Native window handle has changed by QWidget!"; 365 | initializeGL(); 366 | } 367 | else if (aDevPixelRatioNew != aDevPixelRatioOld) 368 | { 369 | initializeGL(); 370 | } 371 | else 372 | { 373 | Graphic3d_Vec2i aViewSizeOld; myView->Window()->Size(aViewSizeOld.x(), aViewSizeOld.y()); 374 | 375 | const QRect aRect = rect(); 376 | Graphic3d_Vec2i aViewSizeNew(Graphic3d_Vec2d(Round((aRect.right() - aRect.left()) * aDevPixelRatioNew), 377 | Round((aRect.bottom() - aRect.top()) * aDevPixelRatioNew))); 378 | if (aViewSizeNew != aViewSizeOld) 379 | { 380 | Handle(OcctGlTools::OcctNeutralWindow) aWindow = Handle(OcctGlTools::OcctNeutralWindow)::DownCast(myView->Window()); 381 | aWindow->SetSize(aViewSizeNew.x(), aViewSizeNew.y()); 382 | myView->MustBeResized(); 383 | myView->Invalidate(); 384 | dumpGlInfo(true, false); 385 | 386 | #if (OCC_VERSION_HEX >= 0x070700) 387 | for (const Handle(V3d_View)& aSubviewIter : myView->Subviews()) 388 | { 389 | aSubviewIter->MustBeResized(); 390 | aSubviewIter->Invalidate(); 391 | } 392 | #endif 393 | } 394 | } 395 | 396 | // flush pending input events and redraw the viewer 397 | Handle(V3d_View) aView = !myFocusView.IsNull() ? myFocusView : myView; 398 | aView->InvalidateImmediate(); 399 | AIS_ViewController::FlushViewEvents(myContext, aView, true); 400 | } 401 | -------------------------------------------------------------------------------- /occt-qopenglwidget/OcctQOpenGLWidgetViewer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Kirill Gavrilov 2 | 3 | #ifdef _WIN32 4 | // should be included before other headers to avoid missing definitions 5 | #include 6 | #endif 7 | #include 8 | 9 | #include "OcctQOpenGLWidgetViewer.h" 10 | 11 | #include "../occt-qt-tools/OcctGlTools.h" 12 | #include "../occt-qt-tools/OcctQtTools.h" 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | 20 | #include 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | 27 | #if !defined(__APPLE__) && !defined(_WIN32) && defined(__has_include) 28 | #if __has_include() 29 | #include 30 | #define USE_XW_DISPLAY 31 | #endif 32 | #endif 33 | #ifndef USE_XW_DISPLAY 34 | typedef Aspect_DisplayConnection Xw_DisplayConnection; 35 | #endif 36 | 37 | // ================================================================ 38 | // Function : OcctQOpenGLWidgetViewer 39 | // ================================================================ 40 | OcctQOpenGLWidgetViewer::OcctQOpenGLWidgetViewer(QWidget* theParent) 41 | : QOpenGLWidget(theParent) 42 | { 43 | Handle(Aspect_DisplayConnection) aDisp = new Xw_DisplayConnection(); 44 | Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver(aDisp, false); 45 | // lets QOpenGLWidget to manage buffer swap 46 | aDriver->ChangeOptions().buffersNoSwap = true; 47 | // don't write into alpha channel 48 | aDriver->ChangeOptions().buffersOpaqueAlpha = true; 49 | // offscreen FBOs should be always used 50 | aDriver->ChangeOptions().useSystemBuffer = false; 51 | 52 | // create viewer 53 | myViewer = new V3d_Viewer(aDriver); 54 | myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK); 55 | myViewer->SetDefaultLights(); 56 | myViewer->SetLightOn(); 57 | myViewer->ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines); 58 | 59 | // create AIS context 60 | myContext = new AIS_InteractiveContext(myViewer); 61 | 62 | myViewCube = new AIS_ViewCube(); 63 | myViewCube->SetViewAnimation(myViewAnimation); 64 | myViewCube->SetFixedAnimationLoop(false); 65 | myViewCube->SetAutoStartAnimation(true); 66 | myViewCube->TransformPersistence()->SetOffset2d(Graphic3d_Vec2i(100, 150)); 67 | 68 | // note - window will be created later within initializeGL() callback! 69 | myView = myViewer->CreateView(); 70 | myView->SetImmediateUpdate(false); 71 | #ifndef __APPLE__ 72 | myView->ChangeRenderingParams().NbMsaaSamples = 4; // warning - affects performance 73 | #endif 74 | myView->ChangeRenderingParams().ToShowStats = true; 75 | // NOLINTNEXTLINE 76 | myView->ChangeRenderingParams().CollectedStats = (Graphic3d_RenderingParams::PerfCounters)( 77 | Graphic3d_RenderingParams::PerfCounters_FrameRate | Graphic3d_RenderingParams::PerfCounters_Triangles); 78 | 79 | // Qt widget setup 80 | setAttribute(Qt::WA_AcceptTouchEvents); // necessary to receive QTouchEvent events 81 | setMouseTracking(true); 82 | setBackgroundRole(QPalette::NoRole); // or NoBackground 83 | setFocusPolicy(Qt::StrongFocus); // set focus policy to threat QContextMenuEvent from keyboard 84 | setUpdatesEnabled(true); 85 | setUpdateBehavior(QOpenGLWidget::NoPartialUpdate); 86 | 87 | // OpenGL setup managed by Qt - it is better to do this globally 88 | // via QSurfaceFormat::setDefaultFormat() - see main() function 89 | //const QSurfaceFormat aGlFormat = OcctQtTools::qtGlSurfaceFormat(); 90 | //setFormat(aGlFormat); 91 | } 92 | 93 | // ================================================================ 94 | // Function : ~OcctQOpenGLWidgetViewer 95 | // ================================================================ 96 | OcctQOpenGLWidgetViewer::~OcctQOpenGLWidgetViewer() 97 | { 98 | // hold on X11 display connection till making another connection active by glXMakeCurrent() 99 | // to workaround sudden crash in QOpenGLWidget destructor 100 | Handle(Aspect_DisplayConnection) aDisp = myViewer->Driver()->GetDisplayConnection(); 101 | 102 | // release OCCT viewer 103 | myContext->RemoveAll(false); 104 | myContext.Nullify(); 105 | myView->Remove(); 106 | myView.Nullify(); 107 | myViewer.Nullify(); 108 | 109 | // make active OpenGL context created by Qt 110 | makeCurrent(); 111 | aDisp.Nullify(); 112 | } 113 | 114 | // ================================================================ 115 | // Function : event 116 | // ================================================================ 117 | bool OcctQOpenGLWidgetViewer::event(QEvent* theEvent) 118 | { 119 | if (myView.IsNull()) 120 | return QOpenGLWidget::event(theEvent); 121 | 122 | if (theEvent->type() == QEvent::TouchBegin 123 | || theEvent->type() == QEvent::TouchUpdate 124 | || theEvent->type() == QEvent::TouchEnd) 125 | { 126 | theEvent->accept(); 127 | myHasTouchInput = true; 128 | if (OcctQtTools::qtHandleTouchEvent(*this, myView, static_cast(theEvent))) 129 | updateView(); 130 | 131 | return true; 132 | } 133 | 134 | return QOpenGLWidget::event(theEvent); 135 | } 136 | 137 | // ================================================================ 138 | // Function : closeEvent 139 | // ================================================================ 140 | void OcctQOpenGLWidgetViewer::closeEvent(QCloseEvent* theEvent) 141 | { 142 | theEvent->accept(); 143 | } 144 | 145 | // ================================================================ 146 | // Function : keyPressEvent 147 | // ================================================================ 148 | void OcctQOpenGLWidgetViewer::keyPressEvent(QKeyEvent* theEvent) 149 | { 150 | if (myView.IsNull()) 151 | return; 152 | 153 | const Aspect_VKey aKey = OcctQtTools::qtKey2VKey(theEvent->key()); 154 | switch (aKey) 155 | { 156 | case Aspect_VKey_Escape: { 157 | QApplication::exit(); 158 | return; 159 | } 160 | case Aspect_VKey_F: { 161 | myView->FitAll(0.01, false); 162 | update(); 163 | theEvent->accept(); 164 | return; 165 | } 166 | } 167 | QOpenGLWidget::keyPressEvent(theEvent); 168 | } 169 | 170 | // ================================================================ 171 | // Function : mousePressEvent 172 | // ================================================================ 173 | void OcctQOpenGLWidgetViewer::mousePressEvent(QMouseEvent* theEvent) 174 | { 175 | QOpenGLWidget::mousePressEvent(theEvent); 176 | if (myView.IsNull()) 177 | return; 178 | 179 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem) 180 | return; // skip mouse events emulated by system from screen touches 181 | 182 | theEvent->accept(); 183 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 184 | updateView(); 185 | } 186 | 187 | // ================================================================ 188 | // Function : mouseReleaseEvent 189 | // ================================================================ 190 | void OcctQOpenGLWidgetViewer::mouseReleaseEvent(QMouseEvent* theEvent) 191 | { 192 | QOpenGLWidget::mouseReleaseEvent(theEvent); 193 | if (myView.IsNull()) 194 | return; 195 | 196 | theEvent->accept(); 197 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 198 | updateView(); 199 | } 200 | 201 | // ================================================================ 202 | // Function : mouseMoveEvent 203 | // ================================================================ 204 | void OcctQOpenGLWidgetViewer::mouseMoveEvent(QMouseEvent* theEvent) 205 | { 206 | QOpenGLWidget::mouseMoveEvent(theEvent); 207 | if (myView.IsNull()) 208 | return; 209 | 210 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem) 211 | return; // skip mouse events emulated by system from screen touches 212 | 213 | theEvent->accept(); 214 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 215 | updateView(); 216 | } 217 | 218 | // ============================================================================== 219 | // function : wheelEvent 220 | // ============================================================================== 221 | void OcctQOpenGLWidgetViewer::wheelEvent(QWheelEvent* theEvent) 222 | { 223 | QOpenGLWidget::wheelEvent(theEvent); 224 | if (myView.IsNull()) 225 | return; 226 | 227 | theEvent->accept(); 228 | 229 | #if (OCC_VERSION_HEX >= 0x070700) 230 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) 231 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y()); 232 | #else 233 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y()); 234 | #endif 235 | const Graphic3d_Vec2i aPnt2i(myView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5)); 236 | if (!myView->Subviews().IsEmpty()) 237 | { 238 | Handle(V3d_View) aPickedView = myView->PickSubview(aPnt2i); 239 | if (!aPickedView.IsNull() && aPickedView != myFocusView) 240 | { 241 | // switch input focus to another subview 242 | OnSubviewChanged(myContext, myFocusView, aPickedView); 243 | updateView(); 244 | return; 245 | } 246 | } 247 | #endif 248 | 249 | if (OcctQtTools::qtHandleWheelEvent(*this, myView, theEvent)) 250 | updateView(); 251 | } 252 | 253 | // ======================================================================= 254 | // function : updateView 255 | // ======================================================================= 256 | void OcctQOpenGLWidgetViewer::updateView() 257 | { 258 | update(); 259 | // if (window() != NULL) { window()->update(); } 260 | } 261 | 262 | // ================================================================ 263 | // Function : handleViewRedraw 264 | // ================================================================ 265 | void OcctQOpenGLWidgetViewer::handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, 266 | const Handle(V3d_View)& theView) 267 | { 268 | AIS_ViewController::handleViewRedraw(theCtx, theView); 269 | if (myToAskNextFrame) 270 | updateView(); // ask more frames for animation 271 | } 272 | 273 | #if (OCC_VERSION_HEX >= 0x070700) 274 | // ================================================================ 275 | // Function : OnSubviewChanged 276 | // ================================================================ 277 | void OcctQOpenGLWidgetViewer::OnSubviewChanged(const Handle(AIS_InteractiveContext)&, 278 | const Handle(V3d_View)&, 279 | const Handle(V3d_View)& theNewView) 280 | { 281 | myFocusView = theNewView; 282 | } 283 | #endif 284 | 285 | // ================================================================ 286 | // Function : dumpGlInfo 287 | // ================================================================ 288 | void OcctQOpenGLWidgetViewer::dumpGlInfo(bool theIsBasic, bool theToPrint) 289 | { 290 | TColStd_IndexedDataMapOfStringString aGlCapsDict; 291 | myView->DiagnosticInformation(aGlCapsDict, 292 | theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete); 293 | TCollection_AsciiString anInfo; 294 | for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter(aGlCapsDict); aValueIter.More(); aValueIter.Next()) 295 | { 296 | if (!aValueIter.Value().IsEmpty()) 297 | { 298 | if (!anInfo.IsEmpty()) 299 | anInfo += "\n"; 300 | 301 | anInfo += aValueIter.Key() + ": " + aValueIter.Value(); 302 | } 303 | } 304 | 305 | if (theToPrint) 306 | Message::SendInfo(anInfo); 307 | 308 | myGlInfo = QString::fromUtf8(anInfo.ToCString()); 309 | } 310 | 311 | // ================================================================ 312 | // Function : initializeGL 313 | // ================================================================ 314 | void OcctQOpenGLWidgetViewer::initializeGL() 315 | { 316 | Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast(myViewer->Driver()); 317 | OcctQtTools::qtGlCapsFromSurfaceFormat(aDriver->ChangeOptions(), format()); 318 | 319 | const Aspect_Drawable aNativeWin = (Aspect_Drawable)effectiveWinId(); 320 | const Graphic3d_Vec2i aViewSize(rect().right() - rect().left(), rect().bottom() - rect().top()); 321 | 322 | const bool isFirstInit = myView->Window().IsNull(); 323 | if (!OcctGlTools::InitializeGlWindow(myView, aNativeWin, aViewSize, devicePixelRatioF())) 324 | { 325 | QMessageBox::critical(0, "Failure", "OpenGl_Context is unable to wrap OpenGL context"); 326 | QApplication::exit(1); 327 | return; 328 | } 329 | 330 | makeCurrent(); // restore Qt framebuffer 331 | dumpGlInfo(true, true); 332 | if (isFirstInit) 333 | { 334 | myContext->Display(myViewCube, 0, 0, false); 335 | 336 | // dummy shape for testing 337 | TopoDS_Shape aBox = BRepPrimAPI_MakeBox(100.0, 50.0, 90.0).Shape(); 338 | Handle(AIS_Shape) aShape = new AIS_Shape(aBox); 339 | myContext->Display(aShape, AIS_Shaded, 0, false); 340 | } 341 | } 342 | 343 | // ================================================================ 344 | // Function : paintGL 345 | // ================================================================ 346 | void OcctQOpenGLWidgetViewer::paintGL() 347 | { 348 | if (myView.IsNull() || myView->Window().IsNull()) 349 | return; 350 | 351 | const double aDevPixelRatioOld = myView->Window()->DevicePixelRatio(); 352 | if (myView->Window()->NativeHandle() != OcctGlTools::GetGlNativeWindow((Aspect_Drawable)effectiveWinId())) 353 | { 354 | // workaround window recreation done by Qt on monitor (QScreen) disconnection 355 | Message::SendWarning() << "Native window handle has changed by QOpenGLWidget!"; 356 | initializeGL(); 357 | } 358 | else if (devicePixelRatioF() != aDevPixelRatioOld) 359 | { 360 | initializeGL(); 361 | } 362 | 363 | Graphic3d_Vec2i aViewSizeOld; myView->Window()->Size(aViewSizeOld.x(), aViewSizeOld.y()); 364 | 365 | // wrap FBO created by QOpenGLFramebufferObject 366 | if (!OcctGlTools::InitializeGlFbo(myView)) 367 | { 368 | QMessageBox::critical(0, "Failure", "Default FBO wrapper creation failed"); 369 | QApplication::exit(1); 370 | return; 371 | } 372 | 373 | Graphic3d_Vec2i aViewSizeNew; myView->Window()->Size(aViewSizeNew.x(), aViewSizeNew.y()); 374 | if (aViewSizeNew != aViewSizeOld || myView->Window()->DevicePixelRatio() != aDevPixelRatioOld) 375 | dumpGlInfo(true, false); 376 | 377 | // reset global GL state from Qt before redrawing OCCT 378 | OcctGlTools::ResetGlStateBeforeOcct(myView); 379 | 380 | // flush pending input events and redraw the viewer 381 | Handle(V3d_View) aView = !myFocusView.IsNull() ? myFocusView : myView; 382 | aView->InvalidateImmediate(); 383 | AIS_ViewController::FlushViewEvents(myContext, aView, true); 384 | 385 | // reset global GL state after OCCT before redrawing Qt 386 | OcctGlTools::ResetGlStateAfterOcct(myView); 387 | } 388 | -------------------------------------------------------------------------------- /occt-qtquick/OcctQQuickFramebufferViewer.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2023 Kirill Gavrilov 2 | 3 | #ifdef _WIN32 4 | // should be included before other headers to avoid missing definitions 5 | #include 6 | #endif 7 | #include 8 | 9 | #include "OcctQQuickFramebufferViewer.h" 10 | 11 | #include "../occt-qt-tools/OcctGlTools.h" 12 | 13 | #include 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | 21 | #include 22 | #include 23 | #include 24 | #include 25 | #include 26 | #include 27 | 28 | #if !defined(__APPLE__) && !defined(_WIN32) && defined(__has_include) 29 | #if __has_include() 30 | #include 31 | #define USE_XW_DISPLAY 32 | #endif 33 | #endif 34 | #ifndef USE_XW_DISPLAY 35 | typedef Aspect_DisplayConnection Xw_DisplayConnection; 36 | #endif 37 | 38 | // ================================================================ 39 | // Function : OcctQQuickFramebufferViewer 40 | // ================================================================ 41 | OcctQQuickFramebufferViewer::OcctQQuickFramebufferViewer(QQuickItem* theParent) 42 | : QQuickFramebufferObject(theParent) 43 | { 44 | Handle(Aspect_DisplayConnection) aDisp = new Xw_DisplayConnection(); 45 | Handle(OpenGl_GraphicDriver) aDriver = new OpenGl_GraphicDriver(aDisp, false); 46 | // lets QtQuick to manage buffer swap 47 | aDriver->ChangeOptions().buffersNoSwap = true; 48 | // don't write into alpha channel 49 | aDriver->ChangeOptions().buffersOpaqueAlpha = true; 50 | // offscreen FBOs should be always used 51 | aDriver->ChangeOptions().useSystemBuffer = false; 52 | 53 | // create viewer 54 | myViewer = new V3d_Viewer(aDriver); 55 | myViewer->SetDefaultBackgroundColor(Quantity_NOC_BLACK); 56 | myViewer->SetDefaultLights(); 57 | myViewer->SetLightOn(); 58 | myViewer->ActivateGrid(Aspect_GT_Rectangular, Aspect_GDM_Lines); 59 | 60 | // create AIS context 61 | myContext = new AIS_InteractiveContext(myViewer); 62 | 63 | myViewCube = new AIS_ViewCube(); 64 | myViewCube->SetViewAnimation(myViewAnimation); 65 | myViewCube->SetFixedAnimationLoop(false); 66 | myViewCube->SetAutoStartAnimation(true); 67 | myViewCube->TransformPersistence()->SetOffset2d(Graphic3d_Vec2i(100, 150)); 68 | 69 | // note - window will be created later within initializeGL() callback! 70 | myView = myViewer->CreateView(); 71 | myView->SetImmediateUpdate(false); 72 | #ifndef __APPLE__ 73 | myView->ChangeRenderingParams().NbMsaaSamples = 4; // warning - affects performance 74 | #endif 75 | myView->ChangeRenderingParams().ToShowStats = true; 76 | // NOLINTNEXTLINE 77 | myView->ChangeRenderingParams().CollectedStats = (Graphic3d_RenderingParams::PerfCounters)( 78 | Graphic3d_RenderingParams::PerfCounters_FrameRate | Graphic3d_RenderingParams::PerfCounters_Triangles); 79 | 80 | // QtQuick item setup 81 | setAcceptedMouseButtons(Qt::AllButtons); 82 | //setAcceptTouchEvents(true); // necessary to receive QTouchEvent events 83 | setAcceptHoverEvents(true); 84 | setMirrorVertically(true); 85 | 86 | // GUI elements cannot be created from GL rendering thread - make queued connection 87 | connect(this, &OcctQQuickFramebufferViewer::glCriticalError, this, [this](QString theMsg) 88 | { 89 | QMessageBox::critical(0, "Critical error", theMsg); 90 | QApplication::exit(1); 91 | }, Qt::QueuedConnection); 92 | } 93 | 94 | // ================================================================ 95 | // Function : ~OcctQQuickFramebufferViewer 96 | // ================================================================ 97 | OcctQQuickFramebufferViewer::~OcctQQuickFramebufferViewer() 98 | { 99 | // hold on X11 display connection till making another connection active by glXMakeCurrent() 100 | // to workaround sudden crash in QOpenGLWidget destructor 101 | Handle(Aspect_DisplayConnection) aDisp = myViewer->Driver()->GetDisplayConnection(); 102 | 103 | // release OCCT viewer 104 | myContext->RemoveAll(false); 105 | myContext.Nullify(); 106 | myView->Remove(); 107 | myView.Nullify(); 108 | myViewer.Nullify(); 109 | 110 | // make active OpenGL context created by Qt 111 | //makeCurrent(); 112 | aDisp.Nullify(); 113 | } 114 | 115 | // ================================================================ 116 | // Function : event 117 | // ================================================================ 118 | bool OcctQQuickFramebufferViewer::event(QEvent* theEvent) 119 | { 120 | if (myView.IsNull()) 121 | return QQuickFramebufferObject::event(theEvent); 122 | 123 | if (theEvent->type() == QEvent::UpdateLater) 124 | { 125 | update(); 126 | theEvent->accept(); 127 | return true; 128 | } 129 | 130 | if (theEvent->type() == QEvent::TouchBegin 131 | || theEvent->type() == QEvent::TouchUpdate 132 | || theEvent->type() == QEvent::TouchEnd) 133 | { 134 | theEvent->accept(); 135 | myHasTouchInput = true; 136 | if (OcctQtTools::qtHandleTouchEvent(*this, myView, static_cast(theEvent))) 137 | updateView(); 138 | 139 | return true; 140 | } 141 | 142 | return QQuickFramebufferObject::event(theEvent); 143 | } 144 | 145 | // ================================================================ 146 | // Function : keyPressEvent 147 | // ================================================================ 148 | void OcctQQuickFramebufferViewer::keyPressEvent(QKeyEvent* theEvent) 149 | { 150 | if (myView.IsNull()) 151 | return; 152 | 153 | const Aspect_VKey aKey = OcctQtTools::qtKey2VKey(theEvent->key()); 154 | switch (aKey) 155 | { 156 | case Aspect_VKey_Escape: 157 | { 158 | QApplication::exit(); 159 | return; 160 | } 161 | case Aspect_VKey_F: 162 | { 163 | { 164 | Standard_Mutex::Sentry aLock(myViewerMutex); 165 | myView->FitAll(0.01, false); 166 | } 167 | update(); 168 | theEvent->accept(); 169 | return; 170 | } 171 | } 172 | QQuickFramebufferObject::keyPressEvent(theEvent); 173 | } 174 | 175 | // ================================================================ 176 | // Function : mousePressEvent 177 | // ================================================================ 178 | void OcctQQuickFramebufferViewer::mousePressEvent(QMouseEvent* theEvent) 179 | { 180 | QQuickFramebufferObject::mousePressEvent(theEvent); 181 | if (myView.IsNull()) 182 | return; 183 | 184 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem) 185 | return; // skip mouse events emulated by system from screen touches 186 | 187 | theEvent->accept(); 188 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 189 | updateView(); 190 | } 191 | 192 | // ================================================================ 193 | // Function : mouseReleaseEvent 194 | // ================================================================ 195 | void OcctQQuickFramebufferViewer::mouseReleaseEvent(QMouseEvent* theEvent) 196 | { 197 | QQuickFramebufferObject::mouseReleaseEvent(theEvent); 198 | if (myView.IsNull()) 199 | return; 200 | 201 | theEvent->accept(); 202 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 203 | updateView(); 204 | 205 | // take keyboard focus on mouse click 206 | setFocus(true); 207 | } 208 | 209 | // ================================================================ 210 | // Function : mouseMoveEvent 211 | // ================================================================ 212 | void OcctQQuickFramebufferViewer::mouseMoveEvent(QMouseEvent* theEvent) 213 | { 214 | QQuickFramebufferObject::mouseMoveEvent(theEvent); 215 | if (myView.IsNull()) 216 | return; 217 | 218 | if (myHasTouchInput && theEvent->source() == Qt::MouseEventSynthesizedBySystem) 219 | return; // skip mouse events emulated by system from screen touches 220 | 221 | theEvent->accept(); 222 | if (OcctQtTools::qtHandleMouseEvent(*this, myView, theEvent)) 223 | updateView(); 224 | } 225 | 226 | // ============================================================================== 227 | // function : wheelEvent 228 | // ============================================================================== 229 | void OcctQQuickFramebufferViewer::wheelEvent(QWheelEvent* theEvent) 230 | { 231 | QQuickFramebufferObject::wheelEvent(theEvent); 232 | if (myView.IsNull()) 233 | return; 234 | 235 | theEvent->accept(); 236 | if (OcctQtTools::qtHandleWheelEvent(*this, myView, theEvent)) 237 | updateView(); 238 | } 239 | 240 | // ================================================================ 241 | // Function : hoverMoveEvent 242 | // ================================================================ 243 | void OcctQQuickFramebufferViewer::hoverEnterEvent(QHoverEvent* theEvent) 244 | { 245 | QQuickFramebufferObject::hoverEnterEvent(theEvent); 246 | if (myView.IsNull()) 247 | return; 248 | 249 | theEvent->accept(); 250 | } 251 | 252 | // ================================================================ 253 | // Function : hoverMoveEvent 254 | // ================================================================ 255 | void OcctQQuickFramebufferViewer::hoverLeaveEvent(QHoverEvent* theEvent) 256 | { 257 | QQuickFramebufferObject::hoverLeaveEvent(theEvent); 258 | if (myView.IsNull()) 259 | return; 260 | 261 | theEvent->accept(); 262 | } 263 | 264 | // ================================================================ 265 | // Function : hoverMoveEvent 266 | // ================================================================ 267 | void OcctQQuickFramebufferViewer::hoverMoveEvent(QHoverEvent* theEvent) 268 | { 269 | QQuickFramebufferObject::hoverMoveEvent(theEvent); 270 | if (myView.IsNull()) 271 | return; 272 | 273 | theEvent->accept(); 274 | if (OcctQtTools::qtHandleHoverEvent(*this, myView, theEvent)) 275 | updateView(); 276 | } 277 | 278 | // ======================================================================= 279 | // Function : updateView 280 | // ======================================================================= 281 | void OcctQQuickFramebufferViewer::updateView() 282 | { 283 | update(); 284 | } 285 | 286 | // ================================================================ 287 | // Function : handleViewRedraw 288 | // ================================================================ 289 | void OcctQQuickFramebufferViewer::handleViewRedraw(const Handle(AIS_InteractiveContext)& theCtx, 290 | const Handle(V3d_View)& theView) 291 | { 292 | AIS_ViewController::handleViewRedraw(theCtx, theView); 293 | if (myToAskNextFrame) 294 | QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateLater)); // ask more frames for animation 295 | } 296 | 297 | // ======================================================================= 298 | // Function : createRenderer 299 | // ======================================================================= 300 | QQuickFramebufferObject::Renderer* OcctQQuickFramebufferViewer::createRenderer() const 301 | { 302 | return new Renderer(const_cast(this)); 303 | } 304 | 305 | // ================================================================ 306 | // Function : Renderer 307 | // ================================================================ 308 | OcctQQuickFramebufferViewer::Renderer::Renderer(OcctQQuickFramebufferViewer* theViewer) 309 | : myViewer(theViewer) 310 | { 311 | // 312 | } 313 | 314 | // ================================================================ 315 | // Function : ~Renderer 316 | // ================================================================ 317 | OcctQQuickFramebufferViewer::Renderer::~Renderer() 318 | { 319 | // 320 | } 321 | 322 | // ================================================================ 323 | // Function : createFramebufferObject 324 | // ================================================================ 325 | QOpenGLFramebufferObject* OcctQQuickFramebufferViewer::Renderer::createFramebufferObject(const QSize& theSize) 326 | { 327 | QOpenGLFramebufferObjectFormat aQFormat; 328 | aQFormat.setAttachment(QOpenGLFramebufferObject::CombinedDepthStencil); 329 | //aQFormat.setInternalTextureFormat(GL_RGBA8); 330 | //aQFormat.setSamples(4); do not create MSAA buffer here 331 | 332 | /*#if (QT_VERSION_MAJOR > 5) || (QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR >= 10) 333 | const QQuickWindow* aQWindow = window(); 334 | const QSurfaceFormat aQtGlFormat = aQWindow->format(); 335 | if (aQtGlFormat.colorSpace() == QSurfaceFormat::sRGBColorSpace) 336 | aQFormat.setInternalTextureFormat(GL_SRGB8_ALPHA8); 337 | #endif*/ 338 | 339 | return new QOpenGLFramebufferObject(theSize, aQFormat); 340 | } 341 | 342 | // ================================================================ 343 | // Function : synchronize 344 | // ================================================================ 345 | void OcctQQuickFramebufferViewer::Renderer::synchronize(QQuickFramebufferObject* theItem) 346 | { 347 | if (myViewer != theItem) 348 | std::cerr << "Error: unexpected synchronize\n"; 349 | 350 | if (myViewer != nullptr) 351 | myViewer->synchronize(framebufferObject()); 352 | } 353 | 354 | // ================================================================ 355 | // Function : render 356 | // ================================================================ 357 | void OcctQQuickFramebufferViewer::Renderer::render() 358 | { 359 | if (myViewer != nullptr) 360 | myViewer->render(framebufferObject()); 361 | } 362 | 363 | // ================================================================ 364 | // Function : dumpGlInfo 365 | // ================================================================ 366 | void OcctQQuickFramebufferViewer::dumpGlInfo(bool theIsBasic, bool theToPrint) 367 | { 368 | TColStd_IndexedDataMapOfStringString aGlCapsDict; 369 | myView->DiagnosticInformation(aGlCapsDict, 370 | theIsBasic ? Graphic3d_DiagnosticInfo_Basic : Graphic3d_DiagnosticInfo_Complete); 371 | TCollection_AsciiString anInfo; 372 | for (TColStd_IndexedDataMapOfStringString::Iterator aValueIter(aGlCapsDict); aValueIter.More(); aValueIter.Next()) 373 | { 374 | if (!aValueIter.Value().IsEmpty()) 375 | { 376 | if (!anInfo.IsEmpty()) 377 | anInfo += "\n"; 378 | 379 | anInfo += aValueIter.Key() + ": " + aValueIter.Value(); 380 | } 381 | } 382 | 383 | if (theToPrint) 384 | Message::SendInfo(anInfo); 385 | 386 | myGlInfo = QString::fromUtf8(anInfo.ToCString()); 387 | Q_EMIT glInfoChanged(); 388 | } 389 | 390 | // ================================================================ 391 | // Function : synchronize 392 | // ================================================================ 393 | void OcctQQuickFramebufferViewer::synchronize(QOpenGLFramebufferObject* ) 394 | { 395 | // this method will be called from GL rendering thread while GUI thread is locked, 396 | // the place to synchronize GUI / GL rendering states 397 | if (myGlBackColor.first) 398 | { 399 | myGlBackColor.first = false; 400 | const Quantity_Color aColor = OcctQtTools::qtColorToOcct(myGlBackColor.second); 401 | myView->SetBgGradientColors(aColor, Quantity_NOC_BLACK, Aspect_GradientFillMethod_Elliptical); 402 | myView->Invalidate(); 403 | } 404 | } 405 | 406 | // ================================================================ 407 | // Function : initializeGL 408 | // ================================================================ 409 | void OcctQQuickFramebufferViewer::initializeGL(QOpenGLFramebufferObject* theFbo) 410 | { 411 | // handle OCCT 3D Viewer initialization 412 | Standard_Mutex::Sentry aLock(myViewerMutex); 413 | const QQuickWindow* aQWindow = window(); 414 | if (theFbo == nullptr) 415 | return; 416 | 417 | Handle(OpenGl_GraphicDriver) aDriver = Handle(OpenGl_GraphicDriver)::DownCast(myViewer->Driver()); 418 | OcctQtTools::qtGlCapsFromSurfaceFormat(aDriver->ChangeOptions(), aQWindow->format()); 419 | 420 | const Aspect_Drawable aNativeWin = aQWindow != nullptr ? (Aspect_Drawable)aQWindow->winId() : 0; 421 | const Graphic3d_Vec2i aViewSize(theFbo->size().width(), theFbo->size().height()); 422 | 423 | const bool isFirstInit = myView->Window().IsNull(); 424 | theFbo->bindDefault(); // initialize OpenGl_Context with default (window) FBO 425 | if (!OcctGlTools::InitializeGlWindow(myView, aNativeWin, aViewSize, aQWindow->devicePixelRatio())) 426 | { 427 | Q_EMIT glCriticalError("OpenGl_Context is unable to wrap OpenGL context"); 428 | return; 429 | } 430 | 431 | theFbo->bind(); // rebind offscreen FBO 432 | dumpGlInfo(true, true); 433 | if (isFirstInit) 434 | { 435 | myContext->Display(myViewCube, 0, 0, false); 436 | 437 | // dummy shape for testing 438 | TopoDS_Shape aBox = BRepPrimAPI_MakeBox(100.0, 50.0, 90.0).Shape(); 439 | Handle(AIS_Shape) aShape = new AIS_Shape(aBox); 440 | myContext->Display(aShape, AIS_Shaded, 0, false); 441 | } 442 | } 443 | 444 | // ================================================================ 445 | // Function : render 446 | // ================================================================ 447 | void OcctQQuickFramebufferViewer::render(QOpenGLFramebufferObject* theFbo) 448 | { 449 | // this method is called from GL rendering thread; 450 | // accessing GUI items is not allowed here! 451 | Standard_Mutex::Sentry aLock(myViewerMutex); 452 | const QQuickWindow* aQWindow = window(); 453 | if (theFbo == nullptr || myView.IsNull() || aQWindow == nullptr) 454 | return; 455 | 456 | const Aspect_Drawable aNativeWin = OcctGlTools::GetGlNativeWindow((Aspect_Drawable)aQWindow->winId()); 457 | if (myView->Window().IsNull() 458 | || myView->Window()->NativeHandle() != aNativeWin) 459 | { 460 | initializeGL(theFbo); 461 | if (myView->Window().IsNull()) 462 | return; 463 | } 464 | 465 | Graphic3d_Vec2i aViewSizeOld; myView->Window()->Size(aViewSizeOld.x(), aViewSizeOld.y()); 466 | const double aDevPixelRatioOld = myView->Window()->DevicePixelRatio(); 467 | if (aDevPixelRatioOld != aQWindow->devicePixelRatio()) 468 | initializeGL(theFbo); 469 | 470 | // wrap FBO created by QOpenGLFramebufferObject 471 | if (!OcctGlTools::InitializeGlFbo(myView)) 472 | { 473 | Q_EMIT glCriticalError("Default FBO wrapper creation failed"); 474 | return; 475 | } 476 | 477 | Graphic3d_Vec2i aViewSizeNew; myView->Window()->Size(aViewSizeNew.x(), aViewSizeNew.y()); 478 | if (aViewSizeNew != aViewSizeOld || myView->Window()->DevicePixelRatio() != aDevPixelRatioOld) 479 | dumpGlInfo(true, false); 480 | 481 | // reset global GL state from Qt before redrawing OCCT 482 | OcctGlTools::ResetGlStateBeforeOcct(myView); 483 | 484 | // flush pending input events and redraw the viewer 485 | myView->InvalidateImmediate(); 486 | AIS_ViewController::FlushViewEvents(myContext, myView, true); 487 | 488 | // reset global GL state after OCCT before redrawing Qt 489 | // (alternative to QQuickOpenGLUtils::resetOpenGLState()) 490 | OcctGlTools::ResetGlStateAfterOcct(myView); 491 | /*#if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 492 | QQuickOpenGLUtils::resetOpenGLState() 493 | #else 494 | if (aQWindow != nullptr) 495 | aQWindow->resetOpenGLState(); 496 | #endif*/ 497 | } 498 | -------------------------------------------------------------------------------- /occt-qt-tools/OcctQtTools.cpp: -------------------------------------------------------------------------------- 1 | // Copyright (c) 2025 Kirill Gavrilov 2 | 3 | #include "OcctQtTools.h" 4 | 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | 12 | #include 13 | #include 14 | #include 15 | #include 16 | 17 | // ================================================================ 18 | // Function : qtColorToOcct 19 | // ================================================================ 20 | Quantity_Color OcctQtTools::qtColorToOcct(const QColor& theColor) 21 | { 22 | return Quantity_Color(theColor.red() / 255.0, theColor.green() / 255.0, theColor.blue() / 255.0, Quantity_TOC_sRGB); 23 | } 24 | 25 | // ================================================================ 26 | // Function : qtColorFromOcct 27 | // ================================================================ 28 | QColor OcctQtTools::qtColorFromOcct(const Quantity_Color& theColor) 29 | { 30 | NCollection_Vec3 anRgb; theColor.Values(anRgb.r(), anRgb.g(), anRgb.b(), Quantity_TOC_sRGB); 31 | return QColor((int)Round(anRgb.r() * 255.0), (int)Round(anRgb.g() * 255.0), (int)Round(anRgb.b() * 255.0)); 32 | } 33 | 34 | // ================================================================ 35 | // Function : qtColorToOcctRgba 36 | // ================================================================ 37 | Quantity_ColorRGBA OcctQtTools::qtColorToOcctRgba(const QColor& theColor) 38 | { 39 | return Quantity_ColorRGBA(OcctQtTools::qtColorToOcct(theColor), theColor.alpha() / 255.0f); 40 | } 41 | 42 | // ================================================================ 43 | // Function : qtColorFromOcctRgba 44 | // ================================================================ 45 | QColor OcctQtTools::qtColorFromOcctRgba(const Quantity_ColorRGBA& theColor) 46 | { 47 | QColor aColor = OcctQtTools::qtColorFromOcct(theColor.GetRGB()); 48 | aColor.setAlpha((int)Round(theColor.Alpha() * 255.0)); 49 | return aColor; 50 | } 51 | 52 | // ================================================================ 53 | // Function : qtStringToOcct 54 | // ================================================================ 55 | TCollection_AsciiString OcctQtTools::qtStringToOcct(const QString& theText) 56 | { 57 | return TCollection_AsciiString(theText.toUtf8().data()); 58 | } 59 | 60 | // ================================================================ 61 | // Function : qtStringFromOcct 62 | // ================================================================ 63 | QString OcctQtTools::qtStringFromOcct(const TCollection_AsciiString& theText) 64 | { 65 | return QString::fromUtf8(theText.ToCString()); 66 | } 67 | 68 | // ================================================================ 69 | // Function : qtStringToOcctExt 70 | // ================================================================ 71 | TCollection_ExtendedString OcctQtTools::qtStringToOcctExt(const QString& theText) 72 | { 73 | return TCollection_ExtendedString(theText.toUtf8().data(), true); 74 | } 75 | 76 | // ================================================================ 77 | // Function : qtStringFromOcctExt 78 | // ================================================================ 79 | QString OcctQtTools::qtStringFromOcctExt(const TCollection_ExtendedString& theText) 80 | { 81 | return QString::fromUtf16(theText.ToExtString()); 82 | } 83 | 84 | // ================================================================ 85 | // Function : qtMsgTypeToGravity 86 | // ================================================================ 87 | Message_Gravity OcctQtTools::qtMsgTypeToGravity(QtMsgType theType) 88 | { 89 | switch (theType) 90 | { 91 | case QtDebugMsg: return Message_Trace; 92 | case QtInfoMsg: return Message_Info; 93 | case QtWarningMsg: return Message_Warning; 94 | case QtCriticalMsg: return Message_Alarm; 95 | case QtFatalMsg: return Message_Fail; 96 | default: return Message_Fail; 97 | } 98 | } 99 | 100 | // ================================================================ 101 | // Function : qtMessageHandlerToOcct 102 | // ================================================================ 103 | void OcctQtTools::qtMessageHandlerToOcct(QtMsgType theType, 104 | const QMessageLogContext& theCtx, 105 | const QString& theMsg) 106 | { 107 | Message_Gravity aGravity = OcctQtTools::qtMsgTypeToGravity(theType); 108 | if (aGravity < (int)Message_Alarm) 109 | aGravity = Message_Trace; // lower gravity to trace for non-critical errors 110 | 111 | const QString aQMsg = qFormatLogMessage(theType, theCtx, theMsg); 112 | const TCollection_AsciiString aMsg = aQMsg.toUtf8().data(); 113 | Message::Send(aMsg, aGravity); 114 | } 115 | 116 | // ================================================================ 117 | // Function : qtGlPlatformSetup 118 | // ================================================================ 119 | void OcctQtTools::qtGlPlatformSetup() 120 | { 121 | #if defined(_WIN32) 122 | // never use ANGLE on Windows, since OCCT 3D Viewer does not expect this; 123 | // use Qt::AA_UseOpenGLES for embedded systems 124 | QCoreApplication::setAttribute(Qt::AA_UseDesktopOpenGL); 125 | #elif defined(__APPLE__) 126 | // 127 | #else 128 | // Qt6 tries to use Wayland platform by default, which is incompatible with OCCT depending on Xlib; 129 | // Force 'xcb' platform plugin (alternatively, could be passed QApplication as '-platform xcb' argument). 130 | OSD_Environment aQpaPlat("QT_QPA_PLATFORM"); 131 | if (aQpaPlat.Value().IsEmpty()) 132 | { 133 | aQpaPlat.SetValue("xcb"); 134 | aQpaPlat.Build(); 135 | } 136 | #endif 137 | 138 | // request Qt to share resources between OpenGL contexts like QOpenGLWidget and QQuickWidget; 139 | // allows detaching OCCT 3D Viewer widget inside QDockWidget into new top-level window 140 | QCoreApplication::setAttribute(Qt::AA_ShareOpenGLContexts); 141 | 142 | /*#if QT_VERSION < QT_VERSION_CHECK(6, 0, 0) 143 | // workaround for some bugs in Qt5 144 | OSD_Environment aQGlyph("QT_ENABLE_GLYPH_CACHE_WORKAROUND"); 145 | if (aQGlyph.Value().IsEmpty()) 146 | { 147 | aQGlyph.SetValue("1"); 148 | aQGlyph.Build(); 149 | } 150 | #endif*/ 151 | 152 | // global OpenGL setup managed by Qt 153 | const QSurfaceFormat aGlFormat = OcctQtTools::qtGlSurfaceFormat(); 154 | QSurfaceFormat::setDefaultFormat(aGlFormat); 155 | 156 | // ask Qt managing rendering from GUI thread instead of QSGRenderThread 157 | // for QtQuick applications 158 | /*OSD_Environment aQsgLoop("QSG_RENDER_LOOP"); 159 | if (aQsgLoop.Value().IsEmpty()) 160 | { 161 | aQsgLoop.SetValue("basic"); 162 | aQsgLoop.Build(); 163 | }*/ 164 | 165 | // enable auto-scaling for high-density screens and fractional scale factors 166 | // (this is default since Qt6) 167 | #if (QT_VERSION_MAJOR == 5) 168 | QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling); 169 | #if (QT_VERSION_MINOR >= 14) 170 | QGuiApplication::setHighDpiScaleFactorRoundingPolicy(Qt::HighDpiScaleFactorRoundingPolicy::PassThrough); 171 | #endif 172 | #endif 173 | 174 | // redirect Qt messages to OCCT 175 | /*static QtMessageHandler aDefHandler = qInstallMessageHandler([](QtMsgType theType, 176 | const QMessageLogContext& theCtx, 177 | const QString& theMsg){ 178 | OcctQtTools::qtMessageHandlerToOcct(theType, theCtx, theMsg); 179 | //if (aDefHandler != nullptr) { aDefHandler(QtCriticalMsg, theCtx, theMsg); } 180 | });*/ 181 | } 182 | 183 | // ================================================================ 184 | // Function : qtGlSurfaceFormat 185 | // ================================================================ 186 | QSurfaceFormat OcctQtTools::qtGlSurfaceFormat(QSurfaceFormat::OpenGLContextProfile theProfile, 187 | bool theToDebug) 188 | { 189 | const bool isDeepColor = false; 190 | QSurfaceFormat::OpenGLContextProfile aProfile = theProfile; 191 | if (theProfile == QSurfaceFormat::NoProfile) 192 | { 193 | aProfile = QSurfaceFormat::CompatibilityProfile; 194 | #ifdef __APPLE__ 195 | // suppress Qt warning "QCocoaGLContext: Falling back to unshared context" 196 | aProfile = QSurfaceFormat::CoreProfile; 197 | #endif 198 | } 199 | 200 | QSurfaceFormat aGlFormat; 201 | if (isDeepColor) 202 | { 203 | aGlFormat.setRedBufferSize(10); 204 | aGlFormat.setGreenBufferSize(10); 205 | aGlFormat.setBlueBufferSize(10); 206 | aGlFormat.setAlphaBufferSize(2); 207 | } 208 | aGlFormat.setDepthBufferSize(24); 209 | aGlFormat.setStencilBufferSize(8); 210 | aGlFormat.setProfile(aProfile); 211 | if (aProfile == QSurfaceFormat::CoreProfile) 212 | aGlFormat.setVersion(4, 5); 213 | 214 | // request sRGBColorSpace color-space to meet OCCT expectations or use OcctQtFrameBuffer fallback. 215 | /*#if (QT_VERSION_MAJOR > 5) || (QT_VERSION_MAJOR == 5 && QT_VERSION_MINOR >= 10) 216 | aGlFormat.setColorSpace(QSurfaceFormat::sRGBColorSpace); 217 | #endif*/ 218 | 219 | if (theToDebug) 220 | aGlFormat.setOption(QSurfaceFormat::DebugContext, true); 221 | 222 | return aGlFormat; 223 | } 224 | 225 | // ================================================================ 226 | // Function : qtGlCapsFromSurfaceFormat 227 | // ================================================================ 228 | void OcctQtTools::qtGlCapsFromSurfaceFormat(OpenGl_Caps& theCaps, const QSurfaceFormat& theFormat) 229 | { 230 | theCaps.contextDebug = theFormat.testOption(QSurfaceFormat::DebugContext); 231 | theCaps.contextSyncDebug = theCaps.contextDebug; 232 | theCaps.contextCompatible = theFormat.profile() != QSurfaceFormat::CoreProfile; 233 | #if (OCC_VERSION_HEX >= 0x070700) 234 | theCaps.buffersDeepColor = theFormat.redBufferSize() == 10 235 | && theFormat.greenBufferSize() == 10 236 | && theFormat.blueBufferSize() == 10; 237 | #endif 238 | } 239 | 240 | // ================================================================ 241 | // Function : qtHandleHoverEvent 242 | // ================================================================ 243 | bool OcctQtTools::qtHandleHoverEvent(Aspect_WindowInputListener& theListener, 244 | const Handle(V3d_View)& theView, 245 | const QHoverEvent* theEvent) 246 | { 247 | if (theView->Window().IsNull()) 248 | return false; 249 | 250 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 251 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y()); 252 | #else 253 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y()); 254 | #endif 255 | const Graphic3d_Vec2i aPnt2i(theView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5)); 256 | const Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE; 257 | const Aspect_VKeyFlags aFlags = OcctQtTools::qtMouseModifiers2VKeys(theEvent->modifiers()); 258 | return theListener.UpdateMousePosition(aPnt2i, aButtons, aFlags, false); 259 | } 260 | 261 | // ================================================================ 262 | // Function : qtHandleMouseEvent 263 | // ================================================================ 264 | bool OcctQtTools::qtHandleMouseEvent(Aspect_WindowInputListener& theListener, 265 | const Handle(V3d_View)& theView, 266 | const QMouseEvent* theEvent) 267 | { 268 | if (theView->Window().IsNull()) 269 | return false; 270 | 271 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 272 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y()); 273 | #else 274 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y()); 275 | #endif 276 | const Graphic3d_Vec2i aPnt2i(theView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5)); 277 | const Aspect_VKeyMouse aButtons = OcctQtTools::qtMouseButtons2VKeys(theEvent->buttons()); 278 | const Aspect_VKeyFlags aFlags = OcctQtTools::qtMouseModifiers2VKeys(theEvent->modifiers()); 279 | if (theEvent->type() == QEvent::MouseMove) 280 | return theListener.UpdateMousePosition(aPnt2i, aButtons, aFlags, false); 281 | 282 | return theListener.UpdateMouseButtons(aPnt2i, aButtons, aFlags, false); 283 | } 284 | 285 | // ================================================================ 286 | // Function : qtHandleWheelEvent 287 | // ================================================================ 288 | bool OcctQtTools::qtHandleWheelEvent(Aspect_WindowInputListener& theListener, 289 | const Handle(V3d_View)& theView, 290 | const QWheelEvent* theEvent) 291 | { 292 | if (theView->Window().IsNull()) 293 | return false; 294 | 295 | #if QT_VERSION >= QT_VERSION_CHECK(5, 14, 0) 296 | const Graphic3d_Vec2d aPnt2d(theEvent->position().x(), theEvent->position().y()); 297 | #else 298 | const Graphic3d_Vec2d aPnt2d(theEvent->pos().x(), theEvent->pos().y()); 299 | #endif 300 | const Graphic3d_Vec2i aPnt2i(theView->Window()->ConvertPointToBacking(aPnt2d) + Graphic3d_Vec2d(0.5)); 301 | return theListener.UpdateMouseScroll(Aspect_ScrollDelta(aPnt2i, double(theEvent->angleDelta().y()) / 120.0)); 302 | } 303 | 304 | // ================================================================ 305 | // Function : qtHandleTouchEvent 306 | // ================================================================ 307 | bool OcctQtTools::qtHandleTouchEvent(Aspect_WindowInputListener& theListener, 308 | const Handle(V3d_View)& theView, 309 | const QTouchEvent* theEvent) 310 | { 311 | if (theView->Window().IsNull()) 312 | return false; 313 | 314 | bool hasUpdates = false; 315 | #if QT_VERSION >= QT_VERSION_CHECK(6, 0, 0) 316 | for (const QTouchEvent::TouchPoint& aQTouch : theEvent->points()) 317 | { 318 | const Standard_Size aTouchId = aQTouch.id(); 319 | const Graphic3d_Vec2d aNewPos2d = 320 | theView->Window()->ConvertPointToBacking(Graphic3d_Vec2d(aQTouch.position().x(), aQTouch.position().y())); 321 | const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i(aNewPos2d + Graphic3d_Vec2d(0.5)); 322 | if (aQTouch.state() == QEventPoint::Pressed 323 | && aNewPos2i.minComp() >= 0) 324 | { 325 | hasUpdates = true; 326 | theListener.AddTouchPoint(aTouchId, aNewPos2d); 327 | } 328 | else if (aQTouch.state() == QEventPoint::Updated 329 | && theListener.TouchPoints().Contains(aTouchId)) 330 | { 331 | hasUpdates = true; 332 | theListener.UpdateTouchPoint(aTouchId, aNewPos2d); 333 | } 334 | else if (aQTouch.state() == QEventPoint::Released 335 | && theListener.RemoveTouchPoint(aTouchId)) 336 | { 337 | hasUpdates = true; 338 | } 339 | } 340 | #else 341 | for (const QTouchEvent::TouchPoint& aQTouch : theEvent->touchPoints()) 342 | { 343 | const Standard_Size aTouchId = aQTouch.id(); 344 | const Graphic3d_Vec2d aNewPos2d = 345 | theView->Window()->ConvertPointToBacking(Graphic3d_Vec2d(aQTouch.pos().x(), aQTouch.pos().y())); 346 | const Graphic3d_Vec2i aNewPos2i = Graphic3d_Vec2i(aNewPos2d + Graphic3d_Vec2d(0.5)); 347 | if (aQTouch.state() == Qt::TouchPointPressed 348 | && aNewPos2i.minComp() >= 0) 349 | { 350 | hasUpdates = true; 351 | theListener.AddTouchPoint(aTouchId, aNewPos2d); 352 | } 353 | else if (aQTouch.state() == Qt::TouchPointMoved 354 | && theListener.TouchPoints().Contains(aTouchId)) 355 | { 356 | hasUpdates = true; 357 | theListener.UpdateTouchPoint(aTouchId, aNewPos2d); 358 | } 359 | else if (aQTouch.state() == Qt::TouchPointReleased 360 | && theListener.RemoveTouchPoint(aTouchId)) 361 | { 362 | hasUpdates = true; 363 | } 364 | } 365 | #endif 366 | return hasUpdates; 367 | } 368 | 369 | // ================================================================ 370 | // Function : qtMouseButtons2VKeys 371 | // ================================================================ 372 | Aspect_VKeyMouse OcctQtTools::qtMouseButtons2VKeys(Qt::MouseButtons theButtons) 373 | { 374 | Aspect_VKeyMouse aButtons = Aspect_VKeyMouse_NONE; 375 | if ((theButtons & Qt::LeftButton) != 0) 376 | aButtons |= Aspect_VKeyMouse_LeftButton; 377 | 378 | if ((theButtons & Qt::MiddleButton) != 0) 379 | aButtons |= Aspect_VKeyMouse_MiddleButton; 380 | 381 | if ((theButtons & Qt::RightButton) != 0) 382 | aButtons |= Aspect_VKeyMouse_RightButton; 383 | 384 | return aButtons; 385 | } 386 | 387 | // ================================================================ 388 | // Function : qtMouseModifiers2VKeys 389 | // ================================================================ 390 | Aspect_VKeyFlags OcctQtTools::qtMouseModifiers2VKeys(Qt::KeyboardModifiers theModifiers) 391 | { 392 | Aspect_VKeyFlags aFlags = Aspect_VKeyFlags_NONE; 393 | if ((theModifiers & Qt::ShiftModifier) != 0) 394 | aFlags |= Aspect_VKeyFlags_SHIFT; 395 | 396 | if ((theModifiers & Qt::ControlModifier) != 0) 397 | aFlags |= Aspect_VKeyFlags_CTRL; 398 | 399 | if ((theModifiers & Qt::AltModifier) != 0) 400 | aFlags |= Aspect_VKeyFlags_ALT; 401 | 402 | return aFlags; 403 | } 404 | 405 | // ================================================================ 406 | // Function : qtKey2VKey 407 | // ================================================================ 408 | Aspect_VKey OcctQtTools::qtKey2VKey(int theKey) 409 | { 410 | switch (theKey) 411 | { 412 | case 1060: // ru 413 | case Qt::Key_A: 414 | return Aspect_VKey_A; 415 | case 1048: // ru 416 | case Qt::Key_B: 417 | return Aspect_VKey_B; 418 | case 1057: // ru 419 | case Qt::Key_C: 420 | return Aspect_VKey_C; 421 | case 1042: // ru 422 | case Qt::Key_D: 423 | return Aspect_VKey_D; 424 | case 1059: // ru 425 | case Qt::Key_E: 426 | return Aspect_VKey_E; 427 | case 1040: // ru 428 | case Qt::Key_F: 429 | return Aspect_VKey_F; 430 | case Qt::Key_G: 431 | return Aspect_VKey_G; 432 | case Qt::Key_H: 433 | return Aspect_VKey_H; 434 | case Qt::Key_I: 435 | return Aspect_VKey_I; 436 | case Qt::Key_J: 437 | return Aspect_VKey_J; 438 | case Qt::Key_K: 439 | return Aspect_VKey_K; 440 | case 1044: // ru 441 | case Qt::Key_L: 442 | return Aspect_VKey_L; 443 | case Qt::Key_M: 444 | return Aspect_VKey_M; 445 | case Qt::Key_N: 446 | return Aspect_VKey_N; 447 | case Qt::Key_O: 448 | return Aspect_VKey_O; 449 | case Qt::Key_P: 450 | return Aspect_VKey_P; 451 | case 1049: // ru 452 | case Qt::Key_Q: 453 | return Aspect_VKey_Q; 454 | case 1050: // ru 455 | case Qt::Key_R: 456 | return Aspect_VKey_R; 457 | case 1067: // ru 458 | case Qt::Key_S: 459 | return Aspect_VKey_S; 460 | case 1045: // ru 461 | case Qt::Key_T: 462 | return Aspect_VKey_T; 463 | case Qt::Key_U: 464 | return Aspect_VKey_U; 465 | case 1052: // ru 466 | case Qt::Key_V: 467 | return Aspect_VKey_V; 468 | case 1062: // ru 469 | case Qt::Key_W: 470 | return Aspect_VKey_W; 471 | case 1063: // ru 472 | case Qt::Key_X: 473 | return Aspect_VKey_X; 474 | case Qt::Key_Y: 475 | return Aspect_VKey_Y; 476 | case 1071: // ru 477 | case Qt::Key_Z: 478 | return Aspect_VKey_Z; 479 | // 480 | case Qt::Key_0: 481 | return Aspect_VKey_0; 482 | case Qt::Key_1: 483 | return Aspect_VKey_1; 484 | case Qt::Key_2: 485 | return Aspect_VKey_2; 486 | case Qt::Key_3: 487 | return Aspect_VKey_3; 488 | case Qt::Key_4: 489 | return Aspect_VKey_4; 490 | case Qt::Key_5: 491 | return Aspect_VKey_5; 492 | case Qt::Key_6: 493 | return Aspect_VKey_6; 494 | case Qt::Key_7: 495 | return Aspect_VKey_7; 496 | case Qt::Key_8: 497 | return Aspect_VKey_8; 498 | case Qt::Key_9: 499 | return Aspect_VKey_9; 500 | // 501 | case Qt::Key_F1: 502 | return Aspect_VKey_F1; 503 | case Qt::Key_F2: 504 | return Aspect_VKey_F2; 505 | case Qt::Key_F3: 506 | return Aspect_VKey_F3; 507 | case Qt::Key_F4: 508 | return Aspect_VKey_F4; 509 | case Qt::Key_F5: 510 | return Aspect_VKey_F5; 511 | case Qt::Key_F6: 512 | return Aspect_VKey_F6; 513 | case Qt::Key_F7: 514 | return Aspect_VKey_F7; 515 | case Qt::Key_F8: 516 | return Aspect_VKey_F8; 517 | case Qt::Key_F9: 518 | return Aspect_VKey_F9; 519 | case Qt::Key_F10: 520 | return Aspect_VKey_F10; 521 | case Qt::Key_F11: 522 | return Aspect_VKey_F11; 523 | case Qt::Key_F12: 524 | return Aspect_VKey_F12; 525 | // 526 | case Qt::Key_Up: 527 | return Aspect_VKey_Up; 528 | case Qt::Key_Left: 529 | return Aspect_VKey_Left; 530 | case Qt::Key_Right: 531 | return Aspect_VKey_Right; 532 | case Qt::Key_Down: 533 | return Aspect_VKey_Down; 534 | case Qt::Key_Plus: 535 | return Aspect_VKey_Plus; 536 | case Qt::Key_Minus: 537 | return Aspect_VKey_Minus; 538 | case Qt::Key_Equal: 539 | return Aspect_VKey_Equal; 540 | case Qt::Key_PageDown: 541 | return Aspect_VKey_PageDown; 542 | case Qt::Key_PageUp: 543 | return Aspect_VKey_PageUp; 544 | case Qt::Key_Home: 545 | return Aspect_VKey_Home; 546 | case Qt::Key_End: 547 | return Aspect_VKey_End; 548 | case Qt::Key_Escape: 549 | return Aspect_VKey_Escape; 550 | case Qt::Key_Back: 551 | return Aspect_VKey_Back; 552 | case Qt::Key_Enter: 553 | return Aspect_VKey_Enter; 554 | case Qt::Key_Backspace: 555 | return Aspect_VKey_Backspace; 556 | case Qt::Key_Space: 557 | return Aspect_VKey_Space; 558 | case Qt::Key_Delete: 559 | return Aspect_VKey_Delete; 560 | case Qt::Key_Tab: 561 | return Aspect_VKey_Tab; 562 | case 1025: 563 | case Qt::Key_QuoteLeft: 564 | return Aspect_VKey_Tilde; 565 | // 566 | case Qt::Key_Shift: 567 | return Aspect_VKey_Shift; 568 | case Qt::Key_Control: 569 | return Aspect_VKey_Control; 570 | case Qt::Key_Alt: 571 | return Aspect_VKey_Alt; 572 | case Qt::Key_Menu: 573 | return Aspect_VKey_Menu; 574 | case Qt::Key_Meta: 575 | return Aspect_VKey_Meta; 576 | default: 577 | return Aspect_VKey_UNKNOWN; 578 | } 579 | } 580 | --------------------------------------------------------------------------------