我可以在旧版本的 Qt 中使用
QGLWidget
(以下本页),但我在尝试更新到 Qt6 时遇到了困难。
此外,我想要一个良好的集成,以转移到 QQuickFramebufferObject 和 QtQuick2。
尝试1:
此尝试使用
WNT_Window
句柄创建 QWidget
(在 Windows 上)。当我设置 Qt::WA_PaintOnScreen
时,它“似乎”起作用,但是标准输出显示了许多消息:
QWidget::paintEngine: Should no longer be called
QPainter::begin: Paint device returned engine == 0, type: 1
删除
Qt::WA_PaintOnScreen
,图像会闪烁,就像 OCCT 直接在屏幕上绘制一样,然后 Qt 交换缓冲区。
Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);
// OCC Viewer
m_viewer = new V3d_Viewer(graphicDriver);
// OCC View
m_view = m_viewer->CreateView();
// Make this QOpenGLWindow to be the surface for the view
WId windowHandle = (WId)winId();
Handle(Aspect_Window) wind;
#ifdef WIN32
wind = new WNT_Window((Aspect_Handle)windowHandle);
#elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
wind = new Cocoa_Window((NSView *)windowHandle);
#else
wind = new Xw_Window(aDisplayConnection, (Window)windowHandle);
#endif
m_view->SetWindow(wind);
if (!wind->IsMapped()) wind->Map();
// OCC Context
m_context = new AIS_InteractiveContext(m_viewer);
尝试2:
此策略创建一个从
Aspect_NeutralWindow
句柄和当前 GL 上下文初始化的 QWidget
。
此解决方案无法显示任何 OCCT 内容。
// Create OpenGL graphic driver
Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);
// OCC Viewer
m_viewer = new V3d_Viewer(graphicDriver);
// OCC View
m_view = m_viewer->CreateView();
// Aspect rendering context should use the OpenGL context of QOpenGLWidget
Aspect_RenderingContext renderContext = (Aspect_RenderingContext)QOpenGLContext::currentContext()->handle();
Handle(Aspect_Window) wind = new Aspect_NeutralWindow();
m_view->SetWindow(wind, renderContext);
if (!wind->IsMapped()) wind->Map();
// OCC Context
m_context = new AIS_InteractiveContext(m_viewer);
尝试3:
此策略,在本页之后,尝试使用 GL 上下文并构建 OCCT 来使用它。
// Create OpenGL graphic driver
Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);
Handle(OpenGl_Context) oglContext = new OpenGl_Context();
oglContext->Init(QOpenGLContext::currentContext()); //Initialize from currently bound OpenGL context
Handle(OpenGl_FrameBuffer) fbo = new OpenGl_FrameBuffer();
fbo->InitWrapper(oglContext);
oglContext->SetDefaultFrameBuffer(fbo);
// OCC Viewer
m_viewer = new V3d_Viewer(graphicDriver);
// OCC View
m_view = m_internal->m_viewer->CreateView();
m_view->SetImmediateUpdate(false);
graphicDriver->ChangeOptions().buffersNoSwap = false;
// OCC Context
m_context = new AIS_InteractiveContext(m_viewer);
再次强调,OCCT 无法显示任何内容。
完整示例:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.16)
project(VisiTouchPSL LANGUAGES CXX)
set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_AUTOMOC ON)
find_package(OpenCASCADE REQUIRED)
set(EXEC_NAME TestOcctQt6)
# Find Qt packages
find_package(Qt6 COMPONENTS Widgets OpenGLWidgets REQUIRED)
# Add your source and headers files here
set(HEADERS
occView.h
)
set(SOURCES
occView.cpp
main.cpp
)
# Create executable
add_executable(${EXEC_NAME} ${HEADERS} ${SOURCES})
# Link Qt libraries
target_link_libraries(${EXEC_NAME} PRIVATE Qt6::Widgets Qt6::OpenGLWidgets ${OpenCASCADE_LIBRARIES})
# Include OpenCASCADE headers
target_include_directories(${EXEC_NAME} PRIVATE ${OpenCASCADE_INCLUDE_DIRS})
// main.cpp
// Author ADRIAN MAIRE
// MIT license
#include "occView.h"
#include <QApplication>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
OcctViewer w;
w.resize(800, 600);
w.show();
return a.exec();
}
//occView.h
// Author ADRIAN MAIRE
// MIT license
#ifndef OCCTVIEWER_H
#define OCCTVIEWER_H
#include <memory>
#include <QOpenGLWidget>
#include <AIS_InteractiveContext.hxx>
#include <Standard_Handle.hxx>
//! Adapted a QWidget for OpenCASCADE viewer.
class OcctViewer : public QOpenGLWidget
{
Q_OBJECT
// Pimpl to not expose OCCL stuff used internally
struct Internal;
public:
explicit OcctViewer(QWidget* parent=nullptr);
~OcctViewer() override;
// Provide access to OCCT context, for building the scene
const Handle(AIS_InteractiveContext)& getContext() const;
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
// Some default content for this example.
void makeTorus();
std::unique_ptr<Internal> m_internal;
public slots:
void rotateScene();
};
#endif
// occView.cpp
// Author ADRIAN MAIRE
// MIT license
#include "occView.h"
#include <QOpenGLContext>
#include <QTimer>
#include <OpenGl_GraphicDriver.hxx>
#include <V3d_View.hxx>
#include <Aspect_Handle.hxx>
#include <Aspect_NeutralWindow.hxx>
#include <Aspect_DisplayConnection.hxx>
#include <OpenGl_FrameBuffer.hxx>
#include <OpenGl_Context.hxx>
#include <WNT_Window.hxx>
#include <BRepPrimAPI_MakeTorus.hxx>
#include <AIS_Shape.hxx>
#include <OpenGl_GraphicDriver.hxx>
#include <QOpenGLFunctions>
struct OcctViewer::Internal
{
Handle(V3d_Viewer) m_viewer;
Handle(V3d_View) m_view;
Handle(AIS_InteractiveContext) m_context;
//For texting/example
Handle(AIS_Shape) m_torusShape;
double m_rotationAngle = 0.0;
QTimer timer;
};
OcctViewer::OcctViewer(QWidget* parent )
: QOpenGLWidget(parent)
, m_internal(std::make_unique<Internal>())
{
setAttribute(Qt::WA_PaintOnScreen);
setAttribute(Qt::WA_NoSystemBackground); // Avoid many QPainter prints/errors
setAutoFillBackground(false);
setUpdateBehavior(QOpenGLWidget::NoPartialUpdate);
// Setup the rotation of the scene
connect(&m_internal->timer, &QTimer::timeout, this, &OcctViewer::rotateScene);
m_internal->timer.setInterval(500);
m_internal->timer.start();
}
OcctViewer::~OcctViewer() = default;
void OcctViewer::initializeGL()
{
QOpenGLFunctions* f = QOpenGLContext::currentContext()->functions();
//ATTEMPT 3
/*// Create OpenGL graphic driver
Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);
Handle(OpenGl_Context) oglContext = new OpenGl_Context();
oglContext->Init(QOpenGLContext::currentContext()); //Initialize from currently bound OpenGL context
Handle(OpenGl_FrameBuffer) fbo = new OpenGl_FrameBuffer();
fbo->InitWrapper(oglContext);
oglContext->SetDefaultFrameBuffer(fbo);
// OCC Viewer
m_internal->m_viewer = new V3d_Viewer(graphicDriver);
// OCC View
m_internal->m_view = m_internal->m_viewer->CreateView();
m_internal->m_view->SetImmediateUpdate(false);
graphicDriver->ChangeOptions().buffersNoSwap = false;*/
// ATTEMPT 2
/*
// Create OpenGL graphic driver
Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);
// OCC Viewer
m_internal->m_viewer = new V3d_Viewer(graphicDriver);
// OCC View
m_internal->m_view = m_internal->m_viewer->CreateView();
// Aspect rendering context should use the OpenGL context of QOpenGLWidget
Aspect_RenderingContext renderContext = (Aspect_RenderingContext)QOpenGLContext::currentContext()->handle();
Handle(Aspect_Window) wind = new Aspect_NeutralWindow();
m_internal->m_view->SetWindow(wind, renderContext);
if (!wind->IsMapped()) wind->Map();*/
//ATTEMPT 1
// Create OpenGL graphic driver
/*Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection);
// OCC Viewer
m_internal->m_viewer = new V3d_Viewer(graphicDriver);
// OCC View
m_internal->m_view = m_internal->m_viewer->CreateView();
// Make this QOpenGLWindow to be the surface for the view
WId windowHandle = (WId)winId();
Handle(Aspect_Window) wind;
#ifdef _WIN32
wind = new WNT_Window((Aspect_Handle)windowHandle);
#elif defined(__APPLE__) && !defined(MACOSX_USE_GLX)
wind = new Cocoa_Window((NSView *)windowHandle);
#else
wind = new Xw_Window(aDisplayConnection, (Window)windowHandle);
#endif
m_internal->m_view->SetWindow(wind);
if (!wind->IsMapped()) wind->Map();*/
// OCC Context
m_internal->m_context = new AIS_InteractiveContext(m_internal->m_viewer);
// Setup the scene
m_internal->m_viewer->SetDefaultLights();
m_internal->m_viewer->SetLightOn();
m_internal->m_context->SetDisplayMode(AIS_Shaded, Standard_True);
// Setup the rendering
m_internal->m_view->SetBackgroundColor(Quantity_NOC_WHITE);
m_internal->m_view->MustBeResized();
m_internal->m_view->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_ZBUFFER);
// Create initial content (for the example)
makeTorus();
}
const Handle(AIS_InteractiveContext)& OcctViewer::getContext() const
{
return m_internal->m_context;
}
void OcctViewer::paintGL()
{
if (QOpenGLContext::currentContext() != context()) // avoid QPaint errors.
{
makeCurrent();
}
static bool color = true;
color = ! color;
QOpenGLFunctions *f = context()->functions();
f->glClearColor(0.0f, color?1.0f:0.5f, 0.0f, 0.1f);
if (!m_internal->m_view.IsNull())
{
m_internal->m_view->Redraw();
}
}
void OcctViewer::resizeGL( int w, int h )
{
if( !m_internal->m_view.IsNull() )
{
m_internal->m_view->MustBeResized();
}
}
void OcctViewer::makeTorus()
{
// Create a Torus
gp_Ax2 axis;
axis.SetLocation(gp_Pnt(0.0, 0.0, 0.0));
TopoDS_Shape torus = BRepPrimAPI_MakeTorus(axis, 90.0, 30.0).Shape();
m_internal->m_torusShape = new AIS_Shape(torus);
m_internal->m_torusShape->SetColor(Quantity_NOC_BLUE);
m_internal->m_context->Display(m_internal->m_torusShape, Standard_True);
// Align the view to fit the full window with the torus
m_internal->m_view->FitAll();
m_internal->m_view->ZFitAll();
}
void OcctViewer::rotateScene()
{
if (m_internal->m_torusShape.IsNull())
{
return;
}
m_internal->m_rotationAngle += 0.01; // Adjust this value to change rotation speed
if (m_internal->m_rotationAngle >= 2 * M_PI)
{
m_internal->m_rotationAngle -= 2 * M_PI;
}
gp_Trsf rotation;
rotation.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), m_internal->m_rotationAngle);
m_internal->m_context->SetLocation(m_internal->m_torusShape, rotation);
// Request a redraw
update();
}
旋转一下
// occView.h
#ifndef OCCTVIEWER_H
#define OCCTVIEWER_H
#include <memory>
#include <QOpenGLWidget>
#include <AIS_InteractiveContext.hxx>
#include <Standard_Handle.hxx>
class OcctViewer : public QOpenGLWidget
{
Q_OBJECT
struct Internal;
public:
explicit OcctViewer(QWidget* parent = nullptr);
~OcctViewer() override;
const Handle(AIS_InteractiveContext)& getContext() const;
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
void makeTorus();
std::unique_ptr<Internal> m_internal;
public slots:
void rotateScene();
};
#endif
// occView.cpp
#include "occView.h"
#include <QOpenGLContext>
#include <QTimer>
#include <OpenGl_GraphicDriver.hxx>
#include <V3d_View.hxx>
#include <Aspect_DisplayConnection.hxx>
#include <OpenGl_Context.hxx>
#include <BRepPrimAPI_MakeTorus.hxx>
#include <AIS_Shape.hxx>
#include <Aspect_NeutralWindow.hxx>
struct OcctViewer::Internal
{
Handle(V3d_Viewer) m_viewer;
Handle(V3d_View) m_view;
Handle(AIS_InteractiveContext) m_context;
Handle(AIS_Shape) m_torusShape;
double m_rotationAngle = 0.0;
QTimer timer;
};
OcctViewer::OcctViewer(QWidget* parent)
: QOpenGLWidget(parent)
, m_internal(std::make_unique<Internal>())
{
setAttribute(Qt::WA_NoSystemBackground);
setAutoFillBackground(false);
connect(&m_internal->timer, &QTimer::timeout, this, &OcctViewer::rotateScene);
m_internal->timer.setInterval(16); // ~60 FPS
m_internal->timer.start();
}
OcctViewer::~OcctViewer() = default;
void OcctViewer::initializeGL()
{
Handle(Aspect_DisplayConnection) displayConnection = new Aspect_DisplayConnection();
Handle(OpenGl_GraphicDriver) graphicDriver = new OpenGl_GraphicDriver(displayConnection, false);
Handle(OpenGl_Context) oglContext = new OpenGl_Context();
if (!oglContext->Init(QOpenGLContext::currentContext()))
{
throw std::runtime_error("Failed to initialize OpenGl_Context");
}
m_internal->m_viewer = new V3d_Viewer(graphicDriver);
m_internal->m_view = m_internal->m_viewer->CreateView();
Handle(Aspect_NeutralWindow) window = new Aspect_NeutralWindow();
Aspect_Drawable drawable = (Aspect_Drawable)this->winId();
window->SetNativeHandle(drawable);
window->SetSize(this->width(), this->height());
m_internal->m_view->SetWindow(window);
if (!window->IsMapped())
{
window->Map();
}
m_internal->m_context = new AIS_InteractiveContext(m_internal->m_viewer);
m_internal->m_viewer->SetDefaultLights();
m_internal->m_viewer->SetLightOn();
m_internal->m_context->SetDisplayMode(AIS_Shaded, Standard_True);
m_internal->m_view->SetBackgroundColor(Quantity_NOC_BLACK);
m_internal->m_view->MustBeResized();
m_internal->m_view->TriedronDisplay(Aspect_TOTP_LEFT_LOWER, Quantity_NOC_GOLD, 0.08, V3d_ZBUFFER);
makeTorus();
}
void OcctViewer::paintGL()
{
if (m_internal->m_view->Window().IsNull())
return;
m_internal->m_view->Redraw();
}
void OcctViewer::resizeGL(int w, int h)
{
if (m_internal->m_view->Window().IsNull())
return;
m_internal->m_view->Window()->Size(w, h);
m_internal->m_view->MustBeResized();
}
void OcctViewer::makeTorus()
{
gp_Ax2 axis;
axis.SetLocation(gp_Pnt(0.0, 0.0, 0.0));
TopoDS_Shape torus = BRepPrimAPI_MakeTorus(axis, 50.0, 20.0).Shape();
m_internal->m_torusShape = new AIS_Shape(torus);
m_internal->m_torusShape->SetColor(Quantity_NOC_AZURE);
m_internal->m_context->Display(m_internal->m_torusShape, Standard_True);
m_internal->m_view->FitAll();
m_internal->m_view->ZFitAll();
}
void OcctViewer::rotateScene()
{
if (m_internal->m_torusShape.IsNull())
return;
m_internal->m_rotationAngle += 0.05;
if (m_internal->m_rotationAngle >= 2 * M_PI)
m_internal->m_rotationAngle -= 2 * M_PI;
gp_Trsf rotation;
rotation.SetRotation(gp_Ax1(gp_Pnt(0, 0, 0), gp_Dir(0, 1, 0)), m_internal->m_rotationAngle);
m_internal->m_context->SetLocation(m_internal->m_torusShape, rotation);
update();
}
const Handle(AIS_InteractiveContext)& OcctViewer::getContext() const
{
return m_internal->m_context;
}
主要C++
#include "occView.h"
#include <QApplication>
#include <QSurfaceFormat>
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 3);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
OcctViewer w;
w.resize(800, 600);
w.show();
return a.exec();
}
主要差异及其基本原理:
删除
Qt::WA_PaintOnScreen
:
使用
Aspect_NeutralWindow
代替特定于平台的窗口:
显式初始化
OpenGl_Context
:
简化
paintGL()
方法:
更新了
resizeGL()
方法:
在
main.cpp
中添加了OpenGL格式设置:
CMakeLists.txt 更改:
这些更改旨在在 OCCT 和 Qt6 之间创建更强大的集成,解决您在渲染和上下文管理方面遇到的问题。新方法应该为基于 OCCT 的 Qt6 应用程序提供稳定的基础。