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

Qt使用createWindowContainer嵌入QWindow出现双窗口问题求助

问题:QMainWindow嵌入QWindow派生类后出现两个窗口

我在Qt应用中实现了继承自QWindow的VtsWindow类,希望将其添加到QMainWindow中,因此使用了QWidget::createWindowContainer方法。但将其设置为中心部件后,出现了两个窗口而非预期的一个。
bug image

VtsWindow类代码

#include "common.hpp"
#include <QGuiApplication>
#include <QMouseEvent>
#include <QWheelEvent>
#include <vts-browser/log.hpp>
#include <vts-browser/map.hpp>
#include <vts-browser/mapOptions.hpp>
#include <vts-browser/camera.hpp>
#include <vts-browser/navigation.hpp>
#include <vts-renderer/renderer.hpp>
#include <stdexcept>
#include <QDebug>
static Gl *openglFunctionPointerInstance;

void *openglFunctionPointerProc(const char *name)
{
    return (void*)openglFunctionPointerInstance->getProcAddress(name);
}

Gl::Gl(QSurface *surface) : surface(surface)
{}

void Gl::current(bool bind)
{
    if (bind)
        makeCurrent(surface);
    else
        doneCurrent();
}

void Gl::initialize()
{
    setFormat(surface->format());
    create();
    if (!isValid())
        throw std::runtime_error("unable to create opengl context");
}

void DataThread::run()
{
    vts::setLogThreadName("data");
    gl->current();
    vts::renderer::installGlDebugCallback();
    map->dataAllRun();
}

VtsWindow::VtsWindow()
{
    QSurfaceFormat format;
    format.setVersion(3, 3);
    format.setProfile(QSurfaceFormat::CoreProfile);
#ifndef NDEBUG
    format.setOption(QSurfaceFormat::DebugContext);
#endif
    format.setDepthBufferSize(0);
    format.setStencilBufferSize(0);
    setFormat(format);
    setSurfaceType(QWindow::OpenGLSurface);

   resize(QSize(800, 600));
   show(); // 导致双窗口的关键代码

    dataThread.gl = std::make_shared<Gl>(this);
    dataThread.gl->initialize();
    dataThread.gl->current(false);

    gl = std::make_shared<Gl>(this);
    gl->setShareContext(dataThread.gl.get());
    gl->initialize();
    gl->current();

    openglFunctionPointerInstance = gl.get();
    vts::renderer::loadGlFunctions(&openglFunctionPointerProc);

    vts::MapCreateOptions mapopts;
    mapopts.clientId = "vts-browser-qt";
    map = dataThread.map = std::make_shared<vts::Map>(mapopts);
    map->setMapconfigPath(
            "http://192.168.4.104:9000/store/vts/gebco_global/mapConfig.json",
            "");
    camera = map->createCamera();
    navigation = camera->createNavigation();

    context = std::make_shared<vts::renderer::RenderContext>();
    context->bindLoadFunctions(map.get());
    view = context->createView(camera.get());

    dataThread.gl->moveToThread(&dataThread);
    dataThread.start();

    lastTime = std::chrono::high_resolution_clock::now();
    requestUpdate();
    hide();
}

VtsWindow::~VtsWindow()
{
    view.reset();
    navigation.reset();
    camera.reset();
    if (map)
        map->renderFinalize();
    dataThread.wait();
}

void VtsWindow::mouseMove(QMouseEvent *event)
{
    QPoint diff = event->globalPos() - lastMousePosition;
    lastMousePosition = event->globalPos();
    double n[3] = { (double)diff.x(), (double)diff.y(), 0 };
    if (event->buttons() & Qt::LeftButton)
        navigation->pan(n);
    if ((event->buttons() & Qt::RightButton)
        || (event->buttons() & Qt::MiddleButton))
        navigation->rotate(n);
}

void VtsWindow::mousePress(QMouseEvent *)
{}

void VtsWindow::mouseRelease(QMouseEvent *)
{}

void VtsWindow::mouseWheel(QWheelEvent *event)
{
    navigation->zoom(event->angleDelta().y() / 120.0);
}

bool VtsWindow::event(QEvent *event)
{
    switch (event->type())
    {
    case QEvent::UpdateRequest:
        tick();
        return true;
    case QEvent::MouseMove:
        mouseMove(dynamic_cast<QMouseEvent*>(event));
        return true;
    case QEvent::MouseButtonPress:
        mousePress(dynamic_cast<QMouseEvent*>(event));
        return true;
    case QEvent::MouseButtonRelease:
        mouseRelease(dynamic_cast<QMouseEvent*>(event));
        return true;
    case QEvent::Wheel:
        mouseWheel(dynamic_cast<QWheelEvent*>(event));
        return true;
    default:
        return QWindow::event(event);
    }
}

void VtsWindow::tick()
{
    auto currentTime = std::chrono::high_resolution_clock::now();
    double elapsedTime = std::chrono::duration_cast<std::chrono::microseconds>(
                currentTime - lastTime).count() * 1e-6;
    lastTime = currentTime;

    requestUpdate();
    if (!isExposed())
        return;

    if (!gl->isValid())
        throw std::runtime_error("invalid gl context");

    gl->makeCurrent(this);
    QSize size = QWindow::size();

    // release build -> catch exceptions and close the window
    // debug build -> let the debugger handle the exceptions
#ifdef NDEBUG
    try
    {
#endif

        map->renderUpdate(elapsedTime);
        camera->setViewportSize(size.width(), size.height());
        camera->renderUpdate();
        view->options().targetFrameBuffer = gl->defaultFramebufferObject();
        view->options().width = size.width();
        view->options().height = size.height();
        view->render();

#ifdef NDEBUG
    }
    catch(...)
    {
        this->close();
        return;
    }
#endif

    // finish the frame
    gl->swapBuffers(this);
}

main函数代码

#include <clocale>
#include <QApplication>
#include <QWindow>
#include <QtWidgets>
#include <QVBoxLayout>
#include "common.hpp"
#include "mainwindow.h"
//#include <vts-renderer/highPerformanceGpuHint.h>

int main(int argc, char *argv[])
{
    QApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
    QApplication app(argc, argv);
    std::setlocale(LC_ALL, "C");

    // Create the main window
       MainWindow mainWindow;

       QWidget *centralWidget = new QWidget(&mainWindow);
       QVBoxLayout *layout = new QVBoxLayout(centralWidget);
       VtsWindow vtsWindow;
      QWidget *container =  QWidget::createWindowContainer(&vtsWindow);
       layout->addWidget(container);
       mainWindow.setCentralWidget(centralWidget);

       // Show the main window
       mainWindow.show();


    // run the main event loop
    return app.exec();
}

原因及解决方法

出现双窗口的核心原因是VtsWindow构造函数中调用了show()方法:

  • QWindow调用show()会直接以独立窗口的形式显示;
  • 之后用createWindowContainer将其包装为QWidget并嵌入QMainWindow时,容器又会触发它的显示逻辑,最终导致两个窗口同时出现。

修复方式

直接删除VtsWindow构造函数中的show()调用即可:

// resize(QSize(800, 600));
// show(); // 删掉这行代码

补充说明

  • 嵌入后的VtsWindow的显示状态由父容器和QMainWindow控制,无需自行调用show();
  • 构造函数末尾的hide()可以保留,但不会影响嵌入后的显示效果,因为主窗口显示时会自动让容器内的窗口可见;
  • 建议将固定resize的逻辑改为由布局控制,比如给container设置合适的大小策略,让它随主窗口自适应。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.28 05:37:07