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.