1

I am working on a C++ project which uses OpenVR to communicate with a HTC Vive kit. The idea is to develop a library with a menu to be shown in the VR environment, from which the user can change some parameters of the program.

I am trying to implement such menu as a QWidget, as shown in the official OpenVR examples. My problem is that in this project there already is a main (to which I have access but I prefer not interfering with it) where a QApplication is created and launched before my code executes.

I managed to create the QWidget, but for some reasons its slots are not being called at all. I suspect it has to do with the fact that I didn't "register" my QWidget in the event loop in some way, but the problem may be elsewhere.

In short, if that is even the problem, how does connecting slots and signals work, if a QApplication is already running?


This is my test QWidget, really basic.
#include "overlaywidget.h"
#include "ui_overlaywidget.h"

OverlayWidget::OverlayWidget(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::OverlayWidget)
{
    ui->setupUi(this);
}

OverlayWidget::~OverlayWidget()
{
    delete ui;
}

void OverlayWidget::on_pushButton_clicked()
{
    QApplication::quit();
}

This is the controller, taken directly from the OpenVR example i linked above. I stripped away everything that was not necessary

#include "openvroverlaycontroller.h"

#include <QOpenGLFramebufferObjectFormat>
#include <QOpenGLPaintDevice>
#include <QPainter>
#include <QtWidgets/QWidget>
#include <QMouseEvent>
#include <QtWidgets/QGraphicsSceneMouseEvent>
#include <QtWidgets/QApplication>
#include <QtWidgets/QGraphicsEllipseItem>
#include <QCursor>
#include <QObject>
#include <QMainWindow>

using namespace vr;

COpenVROverlayController *s_pSharedVRController = NULL; // I guess this makes it visibile in the global scope

COpenVROverlayController *COpenVROverlayController::SharedInstance()
{
    if ( !s_pSharedVRController )
    {
        s_pSharedVRController = new COpenVROverlayController(QApplication::instance());
    }
    return s_pSharedVRController;
}

COpenVROverlayController::COpenVROverlayController(QObject * parent)
    : BaseClass(parent)
    , m_eLastHmdError( vr::VRInitError_None )
    , m_eCompositorError( vr::VRInitError_None )
    , m_eOverlayError( vr::VRInitError_None )
    , m_strVRDriver( "No Driver" )
    , m_strVRDisplay( "No Display" )
    , m_pOpenGLContext( NULL )
    , m_pScene( NULL )
    , m_pOffscreenSurface ( NULL )
    , m_pFbo( NULL )
    , m_pWidget( NULL )
    , m_pPumpEventsTimer( NULL )
    , m_lastMouseButtons( 0 )
    , m_ulOverlayHandle( vr::k_ulOverlayHandleInvalid )
    , m_bManualMouseHandling( false )
{
}

bool COpenVROverlayController::Init()
{
    bool bSuccess = true;

    m_strName = "systemoverlay";

    QStringList arguments = qApp->arguments();

    int nNameArg = arguments.indexOf( "-name" );
    if( nNameArg != -1 && nNameArg + 2 <= arguments.size() )
    {
        m_strName = arguments.at( nNameArg + 1 );
    }

    QSurfaceFormat format;
    format.setMajorVersion( 4 );
    format.setMinorVersion( 1 );
    format.setProfile( QSurfaceFormat::CompatibilityProfile );

    m_pOpenGLContext = new QOpenGLContext();
    m_pOpenGLContext->setFormat( format );
    bSuccess = m_pOpenGLContext->create();
    if( !bSuccess )
        return false;

    // create an offscreen surface to attach the context and FBO to
    m_pOffscreenSurface = new QOffscreenSurface();
    m_pOffscreenSurface->create();
    m_pOpenGLContext->makeCurrent( m_pOffscreenSurface );

    m_pScene = new QGraphicsScene();

    // This is where I connect to the QGraphicsScene
    connect(m_pScene, &QGraphicsScene::changed, this, &COpenVROverlayController::OnSceneChanged);

    // Loading the OpenVR Runtime
    bSuccess = ConnectToVRRuntime();

    bSuccess = bSuccess && vr::VRCompositor() != NULL;

    if( vr::VROverlay() )
    {
        std::string sKey = std::string( "sample." ) + m_strName.toStdString();

        vr::VROverlayError overlayError = vr::VROverlay()->CreateDashboardOverlay( sKey.c_str(), m_strName.toStdString().c_str(), &m_ulOverlayHandle, &m_ulOverlayThumbnailHandle);

        bSuccess = bSuccess && overlayError == vr::VROverlayError_None;
    }

    if( bSuccess )
    {
        vr::VROverlay()->SetOverlayWidthInMeters( m_ulOverlayHandle,1.5f );
        vr::VROverlay()->SetOverlayInputMethod( m_ulOverlayHandle, vr::VROverlayInputMethod_Mouse );

        m_pPumpEventsTimer = new QTimer( this );

        // This is where I connect to the QTimer
        connect(m_pPumpEventsTimer, &QTimer::timeout, this, &COpenVROverlayController::OnTimeoutPumpEvents);
        m_pPumpEventsTimer->setInterval( 20 );
        m_pPumpEventsTimer->start();


    }
    return true;
}

// First slot, never being called
void COpenVROverlayController::OnSceneChanged( const QList<QRectF>& )
{
    /* ... code ... */
}


// Second slot, never being called
void COpenVROverlayController::OnTimeoutPumpEvents()
{
    /* ... */

}


void COpenVROverlayController::SetWidget( QWidget *pWidget )
{
    if( m_pScene )
    {
        // all of the mouse handling stuff requires that the widget be at 0,0
        // (else, the widget will still be there, but it will behave as if it's trasnlated)
        pWidget->move( 0, 0 );
        m_pScene->addWidget( pWidget );
    }
    m_pWidget = pWidget;

    m_pFbo = new QOpenGLFramebufferObject( pWidget->width(), pWidget->height(), GL_TEXTURE_2D );

    if( vr::VROverlay() )
    {
        vr::HmdVector2_t vecWindowSize =
        {
            (float)pWidget->width(),
            (float)pWidget->height()
        };
        vr::VROverlay()->SetOverlayMouseScale( m_ulOverlayHandle, &vecWindowSize );
    }

}

And here is how I set up the two, in the constructor of one of the other classes of my library (which is called as soon as the "VR" button of the application is pressed, at any point when the application is already running).

  OverlayWidget *pOverlayWidget = new OverlayWidget(this->getMainWindow());

  // Basicallly creates a new OverlayController and calls Init
  this->controller = COpenVROverlayController::SharedInstance();
  this->controller->Init();

  // On that same controller, sets the OverlayWidget
  this->controller->SetWidget(pOverlayWidget);

Thank you in advance, Lorenzo

EDIT: Added relevant code. EDIT: Switched to Qt5 style for connects. It still compiles and runs, but the events are never called.

  • 3
    you don't need to register the widgets in the event loop in any way. show us the code, and yes, connecting signals works if a qapplication is already running. – Tomaz Canabrava Jun 09 '17 at 08:39
  • Thank you Tomaz, I added the relevant code. – vrcranfield Jun 09 '17 at 09:03
  • Are you using Qt4 or Qt5? The connect style is Qt4, if you can make them Qt5, this would make the error easy to spot (you would get a compile error if your syntax is wrong for instance.) can you try that and compile the code to see if the code actually compiles? – Tomaz Canabrava Jun 09 '17 at 09:55
  • I am using Qt5. However, I'm pretty much new to Qt so I didn't know that the syntax I was using is Qt4. I will update it right now and see what happens. By the way, the code compiles and runs just fine as it is, it's just that if I put breakpoints on the slots, I see they are never being called. I will update you once I switch to Qt5 connects. – vrcranfield Jun 09 '17 at 10:02
  • I changed the connect syntax (as you can see in the OP). It still compiles and runs, but the slots are never called. – vrcranfield Jun 09 '17 at 10:10
  • Actually, no, this means that the signal is never emitted. Now you need to do some graphical debugging, as the code doesn't tells me what could be happening. Plesae, use KDAB's GammaRay tool to examine your applicattion. GammaRay is a visual debugger for Qt applications and it lets you examine signal / slots, fire signals by hand and such. – Tomaz Canabrava Jun 09 '17 at 10:26
  • To further confirm what you just said, I tried checking the return value of the connects (which is true for both, as expected) and to manually emit the signals, which resulted in the slot being called. So the problem lies in the signals not being emitted, as you suggested. Thank you again, I'll check it right away and report back. – vrcranfield Jun 09 '17 at 10:30

0 Answers0