QGLWidget、QOpenGLWidget详解及区别

QGLWidget

概述

QGLWidget类是用于呈现OpenGL图形的小部件。

QGLWidget提供了显示集成到Qt应用程序中的OpenGL图形的功能。它很容易使用。继承它并使用子类,就像其他任何QWidget一样,额外的可以选择使用QPainter和标准OpenGL渲染命令。

注意:这个类是传统QtOpenGL模块的一部分,与其他QGL类一样,应该在新的应用程序中避免使用。相反,从Qt5.4开始,Qt推荐使用QOpenGLWidget和QOpenGL类。

QGLWidget提供了三个方便的虚拟函数,可以在子类中重写这些函数来执行典型的OpenGL任务:

  • paintGL():渲染OpenGL场景。每当需要更新小部件时调用。
  • resizeGL ():设置OpenGL视区、投影等。每当小部件调整了大小时都会调用该视区(并且当它第一次显示时也会调用,因为所有新创建的小部件都会自动获得一个调整大小的事件)。
  • initializeGL():设置OpenGL呈现上下文,定义显示列表等。在第一次调用resizeGL ()或paintGL ()之前调用一次。

QGLWidget子类示例

继承后,主要是实现三个虚拟函数。

 本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓

更新绘制

如果需要从paintGL()以外的地方触发重新绘制(典型的例子是使用计时器来动画场景),应该调用小部件的updateGL()函数。

当调用paintGL()、resizeGL()或initializeGL()时,小部件的OpenGL呈现上下文成为当前上下文。如果需要从其他地方调用标准的OpenGL API函数(例如,在小部件的构造函数或自己的paint函数中),则必须首先调用makeCurrent()。

QGLWidget提供了请求新显示格式的功能,您还可以使用自定义的呈现上下文创建小部件。

还可以在QGLwidget对象之间共享OpenGL显示列表(有关详细信息,请参阅QGLwidget构造函数的文档)。

请注意,在Windows下,重新编写QGLWidget时,必须重新创建属于QGLWidget的QGLContext。由于Windows平台的限制,这是必需的。这很可能会给子类化并在QGLwidget上安装了自己的QGLContext的用户带来问题。通过将QGLWidget放在一个虚拟小部件中,然后重新设置虚拟小部件(而不是QGLWidget),可以解决这个问题。这将完全避免这个问题,并且是我们为需要这种功能的用户推荐的。

在MacOS上,当Qt使用Cocoa支持构建时,QGLWidget不能将任何兄弟的widget放在自己的本体上。这是由于可可API的限制,苹果不支持。

覆盖层

如果底层系统支持覆盖,那么QGLWidget除了正常上下文之外还创建一个GL覆盖层上下文。

如果要使用覆盖层,请以格式指定。(注意:必须以传递给QGLWidget构造函数的格式请求覆盖。)GL部件还应实现以下部分或全部虚拟方法:

  • paintOverlayGL()
  • resizeOverlayGL()
  • initializeOverlayGL()

这些方法与普通paintGL()等函数的工作方式相同,只是当覆盖上下文变为当前时将调用这些方法可以使用makeOverlayCurrent()显式地使覆盖上下文成为当前上下文,并且可以通过调用overlayContext()直接访问覆盖上下文(例如,请求其透明颜色)。

在默认视觉效果位于覆盖平面的X服务器上,非GL Qt窗口也可用于覆盖。

绘制技术

如上所述,子类QGLWidget以下方式呈现纯3D内容:

  • 重新实现QGLWidget::initializeGL()QGLWidget::resizeGL(),以设置OpenGL状态并提供从始至终的转换。
  • 重新实现QGLWidget::paintGL()以绘制3D场景,只调用OpenGL函数在小部件上绘制。

若要在QGLWidget子类上绘制二维图形,需要重新实现QGLWidget:: paintEvent()并执行以下操作:

  • 构造QPainter对象。
  • 初始化它以在带有QPainer::begin()函数的小部件上使用。
  • 使用QPainter的成员函数绘制基元。
  • 调用QPainter::end()以完成绘制。

线程

从Qt4.8版开始,对线程化GL渲染的支持已经得到了改进。目前支持三种方案:

方案一:在线程中进行缓冲区交换。

在双缓冲上下文中交换缓冲区可能是一个同步的锁定调用,在某些GL实现中这可能是一个代价高昂的操作。尤其是嵌入式设备。在GPU进行缓冲区交换时让CPU空转并不是最佳选择。在这些情况下,可以在主线程中进行渲染,并在单独的线程中进行实际的缓冲区交换。这可以通过以下步骤完成:

  • 步骤一:渲染完成后,在主线程中调用DoneCurrent()。
  • 步骤二:调用QGLContext::moveToThread(swapThread)将上下文的所有权转移到交换线程。
  • 步骤三:通知交换线程它可以获取上下文。
  • 步骤四:使用makeCurrent()使交换线程中的呈现上下文成为当前上下文,然后调用swapBuffers()。
  • 步骤五:在交换线程中调用DoneCurrent()。
  • 步骤六:调用QGLContext::moveToThread(qApp->thread())并通知主线程交换已完成。

这样做将释放主线程,以便它可以继续处理UI事件或网络请求。即使涉及到上下文交换,也比在GPU完成交换操作时让主线程等待要好。请注意,这是高度依赖于实现的。

方案二:在线程中上载纹理。

在线程中进行纹理上载对于处理需要显示的大量图像的应用程序(例如照片库应用程序)可能非常有用。这在qt中通过现有的bindTexture() API得到支持。一个简单的方法是创建两个共享的QGLWidget。一个在主GUI线程中是当前的,另一个在纹理上载线程中是当前的。上载线程中的小部件从未显示,它仅用于与主线程共享纹理。对于通过bindTexture()绑定的每个纹理,通知主线程以便它可以开始使用该纹理。

方案三:使用QPainer在线程中绘制到QGLWidget中。

在Qt4.8中,可以使用单独线程中的QPainer绘制到QGLWidget中。请注意,这对于QGLPIxelBuffers和QGLframeBufferObjects也是可能的。由于这仅在GL2绘制引擎中受支持,因此需要OpenGL 2.0或OpenGL ES 2.0。

QGLwidgets只能在主GUI线程中创建。这意味着需要调用doneCurrent()才能从主线程释放GL上下文,然后其他线程才能将小部件拉入其中。然后,需要调用QGLContext::moveToThread()将上下文的所有权转移到要使其成为当前线程的线程。此外,当小部件被调整大小时,或者小部件的一部分暴露出来或者需要重新绘制时,主GUI线程将把调整大小和绘制事件分派给QGLWidget。

因此,有必要处理这些事件,因为QGLWidget中的默认实现将尝试使QGLWidget的上下文成为当前上下文,这将再次干扰呈现到小部件中的任何线程。重新实现QGLWidget::paintEvent()和QGLWidget::resizeEvent(),通知渲染线程需要调整大小或更新,注意不要调用基类实现。如果要渲染动画,则可能根本不需要处理绘制事件,因为渲染线程正在进行定期更新。然后,只需重新实现QGLWidget::paintEvent()就可以了。

在进行线程渲染时,一般规则是:请注意,不同线程中的绑定和释放上下文必须由用户同步。GL呈现上下文在任何时候都只能在一个线程中是最新的。如果您试图在QGLwidget上打开一个QPainer,而该Widget的呈现上下文在另一个线程中是最新的,那么它将失败。

除此之外,还支持在单独的线程中使用原始GL调用进行渲染。

QOpenGLWidget

概述

QOpenGLWidget类是用于呈现OpenGL图形的小部件。

QOpenGLWidget提供显示集成到Qt应用程序中的OpenGL图形的功能。使用起来非常简单:让类继承它,并像其他QWidget一样使用子类,额外可以选择使用QPainer和标准的OpenGL渲染命令。

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

  • paintGL():渲染OpenGL场景。每当需要更新小部件时调用。
  • resizeGL ():设置OpenGL视区、投影等。每当小部件调整了大小时都会调用该视区(并且当它第一次显示时也会调用,因为所有新创建的小部件都会自动获得一个调整大小的事件)。
  • initializeGL():设置OpenGL呈现上下文,定义显示列表等。在第一次调用resizeGL ()或paintGL ()之前调用一次。

更新绘制

如果需要从paintGL()以外的地方触发重新绘制(典型的例子是使用计时器来动画场景),您应该调用小部件的update()函数来安排更新。

当调用paintGL()、resizeGL()或initializeGL()时,小部件的OpenGL呈现上下文成为当前上下文。如果需要从其他地方调用标准的OpenGL API函数(例如,在小部件的构造函数或自己的paint函数中),则必须首先调用makeCurrent()。

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

当平台支持时,QOpenGLWidget允许使用不同的OpenGL版本和配置文件。只需通过setFormat()设置请求的格式。但是请记住,在同一窗口中拥有多个QOpenGLWidget实例需要它们都使用相同的格式,或者至少使用不使上下文不可共享的格式。要解决此问题,最好使用QSurfaceFormat::setDefaultFormat()而不是setFormat()。

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

绘制技术

子类QOpenGLWidget以以下方式呈现纯3D内容:

  • 重新实现QGLWidget::initializeGL()QGLWidget::resizeGL(),以设置OpenGL状态并提供从始至终的转换。
  • 重新实现QGLWidget::paintGL()以绘制3D场景,只调用OpenGL函数在小部件上绘制。

若要在QGLWidget子类上绘制二维图形,需要重新实现QGLWidget:: paintEvent()并执行以下操作:

  • 在paintGL()中,不要发出OpenGL命令,而是构造一个用于小部件的QPainter对象。
  • 使用QPainter的成员函数绘制基元。
  • 仍然可以发出直接的OpenGL命令。但是,必须确保通过调用画师的beginNativePainting()endNativePainting()将这些内容括起来。

仅使用QPainter执行绘图时,也可以像对普通小部件执行一样执行绘图:通过重新实现paintEvent()

  • 重新实现paintEvent()函数。
  • 构造针对小部件的QPainer对象。将小部件传递给构造函数或QPainter::begin()函数。
  • 使用QPainter的成员函数绘制基元。
  • 绘制完成后,销毁QPainter实例,或者,显式调用QPainter::end()

调用OpenGL头文件和函数

在进行OpenGL函数调用时,强烈建议避免直接调用函数。相反,在面向现代、仅桌面的OpenGL时,更喜欢使用QOpenglFunctions(在制作可移植应用程序时)或版本化的变体(例如,QopenglFunctions_3_2_Core和类似的变体)。这样,应用程序将在所有qt构建配置中正常工作,包括执行动态OpenGL实现加载的配置,这意味着应用程序没有直接链接到GL实现,因此直接函数调用是不可行的。

在paintGL()中,始终可以通过调用QOpenGLContext::currentContext()访问当前上下文。通过调用QOpenGLContex::Functions(),可以从此上下文中检索已初始化、准备好使用的QOpenGLFunctions实例。为每个GL调用添加前缀的另一种方法是从QOpenGLFunctions继承并在InitializeGL()中调用QOpenGLFunctions::nitializeOpenGLFunctions()。

至于OpenGL头文件,请注意,在大多数情况下,不需要直接包含任何头文件,如gl.h。OpenGL相关的qt报头将包括qopengl.h,而qopengl.h又将包含适合系统的报头。这可能是OpenGL ES 3.x或2.0头文件,可用的最高版本,或者是系统提供的gl.h。此外,还为OpenGL和OpenGL ES提供了扩展头文件(在某些系统上称为glext.h)的副本,作为qt的一部分。在可行的情况下,这些将自动包含在平台上。这意味着来自arb、ext、oes扩展的常量和函数指针typedef将自动可用。

代码示例

最简单的QOpenGLWidget子类可以如下所示:

文章转自博客园(莫水千流):Qt开发笔迹:QGLWidget、QOpenGLWidget详解及区别 - 莫水千流 - 博客园

本文福利,莬费领取Qt开发学习资料包、技术视频,内容包括(C++语言基础,Qt编程入门,QT信号与槽机制,QT界面开发-图像绘制,QT网络,QT数据库编程,QT项目实战,QSS,OpenCV,Quick模块,面试题等等)↓↓↓↓↓↓见下面↓↓文章底部点击莬费领取↓↓

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 雷达扫描图可以通过使用QOpenGLWidgetQGLWidget来实现。这两个类都是Qt中用于OpenGL渲染的窗口部件,可以用于绘制雷达扫描图等图形。 首先,我们需要重写QOpenGLWidgetQGLWidget类的绘制函数来绘制雷达扫描图。通过设置OpenGL视口和投影矩阵,我们可以定义一个2D或3D场景,用于绘制雷达扫描效果。 在绘制函数中,我们可以使用OpenGL的绘制函数来绘制雷达扫描图形。例如,我们可以使用圆形绘图函数来绘制一个雷达扫描范围,然后使用线段绘制函数来绘制扫描线,以模拟雷达的运动。还可以使用颜色设置函数来设置扫描线和扫描图形的颜色。 除了绘制函数,我们还可以在QOpenGLWidgetQGLWidget中使用其他函数来处理雷达扫描图的交互。例如,可以使用鼠标事件来控制雷达的旋转或缩放,还可以使用定时器来实现扫描线的动画效果。 最后,在主程序中,我们可以创建一个QOpenGLWidgetQGLWidget对象,并将其添加到Qt应用程序的界面中。通过设置正确的窗口大小和OpenGL版本,我们可以在应用程序中显示雷达扫描图,并使用其他Qt部件来调整雷达扫描效果。 总而言之,通过使用QOpenGLWidgetQGLWidget类,结合OpenGL绘图函数和Qt的其他功能,我们可以创建出具有雷达扫描效果的图形界面。 ### 回答2: QOpenGLWidgetQGLWidget 都是Qt中用于显示OpenGL图形的窗口控件类。它们都可以用来实现雷达扫描图。雷达扫描图通常是通过将一个或多个旋转的扇形线段渲染到窗口上实现的。 使用这两个类中的任何一个来创建雷达扫描图,首先需要重写其绘制函数,并在该函数中使用OpenGL进行绘制操作。在绘制函数中,可以使用OpenGL提供的旋转、平移和缩放等变换函数来实现雷达扫描图的移动和缩放效果。 具体绘制雷达扫描图的步骤如下: 1. 创建一个QOpenGLWidgetQGLWidget类的子类,并重写其绘制函数(paintGL函数); 2. 在绘制函数中,使用OpenGL函数来设置绘制参数,如绘制颜色、线宽等; 3. 使用OpenGL函数来进行坐标变换,如平移、旋转、缩放等,以绘制旋转的扇形线段; 4. 使用OpenGL函数来绘制扇形线段,可以使用GL_LINES或GL_LINE_STRIP等模式来绘制; 5. 调用update或者glSwapBuffers函数刷新窗口,以显示绘制结果。 同时,为了实现雷达扫描图的动态效果,可以使用定时器来触发雷达扫描图的更新。在定时器回调函数中,可以更新雷达扫描图的角度参数,并调用绘制函数来重新绘制扇形线段。 总结起来,无论是使用QOpenGLWidget还是QGLWidget,都可以通过重写绘制函数,使用OpenGL的绘制函数和变换函数来实现雷达扫描图的效果,同时结合定时器来实现动态效果。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值