I'm working on a project that uses a custom subclass of QOpenGLWidget
to display some renderings. At the end of the paintGL()
method it calls the widget's update()
method to trigger a repaint event if it's visible.
Now I want to add an additional QVTKWidget
to my Ui and I do so by using something like this:
QVBoxLayout* layout = findChild<QVBoxLayout*>("layout_simulation");
QVTKWidget* widget = new QVTKWidget();
// Setup arrow
vtkSmartPointer<vtkArrowSource> arrowSource = vtkSmartPointer<vtkArrowSource>::New();
[more VTK stuff...]
widget->GetRenderWindow()->AddRenderer(renderer);
renderer->AddActor(arrowActor);
renderer->ResetCamera();
// Add widget to ui
layout->addWidget(widget);
The VTK widget is added to the ui and is working as intended. The problem is that as soon as I use the layout->addWidget()
method all my other QOpenGLWidget
objects turn black and don't display anything. Also this black is not the background color of the VTKRenderer
because the other widgets are still black even if I change the background color of the VTK widget. I checked and the paintGL()
still gets called in a loop but it just doesn't display anything. I'm pretty sure that I didn't use any bad OpenGL code in my widget subclass so I guess it has something to do with the internal initialization of QOpenGLWidget
and QVTKWidget
. The same happens if I omit all the VTK calls and just add the newly created QVTKWidget
.
The funny thing is that if I omit the layout->addWidget()
call, VTK opens the renderer in a seperate window and all the OpenGL widgets work without a problem. But of course I want to embed the renderer in my ui.
Does anyone have experience with this or do you know any pitfalls I might have stepped into or are there any common problems that may cause this?
BTW: I use a OpenGL 3.3 core profile with custom shaders for my QOpenGLWidget
subclass.
EDIT: I removed my request for a 3.3 core profile and now it uses a 4.4 compatibility profile. I guess it's now using the same profile as the VTKRenderer
so this can be ruled out as a source for the error.
So I don't know what exactly caused the problem but I solved it. At first I found out that there is a second widget class for VTK called QVTKWidget2
but unfortunately it isn't fully compatible to Qt 5.4 because it uses the old QGL classes (like QGLWidget
instead of QOpenGLWidget
). So I started to re-implement it as a subclass of QOpenGLWidget
which was quite straight forward. Below are the source files for my class QVTKWidget3
that works with Qt 5.4 for anyone who is interested. I did only limited testing (because I'm still learning VTK) but simple examples like displaying a sphere work.
Again: this only works with Qt 5.4 (beta) because older versionen don't provide the QOpenGLWidget class!
Header file: qvtkwidget3.h
#ifndef QVTKWIDGET3_H
#define QVTKWIDGET3_H
#include "vtkSmartPointer.h"
#include "vtkGenericOpenGLRenderWindow.h"
#include "vtkEventQtSlotConnect.h"
#include "QVTKInteractorAdapter.h"
#include "QVTKInteractor.h"
#include <QOpenGLWidget>
#include <QSurfaceFormat>
class QVTKWidget3 : public QOpenGLWidget
{
Q_OBJECT
public:
QVTKWidget3(QWidget *parent = NULL, Qt::WindowFlags f = 0, QSurfaceFormat format = QSurfaceFormat::defaultFormat());
virtual ~QVTKWidget3();
//! Set a custom render window
virtual void SetRenderWindow(vtkGenericOpenGLRenderWindow*);
//! Returns the curren render window (creates one if none exists)
virtual vtkGenericOpenGLRenderWindow* GetRenderWindow();
//! Returns interactor of the current render window
virtual QVTKInteractor* GetInteractor();
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);
protected:
//! Initialize handler
virtual void initializeGL();
//! Paint handler
virtual void paintGL();
//! Resize handler
virtual void resizeGL(int, int);
//! Move handler
virtual void moveEvent(QMoveEvent* event);
virtual void mousePressEvent(QMouseEvent* event);
virtual void mouseMoveEvent(QMouseEvent* event);
virtual void mouseReleaseEvent(QMouseEvent* event);
virtual void keyPressEvent(QKeyEvent* event);
virtual void keyReleaseEvent(QKeyEvent* event);
virtual void enterEvent(QEvent*);
virtual void leaveEvent(QEvent*);
virtual void wheelEvent(QWheelEvent*);
virtual void contextMenuEvent(QContextMenuEvent*);
virtual void dragEnterEvent(QDragEnterEvent*);
virtual void dragMoveEvent(QDragMoveEvent*);
virtual void dragLeaveEvent(QDragLeaveEvent*);
virtual void dropEvent(QDropEvent*);
virtual bool focusNextPrevChild(bool);
// Members
vtkGenericOpenGLRenderWindow* m_renWin;
QVTKInteractorAdapter* m_irenAdapter;
vtkSmartPointer<vtkEventQtSlotConnect> m_connect;
private:
//! unimplemented operator=
QVTKWidget3 const& operator=(QVTKWidget3 const&);
//! unimplemented copy
QVTKWidget3(const QVTKWidget3&);
};
#endif // QVTKWIDGET3_H
Source file: qvtkwidget3.cpp
#include "qvtkwidget3.h"
#include "vtkRenderWindowInteractor.h"
#include "vtkInteractorStyle.h"
#include "vtkInteractorStyleTrackballCamera.h"
#include <QResizeEvent>
QVTKWidget3::QVTKWidget3(QWidget *parent, Qt::WindowFlags f, QSurfaceFormat format)
: QOpenGLWidget(parent, f)
, m_renWin(nullptr)
{
// VTK requires a compatibility profile
format.setProfile(QSurfaceFormat::CompatibilityProfile);
setFormat(format);
// Initialize interactors
m_irenAdapter = new QVTKInteractorAdapter(this);
m_connect = vtkSmartPointer<vtkEventQtSlotConnect>::New();
}
// Destructor
QVTKWidget3::~QVTKWidget3()
{
// Following line is not needed.
// get rid of the VTK window
// this->SetRenderWindow(NULL);
}
// GetRenderWindow
vtkGenericOpenGLRenderWindow* QVTKWidget3::GetRenderWindow()
{
if (this->m_renWin == nullptr)
{
// create a default vtk window
vtkGenericOpenGLRenderWindow* win = vtkGenericOpenGLRenderWindow::New();
this->SetRenderWindow(win);
}
return this->m_renWin;
}
// SetRenderWindow
void QVTKWidget3::SetRenderWindow(vtkGenericOpenGLRenderWindow* w)
{
// do nothing if we don't have to
if(w == this->m_renWin) {
return;
}
// unregister previous window
if(this->m_renWin != nullptr) {
this->m_renWin->Finalize();
this->m_renWin->SetMapped(0);
m_connect->Disconnect(m_renWin, vtkCommand::WindowMakeCurrentEvent, this, SLOT(MakeCurrent()));
m_connect->Disconnect(m_renWin, vtkCommand::WindowIsCurrentEvent, this, SLOT(IsCurrent(vtkObject*, unsigned long, void*, void*)));
m_connect->Disconnect(m_renWin, vtkCommand::WindowFrameEvent, this, SLOT(Frame()));
m_connect->Disconnect(m_renWin, vtkCommand::StartEvent, this, SLOT(Start()));
m_connect->Disconnect(m_renWin, vtkCommand::EndEvent, this, SLOT(End()));
m_connect->Disconnect(m_renWin, vtkCommand::WindowIsDirectEvent, this, SLOT(IsDirect(vtkObject*, unsigned long, void*, void*)));
m_connect->Disconnect(m_renWin, vtkCommand::WindowSupportsOpenGLEvent, this, SLOT(SupportsOpenGL(vtkObject*, unsigned long, void*, void*)));
}
// now set the window
this->m_renWin = w;
if(this->m_renWin != nullptr) {
// if it is mapped somewhere else, unmap it
this->m_renWin->Finalize();
this->m_renWin->SetMapped(1);
// tell the vtk window what the size of this window is
this->m_renWin->SetSize(this->width(), this->height());
this->m_renWin->SetPosition(this->x(), this->y());
// if an interactor wasn't provided, we'll make one by default
if(this->m_renWin->GetInteractor() == NULL)
{
// create a default interactor
QVTKInteractor* iren = QVTKInteractor::New();
iren->SetUseTDx(false);
this->m_renWin->SetInteractor(iren);
iren->Initialize();
// now set the default style
vtkInteractorStyle* s = vtkInteractorStyleTrackballCamera::New();
iren->SetInteractorStyle(s);
iren->Delete();
s->Delete();
}
// tell the interactor the size of this window
this->m_renWin->GetInteractor()->SetSize(this->width(), this->height());
m_connect->Connect(m_renWin, vtkCommand::WindowMakeCurrentEvent, this, SLOT(MakeCurrent()));
m_connect->Connect(m_renWin, vtkCommand::WindowIsCurrentEvent, this, SLOT(IsCurrent(vtkObject*, unsigned long, void*, void*)));
m_connect->Connect(m_renWin, vtkCommand::WindowFrameEvent, this, SLOT(Frame()));
m_connect->Connect(m_renWin, vtkCommand::StartEvent, this, SLOT(Start()));
m_connect->Connect(m_renWin, vtkCommand::EndEvent, this, SLOT(End()));
m_connect->Connect(m_renWin, vtkCommand::WindowIsDirectEvent, this, SLOT(IsDirect(vtkObject*, unsigned long, void*, void*)));
m_connect->Connect(m_renWin, vtkCommand::WindowSupportsOpenGLEvent, this, SLOT(SupportsOpenGL(vtkObject*, unsigned long, void*, void*)));
}
}
// GetInteractor
QVTKInteractor* QVTKWidget3::GetInteractor()
{
return QVTKInteractor::SafeDownCast(this->GetRenderWindow()->GetInteractor());
}
// Initialize
void QVTKWidget3::initializeGL()
{
if(this->m_renWin == nullptr) {
return;
}
this->m_renWin->OpenGLInitContext();
}
// Paint
void QVTKWidget3::paintGL()
{
vtkRenderWindowInteractor* iren = nullptr;
if(this->m_renWin != nullptr) {
iren = this->m_renWin->GetInteractor();
}
if(iren == nullptr || !iren->GetEnabled()) {
return;
}
iren->Render();
}
// Resize
void QVTKWidget3::resizeGL(int w, int h)
{
if(this->m_renWin == nullptr) {
return;
}
this->m_renWin->SetSize(w,h);
// and update the interactor
if(this->m_renWin->GetInteractor() != NULL) {
QResizeEvent e(QSize(w,h), QSize());
m_irenAdapter->ProcessEvent(&e, this->m_renWin->GetInteractor());
}
}
// Move
void QVTKWidget3::moveEvent(QMoveEvent* e)
{
QWidget::moveEvent(e);
if(this->m_renWin == nullptr) {
return;
}
this->m_renWin->SetPosition(this->x(), this->y());
}
// --------
// Slots
// --------
void QVTKWidget3::Start()
{
makeCurrent();
m_renWin->PushState();
m_renWin->OpenGLInitState();
}
void QVTKWidget3::End()
{
m_renWin->PopState();
}
void QVTKWidget3::MakeCurrent()
{
return;
// Automaticly handled by QOpenGLWidget
// this->makeCurrent();
}
void QVTKWidget3::IsCurrent(vtkObject*, unsigned long, void*, void* call_data)
{
bool* ptr = reinterpret_cast<bool*>(call_data);
*ptr = (int)true;
}
void QVTKWidget3::IsDirect(vtkObject*, unsigned long, void*, void* call_data)
{
int* ptr = reinterpret_cast<int*>(call_data);
*ptr = (int)true;
}
void QVTKWidget3::SupportsOpenGL(vtkObject*, unsigned long, void*, void* call_data)
{
int* ptr = reinterpret_cast<int*>(call_data);
*ptr = (int)true;
}
void QVTKWidget3::Frame()
{
if(m_renWin->GetSwapBuffers()) {
this->update();
}
// This callback will call swapBuffers() for us
// because sometimes VTK does a render without coming through this paintGL()
// FOLLOWING NOT TESTED FOR QOPENGLWIDGET
// if you want paintGL to always be called for each time VTK renders
// 1. turn off EnableRender on the interactor,
// 2. turn off SwapBuffers on the render window,
// 3. add an observer for the RenderEvent coming from the interactor
// 4. implement the callback on the observer to call updateGL() on this widget
// 5. overload QVTKWidget3::paintGL() to call m_renWin->Render() instead iren->Render()
}
// ----------------------
// Interaction handlers
// ----------------------
/*! handle mouse press event
*/
void QVTKWidget3::mousePressEvent(QMouseEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
/*! handle mouse move event
*/
void QVTKWidget3::mouseMoveEvent(QMouseEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
/*! handle enter event
*/
void QVTKWidget3::enterEvent(QEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
/*! handle leave event
*/
void QVTKWidget3::leaveEvent(QEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
/*! handle mouse release event
*/
void QVTKWidget3::mouseReleaseEvent(QMouseEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
/*! handle key press event
*/
void QVTKWidget3::keyPressEvent(QKeyEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
/*! handle key release event
*/
void QVTKWidget3::keyReleaseEvent(QKeyEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
void QVTKWidget3::wheelEvent(QWheelEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
void QVTKWidget3::contextMenuEvent(QContextMenuEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
void QVTKWidget3::dragEnterEvent(QDragEnterEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
void QVTKWidget3::dragMoveEvent(QDragMoveEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
void QVTKWidget3::dragLeaveEvent(QDragLeaveEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
void QVTKWidget3::dropEvent(QDropEvent* e)
{
if(this->m_renWin)
{
m_irenAdapter->ProcessEvent(e, this->m_renWin->GetInteractor());
}
}
bool QVTKWidget3::focusNextPrevChild(bool)
{
return false;
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With