QOpenGLWidget简介
QOpenGLWidget类是用于渲染OpenGL图形的部件。QOpenGLWidget提供了用于显示集成到Qt应用程序中的OpenGL图形的功能。使用非常简单:让应用的类继承自它,并像使用其他任何QWidget一样使用子类,而且它可以在使用QPainter和标准OpenGL渲染命令之间进行选择。
QOpenGLWidget提供了三个函数,在子类中重新实现它们就可以执行一个典型的OpenGL任务:
- paintGL() 渲染OpenGL场景。在部件需要更新时就会调用它。
- resizeGL() 设置OpenGL视口,投影等。只要调整了窗口部件的大小(以及新创建的窗口部件自动获得resize事件而首次显示时),就会被调用。
- initializeGL() 设置OpenGL资源和状态。在第一次调用resizeGL()或paintGL()之前它被调用一次。
QOpenGLWidget的子类可以通过以下方式渲染纯3D内容:
- 重新实现initializeGL()和resizeGL()函数以设置OpenGL状态并提供透视图转换。
- 重新实现paintGL()来绘制3D场景,它仅调用OpenGL函数。
仅使用QPainter执行绘图时,还可以像执行普通部件一样执行绘图:
- 重新实现paintEven()t函数。
- 构造一个以部件为目标的QPainter对象。将小部件传递给构造函数或QPainter.begin()函数。
- 使用QPainter的成员函数绘制基本图元。
- 绘制完成后,然后销毁QPainter实例。或者显式调用QPainter.end()。
QOpenGLWidget常用方法:
- context(): 返回控件所使用的QOpenGLContext, 如果未初始化,则返回None。
- defaultFramebufferObject():返回帧缓冲区句柄,如果未初始化,则返回None。
- doneCurrent(): 释放Context。
- format(): 返回本控件及其顶层窗口使用的Context和Surface格式。
- grabFramebuffer(): 渲染并返回帧缓冲区的32位RGB图像。
- makeCurrent(): 通过使相应的context成为当前context并在其上绑定framebuffer对象,为渲染此控件的OpenGL内容做准备。
- setFormat(): 设置所需的表面(Surface)格式。
- setTextureFormat(): 设置自定义内部纹理格式。
- textureFormat(): 返回内部纹理格式。
QOpenGLWidget常用信号:
- aboutToCompose(): 当控件的顶级窗口开始合成其QOpenGLWidget子级和其他控件的纹理时,将发出此信号。
- aboutToResize(): 当窗口部件的大小发生更改时,将发出此信号,因此将重新创建帧缓冲区对象。
- frameSwapped(): 在窗口部件的顶级窗口完成合成后发出此信号。
- resized(): 调整窗口部件的大小而重新创建了帧缓冲对象之后立即发出此信号。
QOpenGLWidget类继承关系:
范例代码
最简单的QOpenGLWidget子类可能如下所示:
class MyGLWidget(QOpenGLWidget):
def __init(self, parent=None):
super(MyGLWidget, self).__init__(parent)
def initializeGL(self):
# 设置渲染context,加载shader和资源等
version_profile = QOpenGLVersionProfile()
version_profile.setVersion(2,0)
self.gl = self.context().versionFunctions(version_profile)
self.gl.initializeOpenGLFunctions()
...
def resizeGL(self, w, h):
# 更新投射矩阵和其他尺寸相关的设置
self.projection.setToIdentity()
self.projection.perspective(45, w / h, 0.01, 100)
...
def paintGL():
#绘制场景
self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT)
在演示代码中,使用OpenGL渲染了一个三个顶点带不同颜色的三角形,完整代码如下:
import sys
from PyQt5 import QtCore, QtGui, QtWidgets
from PyQt5.QtCore import Qt
from PyQt5.QtGui import QColor, QOpenGLVersionProfile
from PyQt5.QtWidgets import (QApplication, QWidget, QOpenGLWidget, QHBoxLayout)
class MyGLWidget(QOpenGLWidget):
def __init__(self, parent=None):
super(MyGLWidget, self).__init__(parent)
def initializeGL(self):
version_profile = QOpenGLVersionProfile()
version_profile.setVersion(2, 0)
self.gl = self.context().versionFunctions(version_profile)
self.gl.initializeOpenGLFunctions()
#设置背景色
self.gl.glClearColor(0.2, 0.4, 0.52, 1.0)
#深度测试
self.gl.glEnable(self.gl.GL_DEPTH_TEST)
def paintGL(self):
self.gl.glClear(self.gl.GL_COLOR_BUFFER_BIT | self.gl.GL_DEPTH_BUFFER_BIT)
self.gl.glLoadIdentity()
#self.gl.glRotated(30.0, 1.0, 0.0, 0.0)
self.gl.glBegin(self.gl.GL_TRIANGLES)
self.gl.glColor3d(1.0, 0.0, 0.0)
self.gl.glVertex3d(0.0, 1.0, 0.0)
self.gl.glColor3d(0.0, 1.0, 0.0)
self.gl.glVertex3d(-1.0, -1.0, 0.0)
self.gl.glColor3d(0.0, 0.0, 1.0)
self.gl.glVertex3d(1.0, -1.0, 0.0)
self.gl.glEnd()
def resizeGL(self, width, height):
side = min(width, height)
if side < 0:
return
#视口
self.gl.glViewport((width - side) // 2, (height - side) // 2, side, side)
self.gl.glMatrixMode(self.gl.GL_PROJECTION)
self.gl.glLoadIdentity()
#正交投射
self.gl.glOrtho(-1.5, 1.5, -1.5, 1.5, -10, 10)
self.gl.glMatrixMode(self.gl.GL_MODELVIEW)
class DemoOpenGLWidget(QWidget):
def __init__(self, parent=None):
super(DemoOpenGLWidget, self).__init__(parent)
# 设置窗口标题
self.setWindowTitle('实战PyQt5: QOpenGLWidget Demo!')
# 设置窗口大小
self.resize(400, 320)
self.initUi()
def initUi(self):
glWidget = MyGLWidget()
mainLayout = QHBoxLayout()
mainLayout.addWidget(glWidget)
self.setLayout(mainLayout)
if __name__ == '__main__':
app = QApplication(sys.argv)
window = DemoOpenGLWidget()
window.show()
sys.exit(app.exec())
运行结果如下图:
测试QOpenGLWidget
本文知识点
- 如何实现一个OpenGL渲染任务;
- OpenGL的状态机制;
- OpenGL 渲染一个三角形。