QOpenGLWidget Class
QOpenGLWidget 类是一个用于渲染 OpenGL 图形的控件。更多内容...
| 头文件: | #include <qopenglwidget.h> |
| CMake: | find_package(Qt6 REQUIRED COMPONENTS OpenGLWidgets) target_link_libraries(mytarget PRIVATE Qt6::OpenGLWidgets) |
| qmake: | QT += openglwidgets |
| 继承自: | QWidget |
公共类型
(since 6.5) enum | TargetBuffer { LeftBuffer, RightBuffer } |
| enum | UpdateBehavior { NoPartialUpdate, PartialUpdate } |
公共函数
| QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags()) | |
| virtual | ~QOpenGLWidget() |
| QOpenGLContext * | context() const |
(since 6.5) QOpenGLWidget::TargetBuffer | currentTargetBuffer() const |
| GLuint | defaultFramebufferObject() const |
(since 6.5) GLuint | defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const |
| void | doneCurrent() |
| QSurfaceFormat | format() const |
| QImage | grabFramebuffer() |
(since 6.5) QImage | grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer) |
| bool | isValid() const |
| void | makeCurrent() |
(since 6.5) void | makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer) |
| void | setFormat(const QSurfaceFormat &format) |
| void | setTextureFormat(GLenum texFormat) |
| void | setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior) |
| GLenum | textureFormat() const |
| QOpenGLWidget::UpdateBehavior | updateBehavior() const |
信号
| void | aboutToCompose() |
| void | aboutToResize() |
| void | frameSwapped() |
| void | resized() |
受保护函数
| virtual void | initializeGL() |
| virtual void | paintGL() |
| virtual void | resizeGL(int w, int h) |
重新实现的受保护函数
| virtual bool | event(QEvent *e) override |
| virtual int | metric(QPaintDevice::PaintDeviceMetric metric) const override |
| virtual QPaintEngine * | paintEngine() const override |
| virtual void | paintEvent(QPaintEvent *e) override |
| virtual QPaintDevice * | redirected(QPoint *p) const override |
| virtual void | resizeEvent(QResizeEvent *e) override |
详细说明
Qt OpenGL Widget 提供将 OpenGL 图形集成到 Qt 应用程序中的功能。其使用非常简单:让您的类继承自该类,并像使用其他QWidget 一样使用该子类,唯一的区别在于您可以选择使用QPainter 还是标准 OpenGL 渲染命令。
QOpenGLWidget 提供了三个便捷的虚函数,您可以在子类中重写这些函数以执行典型的 OpenGL 任务:
- paintGL() - 渲染 OpenGL 场景。每当小部件需要更新时,该函数都会被调用。
- resizeGL() - 设置 OpenGL 视口、投影等。每当控件被调整大小时都会被调用(在首次显示时也会被调用,因为所有新创建的控件都会自动触发调整大小事件)。
- initializeGL() - 配置 OpenGL 资源和状态。在首次调用 `resizeGL()` 或 `paintGL()` 之前会调用一次。
如果您需要从paintGL()以外的地方触发重绘(一个典型的例子是使用timers 来动画化场景),则应调用控件的update()函数来安排更新。
当调用paintGL()、resizeGL() 或initializeGL() 时,您的控件的 OpenGL 渲染上下文会被设为当前上下文。如果您需要从其他位置(例如在控件的构造函数中或您自己的绘制函数中)调用标准的 OpenGL API 函数,则必须先调用makeCurrent()。
所有渲染操作均在 OpenGL 帧缓冲区对象中进行。makeCurrent() 确保该对象已在上下文中绑定。在paintGL() 的渲染代码中创建和绑定额外的帧缓冲区对象时,请牢记这一点。切勿重新绑定 ID 为 0 的帧缓冲区。相反,应调用defaultFramebufferObject() 来获取应绑定的 ID。
当平台支持时,QOpenGLWidget 允许使用不同的 OpenGL 版本和配置文件。 只需通过setFormat() 设置所需的格式即可。但请注意,在同一窗口中存在多个 QOpenGLWidget 实例时,它们必须使用相同的格式,或者至少使用不会导致上下文无法共享的格式。为解决此问题,建议优先使用QSurfaceFormat::setDefaultFormat() 而不是setFormat()。
注意: 在某些平台(例如 macOS)上,当请求 OpenGL 核心配置文件上下文时,必须在构造QApplication 实例之前调用 QSurfaceFormat::setDefaultFormat()。这是为了确保上下文之间的资源共享能够正常工作,因为所有内部上下文都是使用正确的版本和配置文件创建的。
绘制技术
如上所述,请通过以下方式继承 QOpenGLWidget 类以渲染纯 3D 内容:
- 重写initializeGL()和resizeGL()函数,以设置OpenGL状态并提供透视变换。
- 重写paintGL() 函数来绘制 3D 场景,且仅调用 OpenGL 函数。
还可以通过QPainter 在 QOpenGLWidget 的子类上绘制 2D 图形:
- 在 `paintGL()` 中,不要发出 OpenGL 命令,而是构建一个 `QPainter ` 对象以供小部件使用。
- 使用QPainter 的成员函数绘制基本图形。
- 仍然可以直接发出 OpenGL 命令。但是,必须确保这些命令被画家对象的 beginNativePainting() 和 endNativePainting() 调用所包围。
当仅使用QPainter 进行绘制时,也可以像处理普通控件那样进行绘制:通过重写paintEvent()方法。
- 重写paintEvent() 函数。
- 构建一个以该小部件为目标的QPainter 对象。可将小部件传递给构造函数,或传递给QPainter::begin() 函数。
- 使用QPainter 的成员函数绘制基元。
- 绘制完成后,QPainter 实例将被销毁。或者,可以显式调用QPainter::end()。
OpenGL 函数调用、头文件和 QOpenGLFunctions
在调用 OpenGL 函数时,强烈建议避免直接调用函数。相反,应优先使用QOpenGLFunctions (当开发可移植应用程序时),或使用带版本号的变体(例如,当针对现代、仅限桌面的 OpenGL 时,使用QOpenGLFunctions_3_2_Core 及类似函数)。 这样,应用程序将在所有 Qt 构建配置中正常运行,包括那些执行动态 OpenGL 实现加载的配置——这意味着应用程序不会直接链接到 GL 实现,因此无法进行直接函数调用。
在 `paintGL()` 中,可通过调用 `QOpenGLContext::currentContext()` 随时访问当前上下文。从该上下文中,可通过调用 `QOpenGLContext::functions()` 获取一个已初始化、可直接使用的 `QOpenGLFunctions ` 实例。除了在每个 GL 调用前添加前缀外,另一种方法是继承自 `QOpenGLFunctions `,并在 `initializeGL()` 中调用 `QOpenGLFunctions::initializeOpenGLFunctions()`。
关于 OpenGL 头文件,请注意在大多数情况下无需直接包含 GL.h 等头文件。与 OpenGL 相关的 Qt OpenGL 头文件会包含 qopengl.h,而该头文件会自动包含适用于该系统的相应头文件。 这可能是 OpenGL ES 3.x 或 2.0 的头文件、可用的最高版本,或是系统提供的 gl.h。 此外,Qt OpenGL 和 Qt OpenGL ES 提供了扩展头文件(在某些系统上称为 glext.h)的副本。在可行的情况下,这些头文件将在平台上自动包含。这意味着来自 ARB、EXT、OES 扩展的常量和函数指针 typedef 将自动可用。
代码示例
入门时,最简单的 QOpenGLWidget 子类可以如下所示:
class MyGLWidget : public QOpenGLWidget
{
public:
MyGLWidget(QWidget *parent) : QOpenGLWidget(parent) { }
protected:
void initializeGL() override
{
// Set up the rendering context, load shaders and other resources, etc.:
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
...
}
void resizeGL(int w, int h) override
{
// Update projection matrix and other size related settings:
m_projection.setToIdentity();
m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f);
...
}
void paintGL() override
{
// Draw the scene:
QOpenGLFunctions *f = QOpenGLContext::currentContext()->functions();
f->glClear(GL_COLOR_BUFFER_BIT);
...
}
};或者,也可以通过继承自QOpenGLFunctions 来避免为每个OpenGL调用添加前缀:
class MyGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
...
void initializeGL() override
{
initializeOpenGLFunctions();
glClearColor(...);
...
}
...
};若要获取与特定 OpenGL 版本或配置文件兼容的上下文,或请求深度和模板缓冲区,请调用setFormat():
QOpenGLWidget *widget = new QOpenGLWidget(parent);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
widget->setFormat(format); // must be called before the widget or its parent window gets shown注意:应用程序需自行 确保通过底层窗口系统接口请求深度缓冲区和模板缓冲区。若未请求非零大小的深度缓冲区,则无法保证深度缓冲区可用,因此与深度测试相关的 OpenGL 操作可能会无法按预期运行。 常用的深度缓冲区和模板缓冲区大小请求值分别为 24 和 8。
对于 OpenGL 3.0 及以上版本的上下文,当可移植性不是关键考虑因素时,带版本号的 `QOpenGLFunctions ` 变体可方便地访问给定版本中所有可用的现代 OpenGL 函数:
...
void paintGL() override
{
QOpenGLFunctions_3_2_Core *f = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_2_Core>();
...
f->glDrawArraysInstanced(...);
...
}
...如上所述,将请求的格式全局设置更为简单且稳健,这样在应用程序生命周期内,该设置将适用于所有窗口和上下文。以下是一个示例:
int main(int argc, char **argv)
{
QApplication app(argc, argv);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 2);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
MyWidget widget;
widget.show();
return app.exec();
}多采样
要启用多采样,请在传递给 `setFormat()` 的 `QSurfaceFormat ` 上设置所需采样数。在不支持多采样的系统上,该请求可能会被忽略。
多采样功能需要系统支持多采样渲染缓冲区和帧缓冲区复制操作。 在 OpenGL ES 2.0 的实现中,这些功能很可能不存在。这意味着将无法使用多采样。在现代 OpenGL 版本以及 OpenGL ES 3.0 及更高版本中,这通常已不再是问题。
多线程
通过公开控件的QOpenGLContext ,可在各线程上创建与之共享的额外渲染上下文,从而支持在工作线程上执行离屏渲染(例如生成纹理,随后在GUI/主线程的paintGL()中使用)。
通过重新实现paintEvent()使其不执行任何操作,即可在 GUI/主线程之外直接向 QOpenGLWidget 的帧缓冲区绘制。必须通过QObject::moveToThread() 更改上下文的线程亲和性。之后,makeCurrent() 和doneCurrent() 即可在工作线程上使用。 请注意,之后务必将上下文移回 GUI/主线程。
无法仅针对 QOpenGLWidget 触发缓冲区交换,因为它没有真正的、显示在屏幕上的本机绘制表面。在 GUI 线程上管理合成和缓冲区交换是小部件堆栈的职责。当一个线程完成对帧缓冲区的更新后,应在 GUI/主线程上调用update() 来调度合成操作。
必须格外注意,避免在 GUI/主线程执行合成操作时使用帧缓冲区。当合成开始和结束时,将发出aboutToCompose() 和frameSwapped() 信号。这些信号是在 GUI/主线程上发出的。 这意味着,如果使用直接连接,aboutToCompose() 可能会阻塞 GUI/主线程,直到工作线程完成渲染为止。此后,工作线程必须停止进一步渲染,直到发出frameSwapped() 信号为止。 如果这种情况无法接受,则工作线程必须实现双缓冲机制。这涉及使用由该线程完全控制的替代渲染目标(例如一个额外的帧缓冲区对象)进行绘制,并在适当的时候将其复制到 QOpenGLWidget 的帧缓冲区中。
上下文共享
当多个 QOpenGLWidget 作为子控件添加到同一个顶级控件时,它们的上下文将相互共享。这不适用于属于不同窗口的 QOpenGLWidget 实例。
这意味着同一窗口中的所有 QOpenGLWidget 都可以访问彼此的可共享资源(如纹理),无需额外的“全局共享”上下文。
要设置属于不同窗口的 QOpenGLWidget 实例之间的共享,请在实例化QApplication 之前设置Qt::AA_ShareOpenGLContexts 应用程序属性。这将触发所有 QOpenGLWidget 实例之间的共享,无需任何额外步骤。
也可以创建额外的QOpenGLContext 实例,使其与QOpenGLWidget的上下文共享纹理等资源。只需在调用QOpenGLContext::create()之前,将context()返回的指针传递给QOpenGLContext::setShareContext()即可。生成的上下文还可以在不同的线程上使用,从而支持多线程生成纹理和异步纹理上传。
请注意,在底层图形驱动程序方面,QOpenGLWidget 期望驱动程序提供符合标准的资源共享实现。例如,某些驱动程序(特别是针对移动设备和嵌入式硬件的驱动程序)在为现有上下文与后续创建的上下文之间建立共享时会出现问题。还有些驱动程序在尝试在不同线程之间使用共享资源时,可能会表现出意料之外的行为。
资源初始化和清理
每当调用initializeGL()和paintGL()时,QOpenGLWidget关联的OpenGL上下文均保证处于活动状态。请勿在调用initializeGL()之前尝试创建OpenGL资源。例如,若在子类的构造函数中尝试编译着色器、初始化顶点缓冲对象或上传纹理数据,都会导致操作失败。 这些操作必须推迟到initializeGL() 时执行。Qt OpenGL 辅助类(如QOpenGLBuffer 或QOpenGLVertexArrayObject )具有相应的延迟行为:它们可以在没有上下文的情况下实例化,但所有初始化操作都会推迟到调用create() 或类似函数时才进行。 这意味着它们可以在 QOpenGLWidget 子类中作为普通的(非指针)成员变量使用,但create() 或类似函数只能在initializeGL() 中调用。 但请注意,并非所有类都采用这种设计。如有疑问,请将成员变量定义为指针,并分别在initializeGL()中动态创建实例,在析构函数中动态销毁实例。
释放资源时,上下文也必须处于活动状态。因此,执行此类清理操作的析构函数应在销毁任何 OpenGL 资源或封装类之前,先调用 `makeCurrent()`。请避免通过 `deleteLater()` 或 `QObject` 的父子关系机制进行延迟删除。因为无法保证在相关实例真正被销毁时,正确的上下文仍处于活动状态。
因此,在资源初始化和销毁方面,典型的子类通常如下所示:
class MyGLWidget : public QOpenGLWidget
{
...
private:
QOpenGLVertexArrayObject m_vao;
QOpenGLBuffer m_vbo;
QOpenGLShaderProgram *m_program;
QOpenGLShader *m_shader;
QOpenGLTexture *m_texture;
};
MyGLWidget::MyGLWidget()
: m_program(0), m_shader(0), m_texture(0)
{
// No OpenGL resource initialization is done here.
}
MyGLWidget::~MyGLWidget()
{
// Make sure the context is current and then explicitly
// destroy all underlying OpenGL resources.
makeCurrent();
delete m_texture;
delete m_shader;
delete m_program;
m_vbo.destroy();
m_vao.destroy();
doneCurrent();
}
void MyGLWidget::initializeGL()
{
m_vao.create();
if (m_vao.isCreated())
m_vao.bind();
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(...);
m_texture = new QOpenGLTexture(QImage(...));
m_shader = new QOpenGLShader(...);
m_program = new QOpenGLShaderProgram(...);
...
}这种做法在大多数情况下可行,但作为通用解决方案并不完全理想。当小部件被重新关联到父窗口,从而最终位于一个完全不同的顶级窗口中时,还需要采取额外措施:通过订阅 `QOpenGLContext` 的 `aboutToBeDestroyed()` 信号,可以在 OpenGL 上下文即将被释放时执行清理操作。
注意:对于 在生命周期内多次更改关联顶级窗口的小部件,必须采用如下代码片段所示的组合清理方法。每当小部件或其父窗口被重新关联,导致顶级窗口发生变化时,小部件关联的上下文就会被销毁并创建一个新的上下文。 随后需调用initializeGL(),此时所有 OpenGL 资源都必须重新初始化。因此,要进行正确的清理,唯一的方法是连接到上下文的 aboutToBeDestroyed() 信号。请注意,当该信号被发出时,所涉及的上下文可能并非当前的上下文。 因此,在已连接的槽函数中调用 `makeCurrent()` 是一种良好的编程实践。此外,还必须在派生类的析构函数中执行相同的清理步骤,因为当小部件被销毁时,连接到该信号的槽函数或 lambda 表达式可能不会被调用。
MyGLWidget::~MyGLWidget()
{
cleanup();
}
void MyGLWidget::initializeGL()
{
...
connect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}
void MyGLWidget::cleanup()
{
makeCurrent();
delete m_texture;
m_texture = 0;
...
doneCurrent();
disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, this, &MyGLWidget::cleanup);
}注意:当 Qt::AA_ShareOpenGLContexts 被设置时 ,小部件的上下文永远不会改变,即使在重新父级化时也是如此,因为小部件关联的纹理在新的顶级上下文中同样可以访问。因此,当此标志被设置时,处理上下文的aboutToBeDestroyed()信号并非强制要求。
由于上下文共享,正确的清理工作尤为重要。尽管每个 QOpenGLWidget 的关联上下文会随 QOpenGLWidget 一起销毁,但该上下文中的可共享资源(如纹理)将保持有效,直到包含该 QOpenGLWidget 的顶级窗口被销毁为止。 此外,Qt::AA_ShareOpenGLContexts 等设置以及某些Qt模块可能会触发更广泛的上下文共享范围,这可能导致相关资源在应用程序的整个生命周期内都保持活动状态。因此,最安全且最稳健的做法是始终对QOpenGLWidget中使用的所有资源和资源封装器进行显式清理。
限制与其他注意事项
将其他小部件放置在 QOpenGLWidget 下方并使其透明,不会产生预期的效果:下方的 widget 将不可见。这是因为实际上 QOpenGLWidget 会在所有其他常规(非 OpenGL)小部件之前被绘制,因此此类“透明”解决方案并不可行。 其他类型的布局(例如将小部件放置在 QOpenGLWidget 之上)将按预期工作。
在绝对必要的情况下,可以通过在 QOpenGLWidget 上设置Qt::WA_AlwaysStackOnTop 属性来克服这一限制。 但请注意,这会破坏堆叠顺序,例如将无法在 QOpenGLWidget 上方放置其他小部件,因此仅应在需要半透明的 QOpenGLWidget 且其下方其他小部件可见的情况下使用。
请注意,当下方没有其他小部件且目的是创建一个半透明窗口时,此限制不适用。在这种情况下,在顶级窗口上设置Qt::WA_TranslucentBackground 的传统方法就足够了。 请注意,如果仅希望在 QOpenGLWidget 中实现透明区域,则在启用 `Qt::WA_TranslucentBackground` 之后,需要将 `Qt::WA_NoSystemBackground ` 重新设置为 `false `。此外,根据系统情况,可能还需要通过 `setFormat()` 为 QOpenGLWidget 的上下文请求一个 alpha 通道。
QOpenGLWidget 支持多种更新行为,与QOpenGLWindow 类似。在保留模式下,上一次paintGL() 调用渲染的内容可在下次调用中使用,从而支持增量渲染。在非保留模式下,内容将被丢失,此时paintGL() 的实现需要重新绘制视图中的所有内容。
在 Qt 5.5 之前,QOpenGLWidget 的默认行为是在paintGL() 调用之间保留已渲染的内容。自 Qt 5.5 起,默认行为改为不保留,因为这能提供更好的性能,且大多数应用程序并不需要之前的内容。 这与基于 OpenGL 的QWindow 的语义相似,也符合QOpenGLWindow 的默认行为——即每帧都会使颜色和辅助缓冲区失效。若要恢复保留内容的行为,请使用PartialUpdate 参数调用setUpdateBehavior()。
注意:当 动态将 QOpenGLWidget 添加到控件层次结构中时 (例如,将一个新的 QOpenGLWidget 作为子控件添加到某个控件下,而该控件对应的顶级控件已显示在屏幕上),如果该 QOpenGLWidget 是其窗口中同类控件中的第一个,则关联的原生窗口可能会被隐式销毁并重新创建。 这是因为窗口类型从RasterSurface 变为OpenGLSurface ,这会产生与平台相关的后果。此行为是 Qt 6.4 中的新特性。
一旦将 QOpenGLWidget 添加到控件层次结构中,顶级窗口的内容将通过基于 OpenGL 的渲染进行刷新。除 QOpenGLWidget 以外的控件仍会使用基于软件的绘制器来绘制其内容,但最终的合成操作是通过 3D API 完成的。
注意: 由于与其他基于QWidget 的内容进行合成时的工作机制,显示 QOpenGLWidget 需要关联的顶级窗口的后备存储中包含 alpha 通道。 如果没有 alpha 通道,QOpenGLWidget 渲染的内容将不可见。在 Linux/X11 的远程显示环境中(例如使用 Xvnc),当颜色深度低于 24 时,这一点尤为重要。 例如,16 位色深通常会映射为使用格式为QImage::Format_RGB16 (RGB565)的后备存储图像,这会占用所有空间,导致无法保留 alpha 通道。 因此,如果遇到 QOpenGLWidget 的内容无法与窗口中的其他控件正确合成的问题,请确保服务器(例如 vncserver)配置为 24 或 32 位色深,而不是 16 位。
替代方案
将 QOpenGLWidget 添加到窗口中会为整个窗口启用基于 OpenGL 的合成。在某些特殊情况下,这可能并不理想,此时需要采用旧式的 QGLWidget 风格行为,即使用单独的原生子窗口。 了解此方法局限性(例如在重叠、透明度、滚动视图和 MDI 区域方面)的桌面应用程序,可以使用QOpenGLWindow 配合QWidget::createWindowContainer()。这是 QGLWidget 的现代替代方案,由于省去了额外的合成步骤,其运行速度比 QOpenGLWidget 更快。 强烈建议仅在别无选择的情况下才使用此方法。请注意,此选项不适用于大多数嵌入式和移动平台,且已知在某些桌面平台(如 macOS)上也存在问题。稳定且跨平台的解决方案始终是 QOpenGLWidget。
立体渲染
从 6.5 版本开始,QOpenGLWidget 支持立体渲染。要启用该功能,请在创建窗口之前,使用 QSurfaceFormat::SetDefaultFormat() 全局设置QSurfaceFormat::StereoBuffers 标志。
注意: 由于该标志在内部的处理方式,使用 setFormat() 并不一定能生效。
这将触发每帧调用两次paintGL(),每次针对一个QOpenGLWidget::TargetBuffer 。在paintGL()中,调用currentTargetBuffer()来查询当前正在绘制的是哪个缓冲区。
注意:若需 对左右颜色缓冲区进行 更精细的控制,建议改用QOpenGLWindow +QWidget::createWindowContainer()。
注意:此类 3D 渲染有特定的硬件要求,例如显卡需要配置为支持立体显示。
OpenGL 是 Silicon Graphics, Inc. 在美国和其他国家/地区的注册商标。
另请参阅 QOpenGLFunctions 、QOpenGLWindow 、Qt::AA_ShareOpenGLContexts 和UpdateBehavior 。
成员类型文档
[since 6.5] enum QOpenGLWidget::TargetBuffer
指定在启用立体渲染时使用的缓冲区,该功能可通过设置QSurfaceFormat::StereoBuffers 来开启或关闭。
注意:LeftBuffer 始终是默认值,并在立体渲染被禁用或图形驱动程序不支持时用作备用值。
| 常量 | 值 |
|---|---|
QOpenGLWidget::LeftBuffer | 0 |
QOpenGLWidget::RightBuffer | 1 |
该枚举在 Qt 6.5 中引入。
enum QOpenGLWidget::UpdateBehavior
此枚举描述了QOpenGLWidget 的更新语义。
| 常量 | 值 | 描述 |
|---|---|---|
QOpenGLWidget::NoPartialUpdate | 0 | QOpenGLWidget 将在QOpenGLWidget 渲染到屏幕后,丢弃颜色缓冲区和辅助缓冲区的内容。这与调用QOpenGLContext::swapBuffers 时,将默认启用OpenGL的QWindow 作为参数所产生的行为相同。 当帧缓冲区对象用作渲染目标时,NoPartialUpdate 在移动和嵌入式领域常见的某些硬件架构上可能会带来一些性能优势。 帧缓冲区对象会在帧与帧之间通过 glInvalidateFramebuffer(如果支持)进行失效处理,或者作为备选方案,通过 glDiscardFramebufferEXT(如果支持)或调用 glClear 进行失效处理。 |
QOpenGLWidget::PartialUpdate | 1 | 帧与帧之间,帧缓冲区对象的颜色缓冲区和辅助缓冲区不会被无效化。 |
另请参阅 updateBehavior() 和setUpdateBehavior()。
成员函数文档
[explicit] QOpenGLWidget::QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())
创建一个作为parent 子控件的控件,其控件标志设置为f 。
[virtual noexcept] QOpenGLWidget::~QOpenGLWidget()
销毁QOpenGLWidget 实例,并释放其资源。
在析构函数中,QOpenGLWidget 的上下文会被设为当前上下文,从而能够安全地销毁任何可能需要释放属于该控件所提供上下文的 OpenGL 资源的子对象。
警告:如果 OpenGLWidget 子类的成员中包含封装 OpenGL 资源(如QOpenGLBuffer 、QOpenGLShaderProgram 等)的对象,您可能还需要在该子类的析构函数中添加对makeCurrent() 的调用。 由于 C++ 对象销毁规则的限制,这些对象将在调用此函数之前被销毁(但在此之后子类的析构函数已执行),因此在此函数中将 OpenGL 上下文设为当前上下文的操作发生得太晚,无法确保这些对象被安全释放。
另请参阅 makeCurrent 。
[signal] void QOpenGLWidget::aboutToCompose()
当控件的顶级窗口即将开始合成其QOpenGLWidget 子控件及其他控件的纹理时,会发出此信号。
[signal] void QOpenGLWidget::aboutToResize()
当控件的大小发生变化,从而导致帧缓冲区对象即将被重新创建时,会发出此信号。
QOpenGLContext *QOpenGLWidget::context() const
返回该控件使用的QOpenGLContext ,若尚未初始化,则返回0 。
注意: 通过setParent() 重新设置控件的父对象时,该控件所使用的上下文 和帧缓冲区对象会发生变化。
另请参阅 QOpenGLContext::setShareContext() 和defaultFramebufferObject()。
[since 6.5] QOpenGLWidget::TargetBuffer QOpenGLWidget::currentTargetBuffer() const
返回当前活动的目标缓冲区。默认情况下,该缓冲区为左缓冲区;右缓冲区仅在启用QSurfaceFormat::StereoBuffers 时使用。当启用立体渲染时,可通过paintGL()查询以了解当前正在使用的缓冲区。paintGL()将被调用两次,每个目标各调用一次。
该函数在 Qt 6.5 中引入。
另请参阅 paintGL()。
GLuint QOpenGLWidget::defaultFramebufferObject() const
返回帧缓冲区对象句柄;如果尚未初始化,则返回0 。
注意: 帧缓冲区对象 属于由context() 返回的上下文,可能无法从其他上下文中访问。
注意: 通过setParent() 重新关联小部件时,该小部件所使用的上下文 和帧缓冲区对象会发生变化。此外,每次调整大小时,帧缓冲区对象也会发生变化。
另请参阅 context()。
[since 6.5] GLuint QOpenGLWidget::defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const
返回指定目标缓冲区的帧缓冲区对象句柄;如果尚未初始化,则返回0 。
只有在启用了QSurfaceFormat::StereoBuffers 且硬件支持该功能时,调用此重载方法才有意义。否则,该方法将返回默认缓冲区。
注意: 帧缓冲区对象 属于由 `context()` 返回的上下文,可能无法从其他上下文中访问。通过 `setParent()` 重新关联小部件时,小部件使用的上下文和帧缓冲区对象会发生变化。此外,每次调整大小时,帧缓冲区对象也会发生变化。
该函数在 Qt 6.5 中引入。
另请参阅 context()。
void QOpenGLWidget::doneCurrent()
释放上下文。
在大多数情况下无需调用此函数,因为控件会在调用 `paintGL()` 时确保上下文被正确绑定和释放。
[override virtual protected] bool QOpenGLWidget::event(QEvent *e)
重写了:QWidget::event(QEvent *event)。
QSurfaceFormat QOpenGLWidget::format() const
返回该控件及其顶级窗口所使用的上下文和表面格式。
在小部件及其顶层窗口均已创建、调整大小并显示后,此函数将返回上下文的实际格式。如果平台无法满足请求,则该格式可能与请求的格式不同。此外,获取到的颜色缓冲区大小也可能大于请求的大小。
当控件的窗口及相关 OpenGL 资源尚未初始化时,返回值即为通过setFormat() 设置的格式。
[signal] void QOpenGLWidget::frameSwapped()
当控件的顶级窗口完成合成,并从可能阻塞的QOpenGLContext::swapBuffers()调用中返回后,会发出此信号。
QImage QOpenGLWidget::grabFramebuffer()
渲染并返回帧缓冲区的 32 位 RGB 图像。
注意:这是一项 可能消耗较多的操作,因为它依赖于 glReadPixels() 来读取像素。此操作可能较慢,并可能导致 GPU 管道停滞。
[since 6.5] QImage QOpenGLWidget::grabFramebuffer(QOpenGLWidget::TargetBuffer targetBuffer)
渲染并返回指定目标缓冲区的帧缓冲区对应的32位RGB图像。仅当启用QSurfaceFormat::StereoBuffers 时,调用此重载才具有意义。如果立体渲染被禁用或硬件不支持,则抓取正确目标缓冲区的帧缓冲区将返回默认图像。
注意:这是一项 可能耗时较长的操作,因为它依赖于 glReadPixels() 来读取像素。此过程可能较慢,并可能导致 GPU 管道停滞。
该函数于 Qt 6.5 中引入。
[virtual protected] void QOpenGLWidget::initializeGL()
在首次调用paintGL()或resizeGL()之前,会调用一次此虚拟函数。请在子类中重新实现它。
该函数应初始化所有必需的 OpenGL 资源。
无需调用makeCurrent(),因为在调用此函数时该操作已完成。但请注意,此时帧缓冲区尚未可用,因此应避免在此处发出绘制调用。请将此类调用推迟到paintGL() 中执行。
bool QOpenGLWidget::isValid() const
如果小部件和 OpenGL 资源(如上下文)已成功初始化,则返回true。请注意,在小部件显示之前,返回值始终为 false。
void QOpenGLWidget::makeCurrent()
通过将相应的上下文设为当前上下文,并在该上下文中绑定帧缓冲区对象,为本控件的 OpenGL 内容渲染做好准备。
在大多数情况下无需调用此函数,因为在调用paintGL() 之前,系统会自动调用它。
另请参阅 context()、paintGL() 和doneCurrent()。
[since 6.5] void QOpenGLWidget::makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer)
通过将传入缓冲区的上下文设为当前上下文,并在此上下文中绑定帧缓冲区对象,为该控件的 OpenGL 内容渲染做好准备。
注意: 仅当启用立体渲染时,调用此函数 才有意义。如果在立体渲染禁用时请求右缓冲区,则不会发生任何操作。
在大多数情况下无需调用此函数,因为在调用paintGL() 之前,系统会自动调用它。
该函数在 Qt 6.5 中引入。
另请参阅 context()、paintGL() 和doneCurrent()。
[override virtual protected] int QOpenGLWidget::metric(QPaintDevice::PaintDeviceMetric metric) const
重写了:QWidget::metric(QPaintDevice::PaintDeviceMetric m) const。
[override virtual protected] QPaintEngine *QOpenGLWidget::paintEngine() const
重新实现了:QWidget::paintEngine() const。
[override virtual protected] void QOpenGLWidget::paintEvent(QPaintEvent *e)
重写:QWidget::paintEvent(QPaintEvent *event)。
处理绘制事件。
调用QWidget::update()将导致发送一个绘制事件e ,从而调用此函数。(注意:这是异步操作,将在update()返回后的某个时刻发生。)随后,此函数在完成一些准备工作后,将调用虚拟函数paintGL()来更新QOpenGLWidget 的帧缓冲区内容。 随后,该小部件的顶级窗口将把帧缓冲区的纹理与窗口的其余部分进行合成。
[virtual protected] void QOpenGLWidget::paintGL()
每当需要绘制控件时,都会调用此虚拟函数。请在子类中重新实现它。
无需调用makeCurrent(),因为在调用此函数时,该操作已经完成。
在调用此函数之前,上下文和帧缓冲区已绑定,并且通过调用 glViewport() 设置了视口。框架不会设置其他状态,也不会执行清除或绘制操作。
默认实现会执行 glClear()。子类不应调用基类的实现,而应自行执行清除操作。
注意:为确保 可移植性,请勿期望在 `initializeGL()` 中设置的状态会持续存在。 相反,应在paintGL()中设置所有必要的状态,例如通过调用glEnable()。这是因为某些平台(例如使用WebGL的WebAssembly)在某些情况下可能对OpenGL上下文存在限制,这可能导致与QOpenGLWidget 关联的上下文也被用于其他用途。
当启用QSurfaceFormat::StereoBuffers 时,此函数将被调用两次——每个缓冲区各调用一次。可通过调用currentTargetBuffer()查询当前绑定的缓冲区。
注意: 即使硬件不支持立体渲染,每个目标的帧缓冲区 仍会被绘制。实际上,窗口中仅会显示左侧缓冲区。
另请参阅 initializeGL()、resizeGL() 和currentTargetBuffer()。
[override virtual protected] QPaintDevice *QOpenGLWidget::redirected(QPoint *p) const
[override virtual protected] void QOpenGLWidget::resizeEvent(QResizeEvent *e)
重写:QWidget::resizeEvent(QResizeEvent *event)。
处理通过e 事件参数传递的调整大小事件。调用虚函数resizeGL()。
注意:请避免 在派生类中重写此函数。如果无法避免,请确保也调用QOpenGLWidget 的实现。否则,底层帧缓冲区对象及相关资源将无法正确调整大小,从而导致渲染错误。
[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)
每当控件被调整大小时,都会调用此虚拟函数。请在子类中重写该函数。新的尺寸会通过w 和h 传递进来。
无需调用makeCurrent(),因为在调用此函数时该操作已完成。此外,帧缓冲区也已绑定。
另请参阅 initializeGL() 和paintGL()。
[signal] void QOpenGLWidget::resized()
当小部件因调整大小而重新创建帧缓冲区对象后,该信号会立即发出。
void QOpenGLWidget::setFormat(const QSurfaceFormat &format)
设置请求的表面format 。
如果未通过此函数显式设置格式,则将使用QSurfaceFormat::defaultFormat()返回的格式。这意味着当存在多个OpenGL控件时,可以在创建第一个控件之前,将对该函数的多次单独调用替换为一次对QSurfaceFormat::setDefaultFormat()的调用。
注意: 若希望使下方的其他控件可见,通过此函数请求 alpha 缓冲区将无法达到预期效果。请改用Qt::WA_AlwaysStackOnTop 来启用半透明的QOpenGLWidget 实例,以便下方其他控件可见。但请注意,这会打破堆叠顺序,因此将无法再在QOpenGLWidget 上方放置其他控件。
另请参阅 format()、Qt::WA_AlwaysStackOnTop 以及QSurfaceFormat::setDefaultFormat()。
void QOpenGLWidget::setTextureFormat(GLenum texFormat)
设置自定义内部纹理格式为texFormat 。
在使用 sRGB 帧缓冲区时,必须指定类似GL_SRGB8_ALPHA8 的格式。可通过调用此函数来实现。
注意: 如果在控件已显示且已完成初始化后调用此 函数,则该函数 将不起作用。
注意: 通常必须将此 函数与QSurfaceFormat::setColorSpace()调用结合使用,该调用将色彩空间设置为QColorSpace::SRgb 。
另请参阅 textureFormat()。
void QOpenGLWidget::setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)
将此控件的更新行为设置为updateBehavior 。
另请参阅 updateBehavior()。
GLenum QOpenGLWidget::textureFormat() const
如果小部件已初始化,则返回当前活动的内部纹理格式;如果已设置格式但小部件尚未显示,则返回所请求的格式;如果未调用setTextureFormat()且小部件尚未显示,则返回nullptr 。
另请参阅 setTextureFormat()。
QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const
返回控件的更新行为。
另请参阅 setUpdateBehavior()。
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.