Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using QVTKWidget and QOpenGLWidget in the same UI?

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.

like image 870
w1th0utnam3 Avatar asked Nov 15 '14 10:11

w1th0utnam3


1 Answers

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;
}
like image 163
w1th0utnam3 Avatar answered Nov 16 '22 00:11

w1th0utnam3