我正在开发一个使用 OpenVR 与 HTC Vive 套件通信的 C++ 项目。这个想法是开发一个带有菜单的库,在 VR 环境中显示,用户可以从中更改程序的一些参数。
我正在尝试将此类菜单实现为 QWidget,如官方 OpenVR 示例中所示。我的问题是,在这个项目中已经有一个主(我可以访问但我不想干扰它)在我的代码执行之前创建和启动一个 QApplication 。
我设法创建了 QWidget,但由于某些原因,它的插槽根本没有被调用。我怀疑这与我没有以某种方式在事件循环中“注册”我的 QWidget 的事实有关,但问题可能出在其他地方。
简而言之,如果这甚至是问题,那么如果 QApplication 已经在运行,那么连接槽和信号如何工作?
这是我的测试 QWidget,非常基础。
#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();
}
这是控制器,直接取自我上面链接的 OpenVR 示例。我剥离了所有不必要的东西
#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 );
}
}
这是我在我的库的其他类之一的构造函数中设置这两者的方法(只要按下应用程序的“VR”按钮,在应用程序已经运行的任何时候都会调用它) .
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);
提前谢谢你,洛伦佐
编辑:添加了相关代码。 编辑:切换到 Qt5 样式进行连接。它仍然可以编译和运行,但永远不会调用这些事件。