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