class QOpenGLWidget#

QOpenGLWidget 类是一个用于渲染 OpenGL 图形的窗口小部件。更多...

Inheritance diagram of PySide6.QtOpenGLWidgets.QOpenGLWidget

概览#

方法#

虚方法#

信号#

注意

本文档可能包含从 C++ 自动翻译到 Python 的代码片段。我们始终欢迎对片段翻译的贡献。如果您发现翻译问题,也可以通过在 https:/bugreports.qt.io/projects/PYSIDE 上创建一个工单来告知我们。

详细说明#

警告

本节包含自动从 C++ 翻译到 Python 的代码片段,可能存在错误。

QOpenGLWidget 提供了将 OpenGL 图形集成到 Qt 应用程序中的功能。使用非常简单:让您的类继承自它,并像使用任何其他 QWidget 一样使用子类,只是您可以选择使用 QPainter 和标准 OpenGL 渲染命令之一。

QOpenGLWidget 提供了三个方便的虚函数,您可以在子类中重写以执行典型的 OpenGL 任务。

  • paintGL() - 渲染OpenGL场景。每次小工具需要更新时都会调用。

  • resizeGL() - 设置OpenGL视口、投影等。每当小工具被调整大小(以及第一次显示时,因为所有新创建的小工具都会自动收到调整大小事件)时调用。

  • initializeGL() - 设置OpenGL资源和状态。在第一次调用 resizeGL()paintGL() 之前调用一次。

如果您需要在paintGL()之外的地方触发重绘(典型例子是在使用定时器进行场景动画时),应调用小工具的update()函数来安排更新。

当调用paintGL()resizeGL()initializeGL()时,您的OpenGL渲染上下文处于活动状态。如果您需要在其他地方调用标准OpenGL API函数(例如,在您的小工具构造函数中或在您自己的绘制函数中),必须先调用makeCurrent()

所有渲染都发生在OpenGL帧缓冲区对象中。makeCurrent() 确保它在上下文中绑定。在paintGL()中的渲染代码创建和绑定额外的帧缓冲区对象时,请牢记这一点。永远不要重新绑定ID为0的帧缓冲区。相反,调用 defaultFramebufferObject() 来获取应该绑定的ID。

QOpenGLWidget支持在平台支持的情况下使用不同的OpenGL版本和配置文件。只需通过setFormat()方法设置所请求的格式即可。然而,要注意,在同一个窗口中使用多个QOpenGLWidget实例时,它们必须使用相同的格式,或者至少是格式不导致上下文不可共享的格式。为了解决这个问题,请首选使用QSurfaceFormat::setDefaultFormat()方法而不是setFormat()方法。

注意

在某些平台上(例如,macOS),在请求OpenGL核心配置文件上下文之前调用QSurfaceFormat::setDefaultFormat()是强制性的。这是为了确保资源共享在所有内部上下文都是使用正确的版本和配置创建的情况下仍然有效。

绘图技术#

如上所述,通过以下方式子类化QOpenGLWidget以渲染纯3D内容

  • 重写initializeGL()resizeGL()方法以设置OpenGL状态并提供透视变换。

  • 重写paintGL()方法以绘制3D场景,仅调用OpenGL函数。

也可以使用QPainter在QOpenGLWidget子类上绘制2D图形

  • paintGL()中,不必发出OpenGL命令,而是为小部件构造一个QPainter对象。

  • 使用QPainter的成员函数绘制原始形状。

  • 仍然可以发出直通的OpenGL命令。然而,必须确保这些命令被封装在调用painter的beginNativePainting()和endNativePainting()中。

当仅使用QPainter进行绘图时,也可以像为普通小部件那样执行绘画:通过重写paintEvent()

  • 重写paintEvent()函数。

  • 为小部件构造一个QPainter对象。可以将小部件传递给构造函数或使用QPainter::begin()函数。

  • 使用QPainter的成员函数绘制原始形状。

  • 绘画完成后,则QPainter实例被销毁。或者,可以显式调用QPainter::end()。

OpenGL函数调用、头文件和QOpenGLFunctions#

在调用OpenGL函数时,强烈建议避免直接调用函数。相反,优先使用QOpenGLFunctions(当创建可移植应用程序时)或带版本的变体(例如,QOpenGLFunctions_3_2_Core及其相似版本,当针对现代、仅桌面版本的OpenGL时)。这样应用程序将在所有Qt构建配置中正确运行,包括那些执行动态OpenGL实现加载的配置。这意味着应用程序没有直接链接到GL实现,因此直接函数调用是不可行的。

paintGL()中,当前上下文始终可以通过调用QOpenGLContext::currentContext()来访问。从这个上下文中,可以通过调用QOpenGLContext::functions()来检索一个已初始化、准备好使用的QOpenGLFunctions实例。另一种避免在每个GL调用前添加前缀的方法是继承自QOpenGLFunctions,并在initializeGL()中调用QOpenGLFunctions::initializeOpenGLFunctions()。

至于OpenGL头文件,请注意,在大多数情况下,没有必要直接包含如GL.h之类的任何头文件。OpenGL相关的Qt头文件将包含qopengl.h,这将进而包括适用于系统的适当头文件。这可能是OpenGL ES 3.x或2.0头文件,是可用的最高版本,或系统提供的gl.h。此外,作为Qt的组成部分,还提供了一份扩展头文件(在某些系统上称为glext.h)的副本,用于OpenGL和OpenGL ES。这些头文件在适合的平台上将自动包含。这意味着ARB、EXT、OES扩展的常量和函数指针typedef将自动可用。

代码示例#

要开始,最简单的QOpenGLWidget子类可能如下所示

class MyGLWidget(QOpenGLWidget):

# public
    MyGLWidget(QWidget parent) : QOpenGLWidget(parent) { }
# protected
    def initializeGL():

        # Set up the rendering context, load shaders and other resources, etc.:
        f = QOpenGLContext.currentContext().functions()
        f.glClearColor(1.0f, 1.0f, 1.0f, 1.0f)
        ...

    def resizeGL(w, h):

        # Update projection matrix and other size related settings:
        m_projection.setToIdentity()
        m_projection.perspective(45.0f, w / float(h), 0.01f, 100.0f)
        ...

    def paintGL():

        # Draw the scene:
        f = QOpenGLContext.currentContext().functions()
        f.glClear(GL_COLOR_BUFFER_BIT)
        ...

或者,通过继承自QOpenGLFunctions来避免对每个OpenGL调用进行前缀

class MyGLWidget(QOpenGLWidget, QOpenGLFunctions):

    ...
    def initializeGL():

        initializeOpenGLFunctions()
        glClearColor(...)
        ...

    ...

要获取与给定OpenGL版本或配置兼容的上下文,或请求深度和模板缓冲区,请调用setFormat()

widget = QOpenGLWidget(parent)
format = QSurfaceFormat()
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函数的轻松访问

...
def paintGL():

    QOpenGLFunctions_3_2_Core f = QOpenGLContext.currentContext().versionFunctions<QOpenGLFunctions_3_2_Core>()
    ...
    f.glDrawArraysInstanced(...)
    ...

...

如上所述,设置请求的格式更简单、更健壮,使之在应用程序生命周期的整个过程中适用于所有窗口和上下文。以下是一个例子

if __name__ == "__main__":

    app = QApplication([])
    format = QSurfaceFormat()
    format.setDepthBufferSize(24)
    format.setStencilBufferSize(8)
    format.setVersion(3, 2)
    format.setProfile(QSurfaceFormat.CoreProfile)
    QSurfaceFormat.setDefaultFormat(format)
    widget = MyWidget()
    widget.show()
    sys.exit(app.exec())

多采样#

要启用多采样,请设置setFormat()中传递给QSurfaceFormat的请求采样数。在不支持它的系统上,请求可能会被忽略。

支持多采样需要支持多采样渲染缓冲区和帧缓冲区复制。在 OpenGL ES 2.0 实现中,这些可能不存在。这意味着无法使用多采样。在现代 OpenGL 版本和 OpenGL ES 3.0 及以上版本中,这通常不再是问题。

线程#

在工作线程上执行离屏渲染,例如在 paintGL() 中使用的纹理,可以通过公开小部件的 QOpenGLContext 来支持,这样就可以在每个线程上创建与其共享的附加上下文。

通过重写 paintEvent() 来执行无操作,可以在 GUI/main 之外的小部件帧缓冲区上直接绘制,从而实现QOpenGLWidget。需要通过(QObject::moveToThread())更改上下文的线程亲和力。之后,makeCurrent()doneCurrent() 可在工作线程中使用。此后,务必将上下文移回 GUI/main 线程。

由于没有真实的、屏幕上的原生表面,无法只为 QOpenGLWidget 触发缓冲区交换。由小部件堆栈管理 GUI 线程上的合成和缓冲区交换。当线程更新帧缓冲区完成后,请在 GUI/main 线程上调用 update(),以调度合成。

当 GUI/main 线程执行合成时,必须格外小心,以避免使用帧缓冲区。信号 aboutToCompose()frameSwapped() 在合成开始和结束时将被发射。它们在 GUI/main 线程上发射。这意味着通过使用直接的连接 aboutToCompose() 可以阻塞 GUI/main 线程,直到工作线程完成渲染。之后,工作线程必须停止进行任何进一步的渲染,直到发出 frameSwapped() 信号。如果这不可接受,工作线程必须实现一个双缓冲机制。这涉及使用替代渲染目标(由线程完全控制)进行绘制,例如额外的帧缓冲区对象,并在适当的时候将该目标绘制到 QOpenGLWidget 的帧缓冲区。

上下文共享#

当多个 QOpenGLWidgets 被添加为同一顶层窗口的子窗口时,它们的上下文将相互共享。这并不适用于属于不同窗口的 QOpenGLWidget 实例。

这意味着同一个窗口中的所有 QOpenGLWidgets 都可以访问彼此的共享资源,如纹理,无需额外的“全局共享”上下文。

要设置不同窗口中 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(QOpenGLWidget):

    ...
# private
    m_vao = QOpenGLVertexArrayObject()
    m_vbo = QOpenGLBuffer()
    m_program = QOpenGLShaderProgram()
    m_shader = QOpenGLShader()
    m_texture = QOpenGLTexture()

def __init__(self):
    self.m_program = 0
    self.m_shader = 0
    self.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()
    del m_texture
    del m_shader
    del m_program
    m_vbo.destroy()
    m_vao.destroy()
    doneCurrent()

def initializeGL(self):

    m_vao.create()
    if m_vao.isCreated():
        m_vao.bind()
    m_vbo.create()
    m_vbo.bind()
    m_vbo.allocate(...)
    m_texture = QOpenGLTexture(QImage(...))
    m_shader = QOpenGLShader(...)
    m_program = QOpenGLShaderProgram(...)
    ...

这适用于大多数情况,但作为一个通用解决方案并不完全理想。当小部件被移动到完全不同的顶级窗口中时,还需要更多的东西:通过连接到`QOpenGLContext`的`aboutToBeDestroyed()`信号,可以在OpenGL上下文即将释放时执行清理操作。

注意

对于在生命周期内多次更改其相关顶级窗口的小部件,以下代码片段演示的综合清理方法至关重要。每当小部件或它的父部件被重组,使得顶级窗口变为不同时,小部件的相关上下文将被销毁并创建一个新的。随后将调用initializeGL(),其中所有OpenGL资源都必须重新初始化。因此,唯一进行适当清理的方法是连接到上下文的aboutToBeDestroyed()信号。注意,在信号发出时,相关的上下文可能不是当前上下文。因此,在使用连接的槽调用makeCurrent()是良好的做法。此外,必须从派生类的析构函数中执行相同的清理步骤,因为连接到信号的槽或lambda表达式可能不会在小部件被销毁时调用。

MyGLWidget.~MyGLWidget()

    cleanup()

def initializeGL(self):

    ...
    connect(context(), &QOpenGLContext::aboutToBeDestroyed, self.cleanup)

def cleanup(self):

    makeCurrent()
    del m_texture
    m_texture = 0
    ...
    doneCurrent()
    disconnect(context(), &QOpenGLContext::aboutToBeDestroyed, self.cleanup)

注意

当Qt::AA_ShareOpenGLContexts被设置时,小部件的上下文永远不会改变,即使在重新组合的情况下,因为小部件的相关纹理也将从新的顶级窗口的上下文中访问。因此,设置此标志时,不需要对上下文的aboutToBeDestroyed()信号进行操作。

由于上下文共享,适当的清理非常重要。尽管每个QOpenGLWidget的相关上下文都与QOpenGLWidget一起销毁,但在该上下文中的可共享资源,如纹理,将保持有效,直到包含该QOpenGLWidget的顶级窗口被销毁。此外,设置如Qt::AA_ShareOpenGLContexts和一些Qt模块可能会触发更广泛的上下文共享范围,可能导致相关的资源在整个应用程序的生命周期内保持活动状态。因此,始终对在QOpenGLWidget中使用的所有资源和资源包装进行显式清理是最安全和最稳健的方法。

局限性和其他注意事项#

将其他小部件放置在下面并使QOpenGLWidget透明将不会导致预期的结果:下方的小部件将不可见。这是因为实际上,QOpenGLWidget在所有其他常规非OpenGL小部件之前被绘制,因此透明类型的解决方案是不可行的。其他类型的布局,如小部件放置在QOpenGLWidget的上方,将按预期工作。

在绝对必要的情况下,可以通过设置 QOpenGLWidget 的 Qt::WA_AlwaysStackOnTop 属性来克服此限制。但请注意,这将打破堆叠顺序,例如,将在 QOpenGLWidget 上无法放置其他控件,因此仅在需要放置半透明 QOpenGLWidget 且下面有其他可见控件的情况下使用。

请注意,当下面没有其他控件且意图拥有半透明窗口时,此方法不适用。在这种情况下,在顶级窗口上设置 Qt::WA_TranslucentBackground 的传统方法就足够了。注意,如果只想在 QOpenGLWidget 中有透明的区域,则需要在启用 Qt::WA_TranslucentBackground 后将 Qt::WA_NoSystemBackground 重新设置为 false。此外,根据系统,请求 QOpenGLWidget 的上下文的 alpha 通道也可能有必要。

QOpenGLWidget 支持多种更新行为,就像 QOpenGLWindow 一样。在保留模式下,前一个 paintGL() 调用的渲染内容在下一个调用中可用,从而允许增量渲染。在不保留模式下,内容会被丢失,并且 paintGL() 的实现期望重新绘制视图中的所有内容。

在 Qt 5.5 之前,QOpenGLWidget 的默认行为是在 paintGL() 调用之间保留渲染内容。自 Qt 5.5 以来,默认行为是不保留,因为这提供了更好的性能,而大多数应用程序都不需要之前的内容。这也类似于基于 OpenGL 的 QWindow 的语义,并且与 QOpenGLWindow 的默认行为相匹配,即在每一帧中颜色和辅助缓冲区都会被失效。为了恢复保留行为,需要调用 setUpdateBehavior() 并设置 PartialUpdate

注意

当动态地将QOpenGLWidget添加到窗口层次结构中时,例如通过将一个新的QOpenGLWidget设置为显示在屏幕上的顶层窗口的父窗口,如果QOpenGLWidget是其窗口中的第一个同类,相关的本地窗口可能会被隐式销毁并重新创建。这是因为窗口类型从RasterSurface变为OpenGLSurface,这在平台上有特定的含义。这是Qt 6.4中的新行为。

一旦将QOpenGLWidget添加到窗口层次结构中,顶层窗口的内容将通过OpenGL渲染刷新。除QOpenGLWidget之外的窗口继续使用基于软件的画家绘制其内容,但最后的合成是通过3D API完成的。

注意

由于与其他基于QWidget的内容的合成方式,显示QOpenGLWidget需要关联顶层窗口的 backing store中的alpha通道。如果没有alpha通道,QOpenGLWidget渲染的内容将不可见。这在Linux/X11的远程显示设置(例如,使用Xvnc)尤其相关,当颜色深度低于24时。例如,颜色深度为16通常映射到使用QImage::Format_RGB16(RGB565)格式的backing store图像,没有为alpha通道留下空间。因此,如果遇到的难题是无法正确与其他窗口中的widget合成QOpenGLWidget的内容,请确保服务器(例如,vncserver)配置为24位或32位深度而不是16位。

替代方案#

将一个 QOpenGLWidget 添加到窗口中会使整个窗口开启基于OpenGL的合成。在某些特殊情况下,这可能不是理想的,希望使用旧式的 QGLWidget 风格行为,即一个独立的、原生的子窗口。理解这种方案局限性的桌面应用程序(例如,当涉及到重叠、透明度、滚动视图和 MDI 区域时),可以使用 QOpenGLWindow 与 QWidget::createWindowContainer()。这是 QGLWidget 的现代替代品,因为它没有额外的合成步骤,所以运行速度更快。强烈建议将这种方法的适用范围限制在没有其他选择的情况。请注意,此选项不适合大多数嵌入式和移动平台,并且在某些桌面平台(如 macOS)上也存在已知问题。稳定的跨平台解决方案始终是 QOpenGLWidget

立体渲染#

从 6.5 版本开始,QOpenGLWidget 支持立体渲染。要启用它,在创建窗口前使用 QSurfaceFormat::SetDefaultFormat() 全局设置 QSurfaceFormat::StereoBuffers 标志。

注意

由于标志的内部处理方式,使用 setFormat() 未必有效。

这将导致每帧执行两次 paintGL(),一次针对每个 TargetBuffer。在 paintGL() 中,调用 currentTargetBuffer() 来查询当前正在绘制的哪一个。

注意

为了更好地控制左右颜色缓冲区,请考虑使用 QOpenGLWindow + QWidget::createWindowContainer()。

注意

这种类型的 3D 渲染有一定硬件要求,如图形卡需要设置为支持立体。

OpenGL 是在美国及其他国家的 Silicon Graphics, Inc. 的商标。

class UpdateBehavior#

此枚举描述了 QOpenGLWidget 的更新语义。

常量

描述

QOpenGLWidget.NoPartialUpdate

QOpenGLWidget在渲染到屏幕后,会丢弃颜色缓冲区和辅助缓冲区的所有内容。这与使用默认启用OpenGL的QWindow作为参数调用QOpenGLContext::swapBuffers的行为相同。当使用帧缓冲对象作为渲染目标时,NoPartialUpdate在某些在移动和嵌入式领域常见的硬件架构上可能具有一些性能优势。通过glInvalidateFramebuffer(如果受支持)或在备选方案中通过glDiscardFramebufferEXT(如果受支持)或调用glClear来确保帧缓冲对象在帧之间的无效化。

QOpenGLWidget.PartialUpdate

在帧之间不会使帧缓冲对象的颜色缓冲区和辅助缓冲区无效。

class TargetBuffer#

指定在启用立体渲染时使用的缓冲区,立体渲染通过设置QSurfaceFormat::StereoBuffers切换。

注意

LeftBuffer始终是默认选项,当立体渲染不可用或图形驱动程序不支持时作为回退值使用。

常量

描述

QOpenGLWidget.LeftBuffer

QOpenGLWidget.RightBuffer

自6.5版本开始。

__init__([parent=None[, f=Qt.WindowFlags()]])#
参数:

构造一个作为parent子项的控件,并设置控件标志为f

aboutToCompose()#

当组件的上层窗口开始组合其QOpenGLWidget子项和其他控件的纹理时,会发出此信号。

aboutToResize()#

当控件的大小发生变化时,会发出此信号,因此将重新创建帧缓冲对象。

context()#
返回类型::

QOpenGLContext

返回由该小部件使用的 QOpenGLContext 或 0 如果尚未初始化。

注意

当通过 setParent() 函数重新设置小部件的父级时,小部件使用的上下文和帧缓冲对象会发生变化。

currentTargetBuffer()#
返回类型::

目标缓冲区

返回当前激活的目标缓冲区。默认情况下,这将是最左侧的缓冲区,而右侧缓冲区仅在启用 QSurfaceFormat::StereoBuffers 的情况下使用。当启用立体渲染时,这可以在 paintGL() 中查询,以确定当前正在使用的缓冲区。在 paintGL() 被调用两次时,一次对应每个目标。

另请参阅

paintGL()

defaultFramebufferObject()#
返回类型::

int

返回帧缓冲对象句柄或 0 如果尚未初始化。

注意

帧缓冲对象属于由 context() 返回的上下文,可能无法在其他上下文中访问。

注意

当通过 setParent() 函数通过子代化小部件时,小部件使用的上下文和帧缓冲对象会发生变化。此外,每次调整大小都会改变帧缓冲对象。

另请参阅

context()

defaultFramebufferObject(targetBuffer)
参数:

targetBufferTargetBuffer

返回类型::

int

返回指定目标缓冲区的帧缓冲对象句柄或 0 如果尚未初始化。

如果 QSurfaceFormat::StereoBuffers 被启用且硬件支持,则调用此重载才有意义。否则,此方法将返回默认缓冲区。

注意

帧缓冲对象属于由 context() 返回的上下文,可能无法在其他上下文中访问。当通过 setParent() 函数通过子代化小部件时,小部件使用的上下文和帧缓冲对象会发生变化。此外,每次调整大小都会改变帧缓冲对象。

另请参阅

context()

doneCurrent()#

释放上下文。

在大多数情况下,不需要调用此函数,因为小部件将确保在调用 paintGL() 时上下文绑定和释放正确。

format()#
返回类型::

QSurfaceFormat

返回由该小部件及其顶级窗口使用的上下文和表面格式。

在创建、调整大小并显示小部件及其顶层后,此函数将返回实际的上下文格式。如果平台无法满足请求,则实际格式可能与请求的格式不同。还可能获得比请求更大的颜色缓冲区大小。

当小部件的窗口和相关的OpenGL资源尚未初始化时,返回值是通过 setFormat() 设置的格式。

另请参阅

setFormat() context()

frameSwapped()#

该信号在小部件的顶层窗口完成合成并从其可能的阻塞函数 QOpenGLContext::swapBuffers() 返回后发出。

grabFramebuffer()#
返回类型::

QImage

渲染并返回一个32位RGB缓冲区图像。

注意

这是一个潜在的昂贵操作,因为它依赖 glReadPixels() 来读取回像素。这可能会很慢,并可能导致GPU管线停滞。

grabFramebuffer(targetBuffer)
参数:

targetBufferTargetBuffer

返回类型::

QImage

渲染并返回指定目标缓冲区的32位RGB缓冲区图像。这个重载只在对准 QSurfaceFormat::StereoBuffers 时有意义。如果立体渲染被禁用或设备不支持,则从正确的目标缓冲区获取帧缓冲区将返回默认图像。

注意

这是一个潜在的昂贵操作,因为它依赖 glReadPixels() 来读取回像素。这可能会很慢,并可能导致GPU管线停滞。

initializeGL()#

此虚拟函数在首次调用 paintGL()resizeGL() 之前被调用一次。在子类中重新实现它。为此函数,不需要调用 makeCurrent() ,因为在此函数调用时已经完成。但请注意,此时帧缓冲区尚不可用,因此请避免在此处发出绘图调用。将这些调用推迟到 paintGL() 中。

此函数应设置任何必需的OpenGL资源。

不用调用 makeCurrent() ,因为这个函数被调用时已经执行了。然而,请注意,此阶段的帧缓冲区尚不可用,因此请避免从此处发出绘图调用。应将这些调用推迟到 paintGL() 中。

另请参阅

paintGL() resizeGL()

isValid()#
返回类型::

bool

如果小部件和OpenGL资源,如上下文,已成功初始化,则返回 true。请注意,返回值始终为false,直到小部件显示。

makeCurrent()#

通过使相应的上下文成为当前上下文并将帧缓冲对象绑定在该上下文中,为此小部件准备渲染OpenGL内容。

在大多数情况下,没有必要调用此函数,因为它在调用 `paintGL()` 之前自动调用。

makeCurrent(targetBuffer)
参数:

targetBufferTargetBuffer

通过将传入的缓冲区的上下文设置为当前上下文并将帧缓冲对象绑定在该上下文中,为此小部件准备渲染OpenGL内容。

注意

仅在启用立体渲染时才调用此函数才有意义。如果未启用,请求右侧缓冲区时不会发生任何操作。

在大多数情况下,没有必要调用此函数,因为它在调用 `paintGL()` 之前自动调用。

paintGL()#

该虚拟函数在每次需要绘制小部件时都会被调用。在子类中重新实现它。

由于在调用此函数时已经执行了此操作,因此无需调用 makeCurrent()

在调用此函数之前,通过调用 glViewport() 设置视口,并将上下文和帧缓冲区绑定。框架不设置其他状态也不执行清除或绘图。

默认实现执行一个 glClear()。子类不应调用基类的实现,而应自行执行清除。

注意

为了确保可移植性,不要期望在 initializeGL() 中设置的状态保持不变。相反,在 paintGL() 中设置所有必要的状态,例如通过调用 glEnable()。这是因为某些平台(如使用 WebGL 的 WebAssembly)可能在某些情况下对 OpenGL 上下文有限制,这可能导致使用具有 QOpenGLWidget 的上下文执行其他用途。

当 QSurfaceFormat::StereoBuffers 启用时,此函数将被调用两次——一次为每个缓冲区。通过调用 currentTargetBuffer() 查询当前绑定的缓冲区。

注意

即使硬件不支持立体渲染,每个目标的帧缓冲区也将被绘制。只有在窗口中实际上将可见的是左侧缓冲区。

resizeGL(w, h)#
参数:
  • w – int

  • h – int

这个虚函数会在小部件被调整大小时被调用。在子类中重新实现它。新的大小以 wh 的形式传入。

不需要调用 makeCurrent(),因为当调用此函数时已经完成了这项工作。此外,帧缓冲区也已绑定。

另请参阅

initializeGL() paintGL()

resized()#

在由于调整小部件大小而重启帧缓冲区对象后,将发出此信号。

setFormat(format)#
参数:

formatQSurfaceFormat

设置所需的表面 格式

当通过此函数未显式设置格式时,将使用 QSurfaceFormat::defaultFormat() 返回的格式。这意味着当有多个 OpenGL 小部件时,可以通过创建第一个小部件之前的单个调用 QSurfaceFormat::setDefaultFormat() 来替换对此函数的多次调用。

注意

通过此函数请求 Alpha 缓冲区不能达到预期的效果,如果目的是让下面的其他小部件可见。相反,请使用 Qt::WA_AlwaysStackOnTop 启用透明 QOpenGLWidget 实例和其他下面的可见小部件。但是请记住,这将破坏堆叠顺序,因此将不能再在其他小部件之上有 QOpenGLWidget

另请参阅

format() setDefaultFormat()

setTextureFormat(texFormat)#
参数:

texFormat – int

设置自定义内部纹理格式 texFormat

当与 sRGB 帧缓冲区一起工作时,将需要指定类似于 GL_SRGB8_ALPHA8 的格式。这可以通过调用此函数来实现。

注意

如果该控件已被显示并进行了初始化,则调用该函数将没有作用。

注意

通常需要在使用 QSurfaceFormat::setDefaultFormat() 调用时,将颜色空间设置为 QSurfaceFormat::sRGBColorSpace 来与该函数一起使用。

另请参阅

textureFormat()

setUpdateBehavior(updateBehavior)#
参数:

updateBehaviorUpdateBehavior

将此控件的更新行为设置为 updateBehavior

另请参阅

updateBehavior()

textureFormat()#
返回类型::

int

如果控件已初始化,则返回活动内部纹理格式,如果设置了请求的格式但控件尚未可见,则返回请求的格式,如果未调用 setTextureFormat() 且控件尚未可见,则返回 None

另请参阅

setTextureFormat()

updateBehavior()#
返回类型::

UpdateBehavior

返回控件的更新行为。

另请参阅

setUpdateBehavior()