1 2############################################################################# 3## 4## Copyright (C) 2017 The Qt Company Ltd. 5## Contact: http://www.qt.io/licensing/ 6## 7## This file is part of the Qt for Python examples of the Qt Toolkit. 8## 9## $QT_BEGIN_LICENSE:BSD$ 10## You may use this file under the terms of the BSD license as follows: 11## 12## "Redistribution and use in source and binary forms, with or without 13## modification, are permitted provided that the following conditions are 14## met: 15## * Redistributions of source code must retain the above copyright 16## notice, this list of conditions and the following disclaimer. 17## * Redistributions in binary form must reproduce the above copyright 18## notice, this list of conditions and the following disclaimer in 19## the documentation and/or other materials provided with the 20## distribution. 21## * Neither the name of The Qt Company Ltd nor the names of its 22## contributors may be used to endorse or promote products derived 23## from this software without specific prior written permission. 24## 25## 26## THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 27## "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 28## LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 29## A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 30## OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 31## SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 32## LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 33## DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 34## THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 35## (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 36## OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE." 37## 38## $QT_END_LICENSE$ 39## 40############################################################################# 41 42"""PySide2 port of the opengl/contextinfo example from Qt v5.x""" 43 44from argparse import ArgumentParser, RawTextHelpFormatter 45import numpy 46import sys 47from textwrap import dedent 48 49 50from PySide2.QtCore import QCoreApplication, QLibraryInfo, QSize, QTimer, Qt 51from PySide2.QtGui import (QMatrix4x4, QOpenGLBuffer, QOpenGLContext, QOpenGLShader, 52 QOpenGLShaderProgram, QOpenGLVertexArrayObject, QSurfaceFormat, QWindow) 53from PySide2.QtWidgets import (QApplication, QHBoxLayout, QMessageBox, QPlainTextEdit, 54 QWidget) 55from PySide2.support import VoidPtr 56try: 57 from OpenGL import GL 58except ImportError: 59 app = QApplication(sys.argv) 60 messageBox = QMessageBox(QMessageBox.Critical, "ContextInfo", 61 "PyOpenGL must be installed to run this example.", 62 QMessageBox.Close) 63 messageBox.setDetailedText("Run:\npip install PyOpenGL PyOpenGL_accelerate") 64 messageBox.exec_() 65 sys.exit(1) 66 67vertexShaderSource110 = dedent(""" 68 // version 110 69 attribute highp vec4 posAttr; 70 attribute lowp vec4 colAttr; 71 varying lowp vec4 col; 72 uniform highp mat4 matrix; 73 void main() { 74 col = colAttr; 75 gl_Position = matrix * posAttr; 76 } 77 """) 78 79fragmentShaderSource110 = dedent(""" 80 // version 110 81 varying lowp vec4 col; 82 void main() { 83 gl_FragColor = col; 84 } 85 """) 86 87vertexShaderSource = dedent(""" 88 // version 150 89 in vec4 posAttr; 90 in vec4 colAttr; 91 out vec4 col; 92 uniform mat4 matrix; 93 void main() { 94 col = colAttr; 95 gl_Position = matrix * posAttr; 96 } 97 """) 98 99fragmentShaderSource = dedent(""" 100 // version 150 101 in vec4 col; 102 out vec4 fragColor; 103 void main() { 104 fragColor = col; 105 } 106 """) 107 108vertices = numpy.array([0, 0.707, -0.5, -0.5, 0.5, -0.5], dtype = numpy.float32) 109colors = numpy.array([1, 0, 0, 0, 1, 0, 0, 0, 1], dtype = numpy.float32) 110 111 112def print_surface_format(surface_format): 113 profile_name = 'core' if surface_format.profile() == QSurfaceFormat.CoreProfile else 'compatibility' 114 return "{} version {}.{}".format(profile_name, 115 surface_format.majorVersion(), surface_format.minorVersion()) 116 117class RenderWindow(QWindow): 118 def __init__(self, format): 119 super(RenderWindow, self).__init__() 120 self.setSurfaceType(QWindow.OpenGLSurface) 121 self.setFormat(format) 122 self.context = QOpenGLContext(self) 123 self.context.setFormat(self.requestedFormat()) 124 if not self.context.create(): 125 raise Exception("Unable to create GL context") 126 self.program = None 127 self.timer = None 128 self.angle = 0 129 130 def initGl(self): 131 self.program = QOpenGLShaderProgram(self) 132 self.vao = QOpenGLVertexArrayObject() 133 self.vbo = QOpenGLBuffer() 134 135 format = self.context.format() 136 useNewStyleShader = format.profile() == QSurfaceFormat.CoreProfile 137 # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile 138 # concept 3.2+ has. This may still fail since version 150 (3.2) is 139 # specified in the sources but it's worth a try. 140 if (format.renderableType() == QSurfaceFormat.OpenGL and format.majorVersion() == 3 141 and format.minorVersion() <= 1): 142 useNewStyleShader = not format.testOption(QSurfaceFormat.DeprecatedFunctions) 143 144 vertexShader = vertexShaderSource if useNewStyleShader else vertexShaderSource110 145 fragmentShader = fragmentShaderSource if useNewStyleShader else fragmentShaderSource110 146 if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertexShader): 147 raise Exception("Vertex shader could not be added: {} ({})".format(self.program.log(), vertexShader)) 148 if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragmentShader): 149 raise Exception("Fragment shader could not be added: {} ({})".format(self.program.log(), fragmentShader)) 150 if not self.program.link(): 151 raise Exception("Could not link shaders: {}".format(self.program.log())) 152 153 self.posAttr = self.program.attributeLocation("posAttr") 154 self.colAttr = self.program.attributeLocation("colAttr") 155 self.matrixUniform = self.program.uniformLocation("matrix") 156 157 self.vbo.create() 158 self.vbo.bind() 159 self.verticesData = vertices.tobytes() 160 self.colorsData = colors.tobytes() 161 verticesSize = 4 * vertices.size 162 colorsSize = 4 * colors.size 163 self.vbo.allocate(VoidPtr(self.verticesData), verticesSize + colorsSize) 164 self.vbo.write(verticesSize, VoidPtr(self.colorsData), colorsSize) 165 self.vbo.release() 166 167 vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) 168 if self.vao.isCreated(): # have VAO support, use it 169 self.setupVertexAttribs() 170 171 def setupVertexAttribs(self): 172 self.vbo.bind() 173 self.program.setAttributeBuffer(self.posAttr, GL.GL_FLOAT, 0, 2) 174 self.program.setAttributeBuffer(self.colAttr, GL.GL_FLOAT, 4 * vertices.size, 3) 175 self.program.enableAttributeArray(self.posAttr) 176 self.program.enableAttributeArray(self.colAttr) 177 self.vbo.release() 178 179 def exposeEvent(self, event): 180 if self.isExposed(): 181 self.render() 182 if self.timer is None: 183 self.timer = QTimer(self) 184 self.timer.timeout.connect(self.slotTimer) 185 if not self.timer.isActive(): 186 self.timer.start(10) 187 else: 188 if self.timer and self.timer.isActive(): 189 self.timer.stop() 190 191 def render(self): 192 if not self.context.makeCurrent(self): 193 raise Exception("makeCurrent() failed") 194 functions = self.context.functions() 195 if self.program is None: 196 functions.glEnable(GL.GL_DEPTH_TEST) 197 functions.glClearColor(0, 0, 0, 1) 198 self.initGl() 199 200 retinaScale = self.devicePixelRatio() 201 functions.glViewport(0, 0, self.width() * retinaScale, 202 self.height() * retinaScale) 203 functions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) 204 205 self.program.bind() 206 matrix = QMatrix4x4() 207 matrix.perspective(60, 4 / 3, 0.1, 100) 208 matrix.translate(0, 0, -2) 209 matrix.rotate(self.angle, 0, 1, 0) 210 self.program.setUniformValue(self.matrixUniform, matrix) 211 212 if self.vao.isCreated(): 213 self.vao.bind() 214 else: # no VAO support, set the vertex attribute arrays now 215 self.setupVertexAttribs() 216 217 functions.glDrawArrays(GL.GL_TRIANGLES, 0, 3) 218 219 self.vao.release() 220 self.program.release() 221 222 # swapInterval is 1 by default which means that swapBuffers() will (hopefully) block 223 # and wait for vsync. 224 self.context.swapBuffers(self) 225 self.context.doneCurrent() 226 227 def slotTimer(self): 228 self.render() 229 self.angle += 1 230 231 def glInfo(self): 232 if not self.context.makeCurrent(self): 233 raise Exception("makeCurrent() failed") 234 functions = self.context.functions() 235 text = """Vendor: {}\nRenderer: {}\nVersion: {}\nShading language: {} 236\nContext Format: {}\n\nSurface Format: {}""".format( 237 functions.glGetString(GL.GL_VENDOR), functions.glGetString(GL.GL_RENDERER), 238 functions.glGetString(GL.GL_VERSION), 239 functions.glGetString(GL.GL_SHADING_LANGUAGE_VERSION), 240 print_surface_format(self.context.format()), 241 print_surface_format(self.format())) 242 self.context.doneCurrent() 243 return text 244 245class MainWindow(QWidget): 246 def __init__(self): 247 super(MainWindow, self).__init__() 248 hBoxLayout = QHBoxLayout(self) 249 self.plainTextEdit = QPlainTextEdit() 250 self.plainTextEdit.setMinimumWidth(400) 251 self.plainTextEdit.setReadOnly(True) 252 hBoxLayout.addWidget(self.plainTextEdit) 253 self.renderWindow = RenderWindow(QSurfaceFormat()) 254 container = QWidget.createWindowContainer(self.renderWindow) 255 container.setMinimumSize(QSize(400, 400)) 256 hBoxLayout.addWidget(container) 257 258 def updateDescription(self): 259 text = "{}\n\nPython {}\n\n{}".format(QLibraryInfo.build(), sys.version, 260 self.renderWindow.glInfo()) 261 self.plainTextEdit.setPlainText(text) 262 263if __name__ == '__main__': 264 parser = ArgumentParser(description="contextinfo", formatter_class=RawTextHelpFormatter) 265 parser.add_argument('--gles', '-g', action='store_true', 266 help='Use OpenGL ES') 267 parser.add_argument('--software', '-s', action='store_true', 268 help='Use Software OpenGL') 269 parser.add_argument('--desktop', '-d', action='store_true', 270 help='Use Desktop OpenGL') 271 options = parser.parse_args() 272 if options.gles: 273 QCoreApplication.setAttribute(Qt.AA_UseOpenGLES) 274 elif options.software: 275 QCoreApplication.setAttribute(Qt.AA_UseSoftwareOpenGL) 276 elif options.desktop: 277 QCoreApplication.setAttribute(Qt.AA_UseDesktopOpenGL) 278 279 app = QApplication(sys.argv) 280 mainWindow = MainWindow() 281 mainWindow.show() 282 mainWindow.updateDescription() 283 sys.exit(app.exec_()) 284