Commit 45704131 authored by Lucas Gandel's avatar Lucas Gandel Committed by Mathieu Westphal

Improve QVTKOpenGLWidget and add QVTKOpenGLWindow class based on QOpenGLWindow

Fixing stereo support with Qt5.
Commit 139f787a was introducing QVTKOpenGLWidget to couple a vtkRenderWindow with a QOpenGLWidget.
However, both QVTKOpenGLWidget and its superclass QOpenGLWidget manage a single frame buffer.

QVTKOpenGLWindow derives the QOpenGLWindow class to benefit from an OpenGL context managing multiple frame buffers.
It can be used as a QWidget by calling QWidget::createWindowContainer() on your QVTKOpenGLWindow instance.
QVTKOpenGLWidget has been reimplemented to use thes QVTKOpenGLWindow directly has a widget.
It is recommended to do so.

The old QVTKOpenGLWidget is been moved into QVTKOpenGLSimpleWidget and should not be removed as it support being a native widget,
wich the new implementation does not.

There is a new QVTKOpenGLWidget::isValid() method to check that the widget is ready to render.

It is to be noted that there is a specific implementation of
event handling in order to work around QTBUG-61836.
The Qt::WindowTransparentForInputFlag has no effect on Mac. Thus events
are not being passed to the widget. We let the window grabbing all events
and then forward them to the widget.
We also take care for providing a method to forward back events to the
window if events were explicitely triggered on the widget instance. This
is the case for some paraview testing logic that should now use the provided
testingEvent() method instead of notifying the widget with events.

It is to be noted that, on OSX, the widget is always resized to its internal vtkRenderWindow
size to ensure that the viewport is correct.
This is necessary only on MacOS when HiDPI is supported. Enabling HiDPI
has the side effect that Cocoa will start overriding any glViewport calls
in application code. For reference, see QCocoaWindow::initialize().

This also improve tests and examples using the QVTKOpenGLWidget
parent bc362f71
......@@ -31,9 +31,10 @@
#include "vtkTimerLog.h"
#include <QApplication>
#include <QWidget>
#include <QMainWindow>
#include <QHBoxLayout>
#include <QMainWindow>
#include <QSurfaceFormat>
#include <QWidget>
//----------------------------------------------------------------------------
......@@ -46,7 +47,7 @@ int main( int argc, char * argv [] )
QApplication app(argc, argv);
// QVTK set up and initialization
QVTKOpenGLWidget qvtkWidget(nullptr);
QVTKOpenGLWidget qvtkWidget;
vtkNew<vtkGenericOpenGLRenderWindow> renderWindow;
qvtkWidget.SetRenderWindow(renderWindow);
......
......@@ -22,6 +22,7 @@
#include "vtkProperty.h"
#include <vtkRenderer.h>
#include <vtkRenderWindow.h>
#include "vtkRenderWindowInteractor.h"
#include "vtkResliceImageViewer.h"
#include "vtkResliceCursorLineRepresentation.h"
#include "vtkResliceCursorThickLineRepresentation.h"
......
......@@ -24,6 +24,7 @@
=========================================================================*/
#include <QApplication>
#include <QSurfaceFormat>
#include "vtkGenericOpenGLRenderWindow.h"
#include "vtkImageViewer.h"
......
......@@ -10,7 +10,7 @@
#include <QApplication>
#include <QSurfaceFormat>
#include "QVTKOpenGLWidget.h"
#include "QVTKOpenGLSimpleWidget.h"
#include "CustomLinkView.h"
extern int qInitResources_icons();
......@@ -18,7 +18,7 @@ extern int qInitResources_icons();
int main( int argc, char** argv )
{
// needed to ensure appropriate OpenGL context is created for VTK rendering.
QSurfaceFormat::setDefaultFormat(QVTKOpenGLWidget::defaultFormat());
QSurfaceFormat::setDefaultFormat(QVTKOpenGLSimpleWidget::defaultFormat());
// QT Stuff
QApplication app( argc, argv );
......
......@@ -14,7 +14,9 @@ set(QVTKLibSrcs
vtkQtDebugLeaksView.cxx
vtkQtTableModelAdapter.cxx
vtkQtTreeModelAdapter.cxx
QVTKOpenGLSimpleWidget.cxx
QVTKOpenGLWidget.cxx
QVTKOpenGLWindow.cxx
)
set(QVTKMocHeaders
......@@ -29,7 +31,9 @@ set(QVTKMocHeaders
vtkQtDebugLeaksView.h
vtkQtTableModelAdapter.h
vtkQtTreeModelAdapter.h
QVTKOpenGLSimpleWidget.h
QVTKOpenGLWidget.h
QVTKOpenGLWindow.h
)
set(add_qvtkwidget FALSE)
......
......@@ -50,10 +50,10 @@ class QVTKInteractorInternal;
/**
* @class QVTKInteractor
* @brief - an interactor for QVTKOpenGLWidget (and QVTKWiget).
* @brief - an interactor for QVTKOpenGLSimpleWidget (and QVTKWiget).
*
* QVTKInteractor handles relaying Qt events to VTK.
* @sa QVTKOpenGLWidget
* @sa QVTKOpenGLSimpleWidget
*/
class VTKGUISUPPORTQT_EXPORT QVTKInteractor : public vtkRenderWindowInteractor
......
......@@ -56,7 +56,7 @@ QVTKInteractorAdapter::~QVTKInteractorAdapter()
{
}
void QVTKInteractorAdapter::SetDevicePixelRatio(int ratio, vtkRenderWindowInteractor* iren)
void QVTKInteractorAdapter::SetDevicePixelRatio(float ratio, vtkRenderWindowInteractor* iren)
{
if (ratio != DevicePixelRatio)
{
......
......@@ -61,8 +61,8 @@ public:
// Description:
// Set the device pixel ration, this defaults to 1, but in Qt 5 can be 2.
void SetDevicePixelRatio(int ratio, vtkRenderWindowInteractor* iren = nullptr);
int GetDevicePixelRatio() { return this->DevicePixelRatio; }
void SetDevicePixelRatio(float ratio, vtkRenderWindowInteractor* iren = nullptr);
float GetDevicePixelRatio() { return this->DevicePixelRatio; }
// Description:
// Process a QEvent and send it to the interactor
......@@ -71,7 +71,7 @@ public:
protected:
int AccumulatedDelta;
int DevicePixelRatio;
float DevicePixelRatio;
};
#endif
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
/*=========================================================================
Program: Visualization Toolkit
Module: QVTKOpenGLWindow.h
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
#ifndef QVTKOpenGLWindow_H
#define QVTKOpenGLWindow_H
#include "vtkGUISupportQtModule.h" // For export macro
#include "vtkSmartPointer.h" // Required for smart pointer internal ivars.
#include <QOpenGLWindow>
// Forward VTK class declarations
class QVTKInteractorAdapter;
class vtkEventQtSlotConnect;
class vtkGenericOpenGLRenderWindow;
class vtkObject;
class vtkRenderWindow;
/**
* @class QVTKOpenGLWindow
* @brief display a vtkGenericOpenGLRenderWindow in a Qt QOpenGLWindow.
*
* QVTKOpenGLWindow provides a way to display VTK data in a Qt OpenGL context.
*
* QVTKOpenGLWindow extends QOpenGLWindow to make it work with a
* vtkGenericOpenGLRenderWindow. This is akin to QVTKOpenGLSimpleWidget except the
* OpenGL context is created and managed by QOpenGLWindow (added in Qt 5.4).
*
* While QVTKOpenGLWindow is intended to be a replacement for
* QVTKOpenGLSimpleWidget, there are a few difference between both.
* Unlike QVTKOpenGLSimpleWidget, QVTKOpenGLWindow can target multiple framebuffers
* and thus allows for stereo-capable applications.
* QVTKOpenGLWindow API was adapted from deprecated QVTKWidget2.
*
* Since QVTKOpenGLWindow uses QOpenGLWindow to create the OpenGL context,
* it uses QSurfaceFormat (set using `QOpenGLWidget::setFormat` or
* `QSurfaceFormat::setDefaultFormat`)to create appropriate window and context.
*
* Since QOpenGLWindow (superclass for QVTKOpenGLWindow) is not a subclass
* of QWidget, a typical usage for QVTKOpenGLWindow is as follows:
* @code{.cpp}
*
* vtkNew<vtkGenericOpenGLRenderWindow> window;
* QPointer<QVTKOpenGLWindow> qWindow = new QVTKOpenGLWindow(window);
*
* // You can continue to use `window` as a regular vtkRenderWindow
* // including adding renderers, actors etc.
*
* // Get QVTKOpenGLWindow `widget` instance as a QWidget instance and add it
* // to the parent widget.
* QWidget* container =
* QWidget::createWindowContainer(qWindow, parent);
*
* // Set flag to propagate Mouse events to the parent
* container->setAttribute(Qt::WA_TransparentForMouseEvents);
*
* @endcode
*
* QVTKOpenGLWidget provides a wrapper around QVTKOpenGLWindow to manipulate a
* QVTKOpenGLWindow object as a QWidget instance, it is recommended to use
* QVTKOpenGLWidget instead of using this class directly.
*
* QVTKOpenGLWindow is targeted for Qt version 5.9 and above.
*
* @sa QVTKOpenGLWidget QVTKOpenGLSimpleWidget QVTKWidget2
*/
class VTKGUISUPPORTQT_EXPORT QVTKOpenGLWindow : public QOpenGLWindow
{
Q_OBJECT
typedef QOpenGLWindow Superclass;
public:
QVTKOpenGLWindow();
QVTKOpenGLWindow(vtkGenericOpenGLRenderWindow* w,
QOpenGLContext *shareContext = QOpenGLContext::currentContext(),
UpdateBehavior updateBehavior = NoPartialUpdate,
QWindow *parent = Q_NULLPTR);
QVTKOpenGLWindow(QOpenGLContext *shareContext,
UpdateBehavior updateBehavior = NoPartialUpdate,
QWindow *parent = Q_NULLPTR);
~QVTKOpenGLWindow();
/**
* Set the VTK render window used for rendering.
*/
virtual void SetRenderWindow(vtkGenericOpenGLRenderWindow*);
/**
* Get the VTK render window used for rendering.
*/
virtual vtkGenericOpenGLRenderWindow* GetRenderWindow();
/**
* Sets up vtkRenderWindow ivars using QSurfaceFormat.
*/
static void copyFromFormat(const QSurfaceFormat& format, vtkRenderWindow* win);
/**
* Using the vtkRenderWindow, setup QSurfaceFormat.
*/
static void copyToFormat(vtkRenderWindow* win, QSurfaceFormat& format);
/**
* Returns a typical QSurfaceFormat suitable for most applications using
* QVTKOpenGLWidget. Note that this is not the QSurfaceFormat that gets used
* if none is specified. That is set using `QSurfaceFormat::setDefaultFormat`.
*/
static QSurfaceFormat defaultFormat();
/**
* Get the QEvent to VTK events translator.
*/
virtual QVTKInteractorAdapter* GetInteractorAdapter();
/**
* Enable or disable support for HiDPI displays.
*/
virtual void setEnableHiDPI(bool enable);
/**
* Process a QEvent and send it to the internal render window interactor
* returns whether the event was recognized and processed
*/
bool ProcessEvent(QEvent* e);
signals:
/**
* Signal emitted when a QMouseEvent has been receive, with the corresponding
* event as argument.
*/
void windowEvent(QEvent* e);
public slots:
/**
* slot to make this vtk render window current
*/
virtual void MakeCurrent();
/**
* slot called when vtk wants to know if the context is current
*/
virtual void IsCurrent(vtkObject* caller, unsigned long vtk_event,
void* client_data, void* call_data);
/**
* slot called when vtk wants to frame the window
*/
virtual void Frame();
/**
* slot called when vtk wants to start the render
*/
virtual void Start();
/**
* slot called when vtk wants to end the render
*/
virtual void End() {}
/**
* slot called when vtk wants to know if a window is direct
*/
virtual void IsDirect(vtkObject* caller, unsigned long vtk_event,
void* client_data, void* call_data);
/**
* slot called when vtk wants to know if a window supports OpenGL
*/
virtual void SupportsOpenGL(vtkObject* caller, unsigned long vtk_event,
void* client_data, void* call_data);
/**
* slot called when the stereo type of the render window changed
*/
virtual void UpdateStereoType(vtkObject* caller, unsigned long vtk_event,
void* client_data, void* call_data);
/**
* slot to process events comming from the widget containing this window
*/
virtual void widgetEvent(QEvent* e);
/**
* Returns true if the internal QOpenGLWindow's is valid, i.e. if OpenGL
* resources, like the context, have been successfully initialized.
*/
virtual bool isValid();
private slots:
/**
* slot called when vtk resized the render window
*/
virtual void ResizeToVTKWindow();
protected:
/**
* Override event handler
*/
virtual void initializeGL();
virtual void paintGL();
virtual void moveEvent(QMoveEvent* event);
virtual bool event(QEvent* e);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual void mouseDoubleClickEvent(QMouseEvent* event);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void enterEvent(QEvent*);
virtual void leaveEvent(QEvent*);
virtual void wheelEvent(QWheelEvent*);
private:
/**
* internal VTK rendern window
*/
vtkSmartPointer<vtkGenericOpenGLRenderWindow> RenderWindow;
bool EnableHiDPI;
int OriginalDPI;
/**
* interaction binding
*/
QVTKInteractorAdapter* IrenAdapter;
vtkSmartPointer<vtkEventQtSlotConnect> EventSlotConnector;
};
#endif // QVTKOpenGLWindow_H
......@@ -29,7 +29,7 @@
* @brief - display a vtkRenderWindow in a Qt's QWidget.
*
* QVTKWidget provides a way to display VTK data in a Qt widget.
* @deprecated Please use QVTKOpenGLWidget instead.
* @deprecated Please use QVTKOpenGLSimpleWidget instead.
*/
#ifndef Q_VTK_WIDGET_H
......
......@@ -5,13 +5,17 @@ vtk_add_test_cxx(vtkGUISupportQtCxxTests tests
TestQtDebugLeaksView.cxx
TestQtTableModelAdapter.cxx
TestQtTreeModelAdapter.cxx
TestQVTKOpenGLSimpleWidgetPicking.cxx
TestQVTKOpenGLWidgetPicking.cxx
TestQtSwapWindows.cxx
TestQVTKOpenGLSimpleWidgetSwapWindows.cxx
TestQVTKOpenGLWidgetSwapWindows.cxx
)
vtk_add_test_cxx(vtkGUISupportQtCxxTests tests
TestQVTKOpenGLSimpleWidget.cxx
TestQVTKOpenGLWidget.cxx
TestQVTKOpenGLWidgetWithDisabledInteractor.cxx
TestQVTKOpenGLSimpleWidgetWithDisabledInteractor.cxx
TestQVTKOpenGLSimpleWidgetWithMSAA.cxx
TestQVTKOpenGLWidgetWithMSAA.cxx
)
......@@ -33,3 +37,6 @@ vtk_test_cxx_executable(vtkGUISupportQtCxxTests tests
set_target_properties(vtkGUISupportQtCxxTests PROPERTIES
COMPILE_FLAGS "${Qt5Widgets_EXECUTABLE_COMPILE_FLAGS}")
set_tests_properties(vtkGUISupportQtCxx-TestQVTKOpenGLWidgetPicking
PROPERTIES RUN_SERIAL ON)
/*=========================================================================
Program: Visualization Toolkit
Module: TestQVTKOpenGLSimpleWidget.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// Tests QVTKOpenGLSimpleWidget
#include "QVTKOpenGLSimpleWidget.h"
#include "vtkActor.h"
#include "vtkGenericOpenGLRenderWindow.h"
#include "vtkNew.h"
#include "vtkPolyDataMapper.h"
#include "vtkRenderer.h"
#include "vtkSphereSource.h"
#include "vtkTesting.h"
#include <QApplication>
#include <QSurfaceFormat>
int TestQVTKOpenGLSimpleWidget(int argc, char* argv[])
{
// disable multisampling.
vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(0);
QSurfaceFormat::setDefaultFormat(QVTKOpenGLSimpleWidget::defaultFormat());
QApplication app(argc, argv);
vtkNew<vtkTesting> vtktesting;
vtktesting->AddArguments(argc, argv);
QVTKOpenGLSimpleWidget widget;
{
vtkNew<vtkGenericOpenGLRenderWindow> window0;
widget.SetRenderWindow(window0);
widget.show();
app.processEvents();
}
// make sure rendering works correctly after switching to a new render window
vtkNew<vtkGenericOpenGLRenderWindow> window;
widget.SetRenderWindow(window);
vtkNew<vtkRenderer> ren;
ren->SetGradientBackground(1);
ren->SetBackground2(0.7, 0.7, 0.7);
window->AddRenderer(ren);
vtkNew<vtkSphereSource> sphere;
vtkNew<vtkPolyDataMapper> mapper;
mapper->SetInputConnection(sphere->GetOutputPort());
vtkNew<vtkActor> actor;
actor->SetMapper(mapper);
ren->AddActor(actor);
int* windowSize = window->GetSize();
int* screenSize = window->GetScreenSize();
if (screenSize[0] < windowSize[0] || screenSize[1] < windowSize[1])
{
std::cout << "Expected vtkGenericOpenGLRenderWindow::GetScreenSize() "
"dimensions to be larger than the render window size"
<< std::endl;
return EXIT_FAILURE;
}
vtktesting->SetRenderWindow(window);
widget.update();
app.processEvents();
int retVal = vtktesting->RegressionTest(10);
switch (retVal)
{
case vtkTesting::DO_INTERACTOR:
return app.exec();
case vtkTesting::FAILED:
case vtkTesting::NOT_RUN:
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
<
/*=========================================================================
Program: Visualization Toolkit
Module: TestQVTKOpenGLSimpleWidgetPicking.cxx
Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
All rights reserved.
See Copyright.txt or http://www.kitware.com/Copyright.htm for details.
This software is distributed WITHOUT ANY WARRANTY; without even
the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
PURPOSE. See the above copyright notice for more information.
=========================================================================*/
// Tests picking actors with QVTKOpenGLSimpleWidget and vtkPropPicker.
#include "QVTKOpenGLSimpleWidget.h"
#include "vtkActor2D.h"
#include "vtkCamera.h"
#include "vtkCoordinate.h"
#include "vtkGenericOpenGLRenderWindow.h"
#include "vtkMath.h"
#include "vtkPolyDataMapper2D.h"
#include "vtkProperty2D.h"
#include "vtkPropPicker.h"
#include "vtkRenderer.h"
#include "vtkSphereSource.h"
#include <QApplication>
#include <QSurfaceFormat>
#include <algorithm>
#include <cmath>
#include <vector>
int TestQVTKOpenGLSimpleWidgetPicking(int argc, char* argv[])
{
// Disable multisampling
vtkOpenGLRenderWindow::SetGlobalMaximumNumberOfMultiSamples(0);
QSurfaceFormat::setDefaultFormat(QVTKOpenGLSimpleWidget::defaultFormat());
QApplication app(argc, argv);
QVTKOpenGLSimpleWidget widget;
widget.resize(300, 300);
auto renWin = vtkSmartPointer<vtkGenericOpenGLRenderWindow>::New();
widget.SetRenderWindow(renWin);
auto interactor = renWin->GetInteractor();
auto ren = vtkSmartPointer<vtkRenderer>::New();
ren->GradientBackgroundOn();
ren->SetBackground2(0.7, 0.7, 0.7);
renWin->AddRenderer(ren);
interactor->Render();
const int NumSpheres = 5;
const double SphereRadius = 0.5;
// Add spheres arranged in a circle
std::vector<vtkSmartPointer<vtkActor2D> > actors;
const double Pi2 = 2.0 * vtkMath::Pi();
const double step = Pi2 / NumSpheres;
for (double theta = 0.0; theta < Pi2; theta += step)
{
auto source = vtkSmartPointer<vtkSphereSource>::New();
const double x = sin(theta);
const double y = cos(theta);
const double z = 0.0;
source->SetRadius(SphereRadius);
source->SetCenter(x, y, z);
auto coordinate = vtkSmartPointer<vtkCoordinate>::New();
coordinate->SetCoordinateSystemToWorld();
auto mapper = vtkSmartPointer<vtkPolyDataMapper2D>::New();
mapper->SetInputConnection(source->GetOutputPort());
mapper->SetTransformCoordinate(coordinate);
auto actor = vtkSmartPointer<vtkActor2D>::New();
actor->SetMapper(mapper);
actor->GetProperty()->SetColor(0.62, 0.81, 0.62);
ren->AddActor(actor);
actors.push_back(actor);
}
ren->GetActiveCamera()->SetPosition(0.0, 0.0, 9.0);
widget.show();
app.processEvents();
auto picker = vtkSmartPointer<vtkPropPicker>::New();
auto coordinate = vtkSmartPointer<vtkCoordinate>::New();
coordinate->SetCoordinateSystemToWorld();
// Pick at sphere centers
std::vector<vtkSmartPointer<vtkActor2D> > hits;
for (double theta = 0.0; theta < Pi2; theta += step)
{
const double x = sin(theta);
const double y = cos(theta);
const double z = 0.0;
coordinate->SetValue(x, y, z);
const int* display = coordinate->GetComputedDisplayValue(ren);
picker->Pick(display[0], display[1], 0.0, ren);
auto actor = picker->GetActor2D();
if (actor)
{
actor->GetProperty()->SetColor(0.89, 0.81, 0.67);
}
hits.push_back(actor);
interactor->Render();
app.processEvents();
}
// Pick outside of spheres
std::vector<vtkSmartPointer<vtkActor2D> > misses;
for (double theta = 0.0; theta < Pi2; theta += (0.5 * step))
{
const double x = 2.0 * sin(theta);
const double y = 2.0 * cos(theta);
const double z = 0.0;
coordinate->SetValue(x, y, z);
const int* display = coordinate->GetComputedDisplayValue(ren);
picker->Pick(display[0], display[1], 0.0, ren);
auto actor = picker->GetActor2D();
if (actor)
{