1 /*========================================================================= 2 3 Program: Visualization Toolkit 4 Module: QVTKOpenGLNativeWidget.h 5 6 Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 7 All rights reserved. 8 See Copyright.txt or http://www.kitware.com/Copyright.htm for details. 9 10 This software is distributed WITHOUT ANY WARRANTY; without even 11 the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR 12 PURPOSE. See the above copyright notice for more information. 13 14 =========================================================================*/ 15 /** 16 * @class QVTKOpenGLNativeWidget 17 * @brief QOpenGLWidget subclass to house a vtkGenericOpenGLRenderWindow in a Qt 18 * application. 19 * 20 * QVTKOpenGLNativeWidget extends QOpenGLWidget to make it work with a 21 * vtkGenericOpenGLRenderWindow. 22 * 23 * Please note that QVTKOpenGLNativeWidget only works with vtkGenericOpenGLRenderWindow. 24 * This is necessary since QOpenGLWidget wants to take over the window management as 25 * well as the OpenGL context creation. Getting that to work reliably with 26 * vtkXRenderWindow or vtkWin32RenderWindow (and other platform specific 27 * vtkRenderWindow subclasses) was tricky and fraught with issues. 28 * 29 * Since QVTKOpenGLNativeWidget uses QOpenGLWidget to create the OpenGL context, 30 * it uses QSurfaceFormat (set using `QOpenGLWidget::setFormat` or 31 * `QSurfaceFormat::setDefaultFormat`) to create appropriate window and context. 32 * You can use `QVTKOpenGLNativeWidget::copyToFormat` to obtain a QSurfaceFormat 33 * appropriate for a vtkRenderWindow. 34 * 35 * A typical usage for QVTKOpenGLNativeWidget is as follows: 36 * @code{.cpp} 37 * 38 * // before initializing QApplication, set the default surface format. 39 * QSurfaceFormat::setDefaultFormat(QVTKOpenGLNativeWidget::defaultFormat()); 40 * 41 * vtkNew<vtkGenericOpenGLRenderWindow> window; 42 * QPointer<QVTKOpenGLNativeWidget> widget = new QVTKOpenGLNativeWidget(...); 43 * widget->SetRenderWindow(window.Get()); 44 * 45 * // If using any of the standard view e.g. vtkContextView, then 46 * // you can do the following. 47 * vtkNew<vtkContextView> view; 48 * view->SetRenderWindow(window.Get()); 49 * 50 * // You can continue to use `window` as a regular vtkRenderWindow 51 * // including adding renderers, actors etc. 52 * 53 * @endcode 54 * 55 * @section OpenGLContext OpenGL Context 56 * 57 * In QOpenGLWidget (superclass for QVTKOpenGLNativeWidget), all rendering happens in a 58 * framebuffer object. Thus, care must be taken in the rendering code to never 59 * directly re-bind the default framebuffer i.e. ID 0. 60 * 61 * QVTKOpenGLNativeWidget creates an internal QOpenGLFramebufferObject, independent of the 62 * one created by superclass, for vtkRenderWindow to do the rendering in. This 63 * explicit double-buffering is useful in avoiding temporary back-buffer only 64 * renders done in VTK (e.g. when making selections) from destroying the results 65 * composed on screen. 66 * 67 * @section RenderAndPaint Handling Render and Paint. 68 * 69 * QWidget subclasses (including `QOpenGLWidget` and `QVTKOpenGLNativeWidget`) display 70 * their contents on the screen in `QWidget::paint` in response to a paint event. 71 * `QOpenGLWidget` subclasses are expected to do OpenGL rendering in 72 * `QOpenGLWidget::paintGL`. QWidget can receive paint events for various 73 * reasons including widget getting focus/losing focus, some other widget on 74 * the UI e.g. QProgressBar in status bar updating, etc. 75 * 76 * In VTK applications, any time the vtkRenderWindow needs to be updated to 77 * render a new result, one call `vtkRenderWindow::Render` on it. 78 * vtkRenderWindowInteractor set on the render window ensures that as 79 * interactions happen that affect the rendered result, it calls `Render` on the 80 * render window. 81 * 82 * Since paint in Qt can be called more often then needed, we avoid potentially 83 * expensive `vtkRenderWindow::Render` calls each time that happens. Instead, 84 * QVTKOpenGLNativeWidget relies on the VTK application calling 85 * `vtkRenderWindow::Render` on the render window when it needs to update the 86 * rendering. `paintGL` simply passes on the result rendered by the most render 87 * vtkRenderWindow::Render to Qt windowing system for composing on-screen. 88 * 89 * There may still be occasions when we may have to render in `paint` for 90 * example if the window was resized or Qt had to recreate the OpenGL context. 91 * In those cases, `QVTKOpenGLNativeWidget::paintGL` can request a render by calling 92 * `QVTKOpenGLNativeWidget::renderVTK`. 93 * 94 * @section Caveats 95 * QVTKOpenGLNativeWidget does not support stereo, 96 * please use QVTKOpenGLStereoWidget if you need support for stereo rendering 97 * 98 * QVTKOpenGLNativeWidget is targeted for Qt version 5.5 and above. 99 * 100 * @sa QVTKOpenGLStereoWidget QVTKRenderWidget 101 * 102 */ 103 #ifndef QVTKOpenGLNativeWidget_h 104 #define QVTKOpenGLNativeWidget_h 105 106 #include <QOpenGLWidget> 107 #include <QScopedPointer> // for QScopedPointer. 108 109 #include "QVTKInteractor.h" // needed for QVTKInteractor 110 #include "vtkDeprecation.h" // For VTK_DEPRECATED_IN_9_0_0 111 #include "vtkGUISupportQtModule.h" // for export macro 112 #include "vtkNew.h" // needed for vtkNew 113 #include "vtkSmartPointer.h" // needed for vtkSmartPointer 114 115 class QVTKInteractor; 116 class QVTKInteractorAdapter; 117 class QVTKRenderWindowAdapter; 118 class vtkGenericOpenGLRenderWindow; 119 120 class VTKGUISUPPORTQT_EXPORT QVTKOpenGLNativeWidget : public QOpenGLWidget 121 { 122 Q_OBJECT 123 typedef QOpenGLWidget Superclass; 124 125 public: 126 QVTKOpenGLNativeWidget(QWidget* parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()); 127 QVTKOpenGLNativeWidget(vtkGenericOpenGLRenderWindow* window, QWidget* parent = nullptr, 128 Qt::WindowFlags f = Qt::WindowFlags()); 129 ~QVTKOpenGLNativeWidget() override; 130 131 ///@{ 132 /** 133 * Set a render window to use. It a render window was already set, it will be 134 * finalized and all of its OpenGL resource released. If the \c win is 135 * non-null and it has no interactor set, then a QVTKInteractor instance will 136 * be created as set on the render window as the interactor. 137 */ 138 void setRenderWindow(vtkGenericOpenGLRenderWindow* win); 139 void setRenderWindow(vtkRenderWindow* win); 140 ///@} 141 142 /** 143 * Returns the render window that is being shown in this widget. 144 */ 145 vtkRenderWindow* renderWindow() const; 146 147 /** 148 * Get the QVTKInteractor that was either created by default or set by the user. 149 */ 150 QVTKInteractor* interactor() const; 151 152 /** 153 * @copydoc QVTKRenderWindowAdapter::defaultFormat(bool) 154 */ 155 static QSurfaceFormat defaultFormat(bool stereo_capable = false); 156 157 ///@{ 158 /** 159 * Enable or disable support for HiDPI displays. When enabled, this enabled 160 * DPI scaling i.e. `vtkWindow::SetDPI` will be called with a DPI value scaled 161 * by the device pixel ratio every time the widget is resized. The unscaled 162 * DPI value can be specified by using `setUnscaledDPI`. 163 */ 164 void setEnableHiDPI(bool enable); enableHiDPI()165 bool enableHiDPI() const { return this->EnableHiDPI; } 166 ///@} 167 168 ///@{ 169 /** 170 * Set/Get unscaled DPI value. Defaults to 72, which is also the default value 171 * in vtkWindow. 172 */ 173 void setUnscaledDPI(int); unscaledDPI()174 int unscaledDPI() const { return this->UnscaledDPI; } 175 ///@} 176 177 ///@{ 178 /** 179 * Set/Get a custom device pixel ratio to use to map Qt sizes to VTK (or 180 * OpenGL) sizes. Thus, when the QWidget is resized, it called 181 * `vtkRenderWindow::SetSize` on the internal vtkRenderWindow after 182 * multiplying the QWidget's size by this scale factor. 183 * 184 * By default, this is set to 0. Which means that `devicePixelRatio` obtained 185 * from Qt will be used. Set this to a number greater than 0 to override this 186 * behaviour and use the custom scale factor instead. 187 * 188 * `effectiveDevicePixelRatio` can be used to obtain the device-pixel-ratio 189 * that will be used given the value for customDevicePixelRatio. 190 */ 191 void setCustomDevicePixelRatio(double cdpr); customDevicePixelRatio()192 double customDevicePixelRatio() const { return this->CustomDevicePixelRatio; } 193 double effectiveDevicePixelRatio() const; 194 ///@} 195 196 ///@{ 197 /** 198 * Set/get the default cursor to use for this widget. 199 */ 200 void setDefaultCursor(const QCursor& cursor); defaultCursor()201 const QCursor& defaultCursor() const { return this->DefaultCursor; } 202 ///@} 203 204 ///@{ 205 /** 206 * @deprecated in VTK 9.0 207 */ 208 VTK_DEPRECATED_IN_9_0_0("Use QVTKOpenGLNativeWidget::setRenderWindow") 209 void SetRenderWindow(vtkGenericOpenGLRenderWindow* win); 210 VTK_DEPRECATED_IN_9_0_0("Use QVTKOpenGLNativeWidget::setRenderWindow") 211 void SetRenderWindow(vtkRenderWindow* win); 212 ///@} 213 214 ///@{ 215 /** 216 * These methods have be deprecated to fix naming style. Since 217 * QVTKOpenGLNativeWidget is QObject subclass, we follow Qt naming conventions 218 * rather than VTK's. 219 */ 220 VTK_DEPRECATED_IN_9_0_0("Use QVTKOpenGLNativeWidget::renderWindow") 221 vtkRenderWindow* GetRenderWindow(); 222 VTK_DEPRECATED_IN_9_0_0("Removed in 9.0.0 (internal)") 223 QVTKInteractor* GetInteractor(); 224 ///@} 225 226 /** 227 * @deprecated in VTK 9.0 228 * QVTKInteractorAdapter is an internal helper. Hence the API was removed. 229 */ 230 VTK_DEPRECATED_IN_9_0_0("Removed in 9.0.0 (internal)") 231 QVTKInteractorAdapter* GetInteractorAdapter(); 232 233 /** 234 * @deprecated in VTK 9.0. Simply use `QWidget::setCursor` API to change 235 * cursor. 236 */ 237 VTK_DEPRECATED_IN_9_0_0("Use QWidget::setCursor") 238 void setQVTKCursor(const QCursor& cursor); 239 240 /** 241 * @deprecated in VTK 9.0. Use `setDefaultCursor` instead. 242 */ 243 VTK_DEPRECATED_IN_9_0_0("Use QWidget::setDefaultCursor") 244 void setDefaultQVTKCursor(const QCursor& cursor); 245 246 protected Q_SLOTS: 247 /** 248 * Called as a response to `QOpenGLContext::aboutToBeDestroyed`. This may be 249 * called anytime during the widget lifecycle. We need to release any OpenGL 250 * resources allocated in VTK work in this method. 251 */ 252 virtual void cleanupContext(); 253 254 void updateSize(); 255 256 protected: 257 bool event(QEvent* evt) override; 258 void initializeGL() override; 259 void paintGL() override; 260 261 protected: 262 vtkSmartPointer<vtkGenericOpenGLRenderWindow> RenderWindow; 263 QScopedPointer<QVTKRenderWindowAdapter> RenderWindowAdapter; 264 265 private: 266 Q_DISABLE_COPY(QVTKOpenGLNativeWidget); 267 268 bool EnableHiDPI; 269 int UnscaledDPI; 270 double CustomDevicePixelRatio; 271 QCursor DefaultCursor; 272 }; 273 274 #endif 275