PySide6.QtOpenGLWidgets.QOpenGLWidget

class QOpenGLWidget

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

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()

注意

在构建QApplication实例之前调用QSurfaceFormat::setDefaultFormat()在某些平台(例如,macOS)上是强制性的,当请求OpenGL核心配置文件上下文时。这是为了确保上下文之间的资源共享保持功能正常,因为所有内部上下文都是使用正确的版本和配置文件创建的。

绘画技巧

如上所述,子类 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(在制作可移植应用程序时)或版本化变体(例如,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,而qopengl.h将依次包含系统的适当头文件。这可能是OpenGL ES 3.x或2.0头文件,可用的最高版本,或系统提供的gl.h。此外,扩展头文件的副本(在某些系统上称为glext.h)作为Qt的一部分提供,适用于OpenGL和OpenGL ES。在可行的平台上,这些头文件将自动包含。这意味着来自ARB、EXT、OES扩展的常量和函数指针类型定义将自动可用。

代码示例

要开始使用,最简单的 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及以上版本,这通常不再是一个问题。

线程

在工作者线程上执行离屏渲染,例如生成纹理,然后在GUI/主线程中的paintGL()中使用,通过暴露小部件的QOpenGLContext来支持,以便可以在每个线程上创建与其共享的额外上下文。

通过重新实现paintEvent()使其不执行任何操作,可以直接在GUI/主线程之外绘制到QOpenGLWidget的帧缓冲区。必须通过QObject::moveToThread()更改上下文的线程关联性。之后,makeCurrent()doneCurrent()可以在工作线程上使用。注意之后要将上下文移回GUI/主线程。

仅为QOpenGLWidget触发缓冲区交换是不可能的,因为它没有真正的屏幕上的本地表面。由小部件堆栈来管理GUI线程上的合成和缓冲区交换。当线程完成帧缓冲区的更新时,在GUI/主线程上调用update()来安排合成。

在GUI/主线程执行合成时,必须格外小心避免使用帧缓冲区。当合成开始和结束时,将发出信号aboutToCompose()frameSwapped()。这些信号在GUI/主线程上发出。这意味着通过使用直接连接,aboutToCompose()可以阻塞GUI/主线程,直到工作线程完成其渲染。之后,工作线程必须停止进一步的渲染,直到发出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辅助类,如QOpenGLBufferQOpenGLVertexArrayObject,具有匹配的延迟行为:它们可以在没有上下文的情况下实例化,但所有初始化都推迟到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。此外,根据系统的不同,可能还需要通过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完成的。

注意

显示QOpenGLWidget需要在关联的顶层窗口的后备存储中具有alpha通道,这是由于与其他基于QWidget的内容的合成方式所致。如果没有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()每帧被调用两次,每次对应一个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()]])
Parameters:

构造一个作为parent子部件的小部件,小部件标志设置为f

aboutToCompose()

当小部件的顶层窗口即将开始组合其QOpenGLWidget子部件和其他小部件的纹理时,会发出此信号。

aboutToResize()

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

context()
Return type:

QOpenGLContext

返回此小部件使用的QOpenGLContext,如果尚未初始化则返回0

注意

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

currentTargetBuffer()
Return type:

TargetBuffer

返回当前活动的目标缓冲区。默认情况下,这将是左侧缓冲区,只有在启用QSurfaceFormat::StereoBuffers时才会使用右侧缓冲区。当启用立体渲染时,可以在paintGL()中查询以了解当前正在使用的缓冲区。paintGL()将被调用两次,每次针对一个目标。

另请参阅

paintGL()

defaultFramebufferObject()
Return type:

整数

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

注意

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

注意

当通过setParent()重新设置小部件的父级时,小部件使用的上下文和帧缓冲对象会发生变化。此外,帧缓冲对象在每次调整大小时都会发生变化。

另请参阅

context()

defaultFramebufferObject(targetBuffer)
Parameters:

targetBufferTargetBuffer

Return type:

整数

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

只有在硬件支持并启用了QSurfaceFormat::StereoBuffers时,调用此重载才有意义。如果不支持,此方法将返回默认缓冲区。

注意

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

另请参阅

context()

doneCurrent()

释放上下文。

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

format()
Return type:

QSurfaceFormat

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

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

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

另请参阅

setFormat() context()

frameSwapped()

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

grabFramebuffer()
Return type:

QImage

渲染并返回帧缓冲区的32位RGB图像。

注意

这是一个可能代价高昂的操作,因为它依赖于glReadPixels()来读取像素。这可能会很慢,并且可能会阻塞GPU管道。

grabFramebuffer(targetBuffer)
Parameters:

targetBufferTargetBuffer

Return type:

QImage

渲染并返回指定目标缓冲区的帧缓冲区的32位RGB图像。只有在启用QSurfaceFormat::StereoBuffers时,调用此重载才有意义。如果立体渲染被禁用或硬件不支持,抓取右侧目标缓冲区的帧缓冲区将返回默认图像。

注意

这是一个可能代价高昂的操作,因为它依赖于glReadPixels()来读取像素。这可能会很慢,并且可能会阻塞GPU管道。

initializeGL()

这个虚函数在第一次调用paintGL()resizeGL()之前被调用一次。在子类中重新实现它。

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

不需要调用makeCurrent(),因为在调用此函数时已经完成了此操作。但请注意,此时帧缓冲区尚不可用,因此避免从此处发出绘制调用。将此类调用推迟到paintGL()

另请参阅

paintGL() resizeGL()

isValid()
Return type:

布尔

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

makeCurrent()

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

在大多数情况下不需要调用此函数,因为在调用 paintGL() 之前会自动调用它。

makeCurrent(targetBuffer)
Parameters:

targetBufferTargetBuffer

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

注意

只有在启用立体渲染时调用才有意义。如果禁用了右缓冲区,则不会发生任何事情。

在大多数情况下不需要调用此函数,因为在调用 paintGL() 之前会自动调用它。

paintGL()

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

不需要调用makeCurrent(),因为在调用此函数时已经完成了此操作。

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

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

注意

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

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

注意

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

resizeGL(w, h)
Parameters:
  • w – 整数

  • h – 整数

每当小部件调整大小时,都会调用此虚拟函数。在子类中重新实现它。新的大小通过 wh 传递。

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

另请参阅

initializeGL() paintGL()

resized()

当由于调整小部件大小而重新创建帧缓冲对象后,会立即发出此信号。

setFormat(format)
Parameters:

格式QSurfaceFormat

设置请求的表面format

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

注意

通过此函数请求alpha缓冲区不会达到预期的效果,当意图是使其他小部件在下方可见时。相反,使用Qt::WA_AlwaysStackOnTop来启用半透明的QOpenGLWidget实例,并使其他小部件在下方可见。但请记住,这会破坏堆叠顺序,因此将不再可能在其他QOpenGLWidget上方放置其他小部件。

另请参阅

format() setDefaultFormat()

setTextureFormat(texFormat)
Parameters:

texFormat – int

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

当处理sRGB帧缓冲区时,需要指定像GL_SRGB8_ALPHA8这样的格式。这可以通过调用此函数来实现。

注意

如果在小部件已经显示并因此执行了初始化之后调用此函数,则此函数无效。

注意

此函数通常需要与设置颜色空间为QSurfaceFormat::sRGBColorSpace的QSurfaceFormat::setDefaultFormat()调用结合使用。

另请参阅

textureFormat()

setUpdateBehavior(updateBehavior)
Parameters:

updateBehaviorUpdateBehavior

将此小部件的更新行为设置为updateBehavior

另请参阅

updateBehavior()

textureFormat()
Return type:

整数

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

另请参阅

setTextureFormat()

updateBehavior()
Return type:

UpdateBehavior

返回小部件的更新行为。

另请参阅

setUpdateBehavior()