QOpenGLWidget Class

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

Header: #include <QOpenGLWidget>
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

详细说明

QOpenGLWidget 为显示集成到 Qt 应用程序中的 OpenGL 图形提供功能。它的使用非常简单:让您的类继承自它,然后像使用其他QWidget 一样使用子类,只不过您可以选择使用QPainter 或标准 OpenGL 渲染命令。

QOpenGLWidget 提供了三个方便的虚拟函数,您可以在子类中重新实现这些函数,以执行典型的 OpenGL 任务:

  • paintGL() - 渲染 OpenGL 场景。每当需要更新 widget 时都会被调用。
  • 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() 。

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 头文件将包括 qopengl.h,而 qopengl.h 又将包括一个合适的系统头文件。这可能是 OpenGL ES 3.x 或 2.0 头文件(可用的最高版本),也可能是系统提供的 gl.h。此外,Qt 还为 OpenGL 和 OpenGL ES 提供了扩展头文件的副本(在某些系统中称为 glext.h)。在可行的情况下,这些扩展头文件会在平台上自动包含。这意味着来自 ARB、EXT 和 OES 扩展的常量和函数指针类型定义将自动可用。

代码示例

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

线程

在工作线程上执行屏幕外渲染(例如生成纹理,然后在paintGL() 中用于 GUI/主线程),可通过公开部件的QOpenGLContext 来支持,这样就可以在每个线程上创建与之共享的附加上下文。

在图形用户界面/主线程之外直接向 QOpenGLWidget 的帧缓冲区绘制图形,可以通过重新实现paintEvent() 来实现。必须通过QObject::moveToThread() 更改上下文的线程亲和性。之后,makeCurrent() 和doneCurrent() 就可以在工作线程上使用了。之后要注意将上下文移回 GUI/主线程。

仅为 QOpenGLWidget 触发缓冲区交换是不可能的,因为它没有真正的屏幕原生表面。在图形用户界面线程上,由部件栈来管理组成和缓冲区交换。当一个线程更新完帧缓冲区后,在 GUI/主线程上调用update() 来安排合成。

在图形用户界面/主线程执行合成时,必须格外注意避免使用帧缓冲区。合成开始和结束时,将发出aboutToCompose() 和frameSwapped() 信号。它们是在图形用户界面/主线程上发出的。这意味着通过使用直接连接aboutToCompose() 可以阻塞 GUI/主线程,直到工作线程完成渲染。在此之后,工作线程不得再执行任何呈现操作,直到frameSwapped() 信号发出为止。如果无法接受这种情况,工作线程必须执行双重缓冲机制。这包括使用完全由线程控制的其他渲染目标(如额外的帧缓冲对象)进行绘制,并在适当的时间混合到 QOpenGLWidget 的帧缓冲中。

上下文共享

当多个 QOpenGLWidget 被添加为同一顶层 widget 的子 widget 时,它们的上下文将相互共享。这不适用于属于不同窗口的 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 辅助类(如QOpenGLBufferQOpenGLVertexArrayObject )也有类似的延迟行为:它们可以在没有上下文的情况下实例化,但所有初始化都会延迟到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(...);

    ...
}

这在大多数情况下都有效,但作为通用解决方案并不完全理想。当窗口部件被重新资源化,以至于最终出现在一个完全不同的顶层窗口中时,就需要采取更多措施:通过连接到QOpenGLContextaboutToBeDestroyed() 信号,只要 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 XML 模块等设置可能会触发更广泛的上下文共享范围,从而可能导致相关资源在应用程序的整个生命周期内都保持有效。因此,最安全、最稳健的方法始终是对 QOpenGLWidget 中使用的所有资源和资源包装器执行显式清理。

限制和其他注意事项

在 QOpenGLWidget 下面放置其他窗口小部件并使 QOpenGLWidget 透明不会导致预期的结果:下面的部件将不可见。这是因为在实践中,QOpenGLWidget 是在所有其他常规的、非 OpenGL 的 widget 之前绘制的,因此透明类型的解决方案是不可行的。其他类型的布局,如在 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 添加到 widget 层次结构中时(例如,将一个新的 QOpenGLWidget 添加到一个 widget 中,而相应的顶层 widget 已经显示在屏幕上),如果 QOpenGLWidget 是其窗口中的第一个同类,则相关的本地窗口可能会被隐式地销毁和重新创建。这是因为窗口类型从RasterSurface 变为了OpenGLSurface ,这对特定平台有影响。这种行为是 Qt 6.4 中的新特性。

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

注: 显示 QOpenGLWidget 需要在相关顶层窗口的后备存储中设置一个 alpha 通道,这是因为与其他基于QWidget 的内容进行合成的工作方式。如果没有 alpha 通道,QOpenGLWidget 呈现的内容将不可见。在 Linux/X11 上的远程显示设置(如使用 Xvnc)中,当使用的色深低于 24 时,这一点尤其重要。例如,色彩深度为 16 时,通常会映射为使用格式为QImage::Format_RGB16 (RGB565) 的后备存储图像,没有为 alpha 通道留出空间。因此,如果在将 QOpenGLWidget 的内容与窗口中的其他 widget 正确合成时遇到问题,请确保服务器(如 vncserver)配置为 24 或 32 位深度,而不是 16 位深度。

替代方案

在窗口中添加 QOpenGLWidget 可为整个窗口打开基于 OpenGL 的合成。在某些特殊情况下,这种方法可能并不理想,这时就需要使用 QOpenGLWidget 风格的老式行为,即单独的本地子窗口。了解这种方法的局限性(例如涉及重叠、透明度、滚动视图和 MDI 区域时)的桌面应用程序可以使用QOpenGLWindowQWidget::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 公司在美国和其他国家的商标。

另请参阅 QOpenGLFunctions,QOpenGLWindow,Qt::AA_ShareOpenGLContexts, 和UpdateBehavior

成员类型文档

[since 6.5] enum QOpenGLWidget::TargetBuffer

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

注: LeftBuffer 始终是默认值,在禁用立体渲染或图形驱动程序不支持立体渲染时用作后备值。

常量
QOpenGLWidget::LeftBuffer0
QOpenGLWidget::RightBuffer1

此枚举在 Qt 6.5 中引入。

enum QOpenGLWidget::UpdateBehavior

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

常量描述
QOpenGLWidget::NoPartialUpdate0QOpenGLWidget 将在 渲染到屏幕后丢弃颜色缓冲区和辅助缓冲区的内容。这与使用默认启用 opengl 的 作为参数调用 时的预期行为相同。将帧缓冲对象用作渲染目标时,NoPartialUpdate 在某些移动和嵌入式领域常见的硬件架构上具有一定的性能优势。帧缓冲对象在帧与帧之间通过 glInvalidateFramebuffer(如果支持)或 glDiscardFramebufferEXT(如果支持)或调用 glClear 来失效。QOpenGLWidget QWindow QOpenGLContext::swapBuffers
QOpenGLWidget::PartialUpdate1帧缓冲区对象的颜色缓冲区和辅助缓冲区不会在帧之间失效。

另请参阅 updateBehavior() 和setUpdateBehavior()。

成员函数文档

[explicit] QOpenGLWidget::QOpenGLWidget(QWidget *parent = nullptr, Qt::WindowFlags f = Qt::WindowFlags())

构造一个 widget,它是parent 的子 widget,widget 标志设置为f

[virtual noexcept] QOpenGLWidget::~QOpenGLWidget()

销毁QOpenGLWidget 实例,释放其资源。

在析构函数中,QOpenGLWidget 的上下文是当前的,这样就可以安全地销毁任何可能需要释放属于此 widget 提供的上下文的 OpenGL 资源的子对象。

警告: 如果将封装 OpenGL 资源的对象(如QOpenGLBufferQOpenGLShaderProgram 等)作为 OpenGLWidget 子类的成员,则可能需要在该子类的析构函数中添加对makeCurrent() 的调用。根据 C++ 对象销毁的规则,这些对象将调用此函数之前销毁(但在子类的析构函数运行之后),因此在此函数中将 OpenGL 上下文设置为当前状态为时已晚,无法安全地处理这些对象。

另请参见 makeCurrent

[signal] void QOpenGLWidget::aboutToCompose()

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

[signal] void QOpenGLWidget::aboutToResize()

当 widget 的大小发生变化,因此要重新创建帧缓存对象时,就会发出这个信号。

QOpenGLContext *QOpenGLWidget::context() const

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

注意: 当通过setParent() 对 widget 进行重新泛型时,widget 使用的上下文和帧缓冲对象会发生变化。

另请参阅 QOpenGLContext::setShareContext() 和defaultFramebufferObject()。

[since 6.5] QOpenGLWidget::TargetBuffer QOpenGLWidget::currentTargetBuffer() const

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

此函数在 Qt 6.5 中引入。

另请参阅 paintGL()。

GLuint QOpenGLWidget::defaultFramebufferObject() const

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

注意: framebuffer 对象属于context() 返回的上下文,其他上下文可能无法访问。

注意: 当通过setParent() 对窗口小部件进行重新分配时,窗口小部件使用的上下文和帧缓冲区对象会发生变化。此外,每次调整大小时,framebuffer 对象都会发生变化。

另请参见 context()。

[since 6.5] GLuint QOpenGLWidget::defaultFramebufferObject(QOpenGLWidget::TargetBuffer targetBuffer) const

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

只有在QSurfaceFormat::StereoBuffers 启用且硬件支持的情况下,调用此重载才有意义。否则,该方法将返回默认缓冲区。

注意: 帧缓冲区对象属于context() 返回的上下文,其他上下文可能无法访问。当通过setParent() 对 widget 进行重新分配时,widget 使用的上下文和 framebuffer 对象会发生变化。此外,每次调整大小时,帧缓冲区对象都会发生变化。

该函数在 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() 设置的格式。

另请参见 setFormat() 和context()。

[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() 进行。

另请参见 paintGL() 和resizeGL()。

bool QOpenGLWidget::isValid() const

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

void QOpenGLWidget::makeCurrent()

将相应的上下文设置为当前,并将帧缓冲对象绑定到该上下文中,为渲染该 widget 的 OpenGL 内容做好准备。

在大多数情况下无需调用该函数,因为在调用paintGL() 之前会自动调用该函数。

另请参阅 context()、paintGL() 和doneCurrent()。

[since 6.5] void QOpenGLWidget::makeCurrent(QOpenGLWidget::TargetBuffer targetBuffer)

为渲染此 widget 的 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()

每当需要绘制 widget 时,都会调用该虚拟函数。请在子类中重新实现它。

无需调用makeCurrent() ,因为在调用该函数时已经完成了绘制。

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

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

注意: 为确保可移植性,不要指望在initializeGL() 中设置的状态会持续存在。相反,应通过调用 glEnable() 等方式在 paintGL() 中设置所有必要的状态。这是因为某些平台(如使用 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 的实现。否则,底层 framebuffer 对象和相关资源将无法正确调整大小,从而导致不正确的渲染。

[virtual protected] void QOpenGLWidget::resizeGL(int w, int h)

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

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

另请参见 initializeGL() 和paintGL()。

[signal] void QOpenGLWidget::resized()

该信号会在由于调整 widget 大小而重新创建帧缓存对象后立即发出。

void QOpenGLWidget::setFormat(const QSurfaceFormat &format)

设置所要求的曲面format

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

注意: 如果想让其他部件在下方可见,那么通过此函数请求阿尔法缓冲区将不会达到预期效果。相反,可以使用Qt::WA_AlwaysStackOnTop 来启用半透明的QOpenGLWidget 实例,使其下方的其他部件可见。但请注意,这会破坏堆叠顺序,因此无法再在QOpenGLWidget 上放置其他部件。

另请参阅 format(),Qt::WA_AlwaysStackOnTopQSurfaceFormat::setDefaultFormat()。

void QOpenGLWidget::setTextureFormat(GLenum texFormat)

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

在使用 sRGB 帧缓冲时,有必要指定一种格式,如GL_SRGB8_ALPHA8 。这可以通过调用此函数来实现。

注: 如果在已显示窗口小部件后调用此函数,则不会产生任何效果,因为它已执行了初始化。

注: 该函数通常必须与QSurfaceFormat::setDefaultFormat() 调用结合使用,后者将色彩空间设置为 QSurfaceFormat::sRGBColorSpace。

另请参阅 textureFormat().

void QOpenGLWidget::setUpdateBehavior(QOpenGLWidget::UpdateBehavior updateBehavior)

将此 widget 的更新行为设置为updateBehavior

另请参阅 updateBehavior() 。

GLenum QOpenGLWidget::textureFormat() const

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

另请参阅 setTextureFormat() 。

QOpenGLWidget::UpdateBehavior QOpenGLWidget::updateBehavior() const

返回 widget 的更新行为。

另请参见 setUpdateBehavior()。

© 2025 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.