You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Qt嵌入OSG渲染中osgGA::StateSetManipulator的T/L按键无响应排查

Qt嵌入OSG后StateSetManipulator部分按键无响应问题排查

在Qt中嵌入OSG渲染场景,将osgQOpenGLWidget放入作为QMainWindow子类中心部件的QTabWidget多页控件内,添加osgGA::StateSetManipulator用于状态操作。目前W(线框模式切换)、B(背面剔除切换)按键功能正常,但T(纹理显示切换)、L(光照显示切换)按键无响应,以下为问题代码及排查方案:


问题代码实现

头文件(hpp)

class V3dViewerWidget : public QMainWindow 
{
    Q_OBJECT
    public:
    V3dViewerWidget(QWidget* parent = nullptr);
    V3dViewerWidget(osg::ArgumentParser& arguments, QWidget* parent = nullptr);
    V3dViewerWidget(QString externalfile, QWidget* parent = nullptr);
    
    protected slots:
    void initData();
    
    private:
    void WidgetUIinit();
    void setCentralData();
    void setCentralData(osg::ArgumentParser& argument);
    
    private:
    QTabWidget* _mTabCenteralWidget = nullptr;
};

实现文件(cpp)

V3dViewerWidget::V3dViewerWidget(QWidget* parent) : QMainWindow(parent)
{
    WidgetUIinit();
    setCentralData();
}

V3dViewerWidget::V3dViewerWidget(osg::ArgumentParser& arguments, QWidget* parent) : QMainWindow(parent)
{
    WidgetUIinit();
    setCentralData(arguments);
}

V3dViewerWidget::V3dViewerWidget(QString externalfile, QWidget* parent) : QMainWindow(parent)
{
    WidgetUIinit();
    setCentralData();
}

void V3dViewerWidget::WidgetUIinit()
{
    #ifdef HIDE_COMPANY_INFO
    setWindowTitle(tr("Drone_image_modeling_tool") + tr(" Viewer")); //%1 Viewer
    #else
    setWindowTitle(QString("%1 Viewer").arg(PRODUCT_NAME));
    setWindowIcon(QIcon(VIEWER_ICON));
    #endif
    this->setMinimumSize(800, 600);
    // tab部件
    _mTabCenteralWidget = new QTabWidget(this);
    QPalette backcolor(QPalette::Background, Qt::darkGray);
    _mTabCenteralWidget->setAutoFillBackground(true);
    _mTabCenteralWidget->setPalette(backcolor);
    // 设置中心部件
    this->setCentralWidget(_mTabCenteralWidget);
}

void V3dViewerWidget::initData()
{
    // 未执行
    osgQOpenGLWidget* sender = dynamic_cast<osgQOpenGLWidget*>(QObject::sender());
    if (!sender)
        return;
}

void V3dViewerWidget::setCentralData()
{
    // 创建osgQOpenGLWidget
    osgQOpenGLWidget widget;
    // 初始化
    widget.setMouseTracking(true);
    widget.setFocusPolicy(Qt::StrongFocus);
    // 数据加载
    connect(&widget, SIGNAL(initialized()), this, SLOT(initData()));
}

void V3dViewerWidget::setCentralData(osg::ArgumentParser& argument)
{
    // 创建osgQOpenGLWidget
    osgQOpenGLWidget* widget = new osgQOpenGLWidget(&argument);
    // 初始化
    widget->setMouseTracking(true);
    widget->setFocusPolicy(Qt::StrongFocus);
    // 添加标签页
    _mTabCenteralWidget->addTab(widget, "osgb_test");
    // 数据加载
    connect(widget, &osgQOpenGLWidget::initialized, [&argument, widget]() 
    {
    // 设置视口操作器与事件
    osgViewer::Viewer* viewer = widget->getOsgViewer();
    widget->getOsgViewer()->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
    widget->getOsgViewer()->setCameraManipulator(new osgGA::TrackballManipulator());
    // 加载模型
    osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile("D:/x/View_test/test.osgb");
    // osg::ref_ptr<osg::Node> loadedModel = osgDB::readRefNodeFiles(argument);
    if (!loadedModel) 
    {
        return;
    }
    osgUtil::Optimizer optimizer;
    optimizer.optimize(loadedModel);
    // 设置场景数据
    widget->getOsgViewer()->setSceneData(loadedModel.get());
});
}

主函数(main)

int main(int argc, char** argv)
{
// OpenGL设置
    QSurfaceFormat format = QSurfaceFormat::defaultFormat();
    
    #ifdef OSG_GL3_AVAILABLE
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setRenderableType(QSurfaceFormat::OpenGL);
    format.setOption(QSurfaceFormat::DebugContext);
    #else
    format.setVersion(2, 0);
    format.setProfile(QSurfaceFormat::CompatibilityProfile);
    format.setRenderableType(QSurfaceFormat::OpenGL);
    format.setOption(QSurfaceFormat::DebugContext);
    #endif
    format.setDepthBufferSize(24);
    // format.setAlphaBufferSize(8);
    format.setSamples(8);
    format.setStencilBufferSize(8);
    format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
    QSurfaceFormat::setDefaultFormat(format);
    // 应用程序
    QApplication qapp(argc, argv);
    
    // 显示部件
    osg::ArgumentParser arguments(&argc, argv);
    V3dViewerWidget     w(arguments);
    w.show();
    return qapp.exec();
}

可正常运行的示例代码

int main(int argc, char** argv)
{
    QSurfaceFormat format = QSurfaceFormat::defaultFormat();
    
    #ifdef OSG_GL3_AVAILABLE
    format.setVersion(3, 2);
    format.setProfile(QSurfaceFormat::CoreProfile);
    format.setRenderableType(QSurfaceFormat::OpenGL);
    format.setOption(QSurfaceFormat::DebugContext);
    #else
    format.setVersion(2, 0);
    format.setProfile(QSurfaceFormat::CompatibilityProfile);
    format.setRenderableType(QSurfaceFormat::OpenGL);
    format.setOption(QSurfaceFormat::DebugContext);
    #endif
    format.setDepthBufferSize(24);
    // format.setAlphaBufferSize(8);
    format.setSamples(8);
    format.setStencilBufferSize(8);
    format.setSwapBehavior(QSurfaceFormat::DoubleBuffer);
    QSurfaceFormat::setDefaultFormat(format);
    QApplication qapp(argc, argv);
    //
    QTabWidget Twidget;
    
    osgQOpenGLWidget mwidget;
    osgQOpenGLWidget ms_widget;
    mwidget.setFocusPolicy(Qt::StrongFocus);
    ms_widget.setFocusPolicy(Qt::StrongFocus);
    
    Twidget.addTab(&mwidget, "osgb_test");
    Twidget.addTab(&ms_widget, "ply_test");

    QObject::connect(&mwidget, &osgQOpenGLWidget::initialized, [&]() 
   {
        osgViewer::Viewer* viewer = mwidget.getOsgViewer();
        if (!viewer) 
        {
            qWarning("Failed to get OSG Viewer");
            return -1;
        }
        viewer->setCameraManipulator(new osgGA::TrackballManipulator());
        viewer->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
        osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile("D:/x/View_test/test.osgb");
        osgUtil::Optimizer optimizer;
        optimizer.optimize(loadedModel);
        viewer->setSceneData(loadedModel);
        return 0;
    });

    QObject::connect(&ms_widget, &osgQOpenGLWidget::initialized, [&]() 
    {
        osgViewer::Viewer* viewer = ms_widget.getOsgViewer();
        if (!viewer) 
        {
            qWarning("Failed to get OSG Viewer");
            return -1;
        }
        viewer->setCameraManipulator(new osgGA::TrackballManipulator());
        osgGA::StateSetManipulator* state = new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet());
        viewer->addEventHandler(state);
        osg::ref_ptr<osg::Node> loadedModel = osgDB::readNodeFile("E:/x/mesh.ply");      
        osgUtil::Optimizer optimizer;
        optimizer.optimize(loadedModel);
        viewer->setSceneData(loadedModel);
        return 0;
    });
    Twidget.show();
    return qapp.exec();
}

问题原因与解决办法

核心原因

  1. QTabWidget拦截按键:Qt的QTabWidget默认会拦截部分按键(如T、L)作为潜在的标签页快捷键,导致这些按键无法传递到下层的osgQOpenGLWidget。
  2. 焦点未正确设置:即使给osgQOpenGLWidget设置了Qt::StrongFocus,控件也可能未实际获得焦点,按键事件被上层窗口部件捕获。

解决步骤

1. 自定义QTabWidget放行指定按键

重写QTabWidget的事件过滤逻辑,阻止其拦截T、L按键:

class CustomTabWidget : public QTabWidget
{
protected:
    bool eventFilter(QObject* obj, QEvent* event) override
    {
        if (event->type() == QEvent::KeyPress)
        {
            QKeyEvent* keyEvent = static_cast<QKeyEvent*>(event);
            // 放行T、L按键,让OSG处理
            if (keyEvent->key() == Qt::Key_T || keyEvent->key() == Qt::Key_L)
            {
                return false;
            }
        }
        return QTabWidget::eventFilter(obj, event);
    }
};

将原代码中的QTabWidget* _mTabCenteralWidget替换为CustomTabWidget* _mTabCenteralWidget,初始化时创建CustomTabWidget实例。

2. 确保osgQOpenGLWidget获得焦点

在osgQOpenGLWidget的initialized回调中添加焦点设置:

connect(widget, &osgQOpenGLWidget::initialized, [&argument, widget]() 
{
    osgViewer::Viewer* viewer = widget->getOsgViewer();
    widget->getOsgViewer()->addEventHandler(new osgGA::StateSetManipulator(viewer->getCamera()->getOrCreateStateSet()));
    widget->getOsgViewer()->setCameraManipulator(new osgGA::TrackballManipulator());
    // 强制设置控件获得焦点
    widget->setFocus();
    // 后续模型加载代码...
});

3. 验证事件传递(可选)

自定义osgQOpenGLWidget重写按键事件,确认按键是否到达控件:

class CustomOSGWidget : public osgQOpenGLWidget
{
protected:
    void keyPressEvent(QKeyEvent* event) override
    {
        qDebug() << "收到按键:" << event->key();
        osgQOpenGLWidget::keyPressEvent(event);
    }
};

替换原代码中的osgQOpenGLWidget为该自定义类,运行后查看控制台输出,确认T、L按键是否被捕获。


内容的提问来源于stack exchange,提问作者ZH Huang

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.19 04:35:00