def __init__(self, id: str, app: QGuiApplication): assert self._initialized self.id = id self.app = app self.qml_view = QUrl(os.fspath(get_data_path("webview.qml"))) gl_context = QOpenGLContext() gl_context.setFormat(QSurfaceFormat.defaultFormat()) gl_context.create() self.client = Client(id, self.qml_view, gl_context) self.client.disconnected.connect(self.on_disconnected) self.client.start()
class RenderWindow(QWindow): def __init__(self, format): super(RenderWindow, self).__init__() self.setSurfaceType(QWindow.OpenGLSurface) self.setFormat(format) self.context = QOpenGLContext(self) self.context.setFormat(self.requestedFormat()) if not self.context.create(): raise Exception("Unable to create GL context") self.program = None self.timer = None self.angle = 0 def initGl(self): self.program = QOpenGLShaderProgram(self) self.vao = QOpenGLVertexArrayObject() self.vbo = QOpenGLBuffer() format = self.context.format() useNewStyleShader = format.profile() == QSurfaceFormat.CoreProfile # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile # concept 3.2+ has. This may still fail since version 150 (3.2) is # specified in the sources but it's worth a try. if (format.renderableType() == QSurfaceFormat.OpenGL and format.majorVersion() == 3 and format.minorVersion() <= 1): useNewStyleShader = not format.testOption(QSurfaceFormat.DeprecatedFunctions) vertexShader = vertexShaderSource if useNewStyleShader else vertexShaderSource110 fragmentShader = fragmentShaderSource if useNewStyleShader else fragmentShaderSource110 if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertexShader): raise Exception("Vertex shader could not be added: {} ({})".format(self.program.log(), vertexShader)) if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragmentShader): raise Exception("Fragment shader could not be added: {} ({})".format(self.program.log(), fragmentShader)) if not self.program.link(): raise Exception("Could not link shaders: {}".format(self.program.log())) self.posAttr = self.program.attributeLocation("posAttr") self.colAttr = self.program.attributeLocation("colAttr") self.matrixUniform = self.program.uniformLocation("matrix") self.vbo.create() self.vbo.bind() self.verticesData = vertices.tobytes() self.colorsData = colors.tobytes() verticesSize = 4 * vertices.size colorsSize = 4 * colors.size self.vbo.allocate(VoidPtr(self.verticesData), verticesSize + colorsSize) self.vbo.write(verticesSize, VoidPtr(self.colorsData), colorsSize) self.vbo.release() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) if self.vao.isCreated(): # have VAO support, use it self.setupVertexAttribs() def setupVertexAttribs(self): self.vbo.bind() self.program.setAttributeBuffer(self.posAttr, GL.GL_FLOAT, 0, 2) self.program.setAttributeBuffer(self.colAttr, GL.GL_FLOAT, 4 * vertices.size, 3) self.program.enableAttributeArray(self.posAttr) self.program.enableAttributeArray(self.colAttr) self.vbo.release() def exposeEvent(self, event): if self.isExposed(): self.render() if self.timer is None: self.timer = QTimer(self) self.timer.timeout.connect(self.slotTimer) if not self.timer.isActive(): self.timer.start(10) else: if self.timer and self.timer.isActive(): self.timer.stop() def render(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() if self.program is None: functions.glEnable(GL.GL_DEPTH_TEST) functions.glClearColor(0, 0, 0, 1) self.initGl() retinaScale = self.devicePixelRatio() functions.glViewport(0, 0, self.width() * retinaScale, self.height() * retinaScale) functions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.program.bind() matrix = QMatrix4x4() matrix.perspective(60, 4 / 3, 0.1, 100) matrix.translate(0, 0, -2) matrix.rotate(self.angle, 0, 1, 0) self.program.setUniformValue(self.matrixUniform, matrix) if self.vao.isCreated(): self.vao.bind() else: # no VAO support, set the vertex attribute arrays now self.setupVertexAttribs() functions.glDrawArrays(GL.GL_TRIANGLES, 0, 3) self.vao.release() self.program.release() # swapInterval is 1 by default which means that swapBuffers() will (hopefully) block # and wait for vsync. self.context.swapBuffers(self) self.context.doneCurrent() def slotTimer(self): self.render() self.angle += 1 def glInfo(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() text = """Vendor: {}\nRenderer: {}\nVersion: {}\nShading language: {} \nContext Format: {}\n\nSurface Format: {}""".format( functions.glGetString(GL.GL_VENDOR), functions.glGetString(GL.GL_RENDERER), functions.glGetString(GL.GL_VERSION), functions.glGetString(GL.GL_SHADING_LANGUAGE_VERSION), print_surface_format(self.context.format()), print_surface_format(self.format())) self.context.doneCurrent() return text
class RenderWindow(QWindow): def __init__(self, format): super(RenderWindow, self).__init__() self.setSurfaceType(QWindow.OpenGLSurface) self.setFormat(format) self.context = QOpenGLContext(self) self.context.setFormat(self.requestedFormat()) if not self.context.create(): raise Exception("Unable to create GL context") self.program = None self.timer = None self.angle = 0 def initGl(self): self.program = QOpenGLShaderProgram(self) self.vao = QOpenGLVertexArrayObject() self.vbo = QOpenGLBuffer() format = self.context.format() useNewStyleShader = format.profile() == QSurfaceFormat.CoreProfile # Try to handle 3.0 & 3.1 that do not have the core/compatibility profile # concept 3.2+ has. This may still fail since version 150 (3.2) is # specified in the sources but it's worth a try. if (format.renderableType() == QSurfaceFormat.OpenGL and format.majorVersion() == 3 and format.minorVersion() <= 1): useNewStyleShader = not format.testOption(QSurfaceFormat.DeprecatedFunctions) vertexShader = vertexShaderSource if useNewStyleShader else vertexShaderSource110 fragmentShader = fragmentShaderSource if useNewStyleShader else fragmentShaderSource110 if not self.program.addShaderFromSourceCode(QOpenGLShader.Vertex, vertexShader): raise Exception("Vertex shader could not be added: {} ({})".format(self.program.log(), vertexShader)) if not self.program.addShaderFromSourceCode(QOpenGLShader.Fragment, fragmentShader): raise Exception("Fragment shader could not be added: {} ({})".format(self.program.log(), fragmentShader)) if not self.program.link(): raise Exception("Could not link shaders: {}".format(self.program.log())) self.posAttr = self.program.attributeLocation("posAttr") self.colAttr = self.program.attributeLocation("colAttr") self.matrixUniform = self.program.uniformLocation("matrix") self.vbo.create() self.vbo.bind() self.verticesData = vertices.tobytes() self.colorsData = colors.tobytes() verticesSize = 4 * vertices.size colorsSize = 4 * colors.size self.vbo.allocate(VoidPtr(self.verticesData), verticesSize + colorsSize) self.vbo.write(verticesSize, VoidPtr(self.colorsData), colorsSize) self.vbo.release() vaoBinder = QOpenGLVertexArrayObject.Binder(self.vao) if self.vao.isCreated(): # have VAO support, use it self.setupVertexAttribs() def setupVertexAttribs(self): self.vbo.bind() self.program.setAttributeBuffer(self.posAttr, GL.GL_FLOAT, 0, 2) self.program.setAttributeBuffer(self.colAttr, GL.GL_FLOAT, 4 * vertices.size, 3) self.program.enableAttributeArray(self.posAttr) self.program.enableAttributeArray(self.colAttr) self.vbo.release() def exposeEvent(self, event): if self.isExposed(): self.render() if self.timer is None: self.timer = QTimer(self) self.timer.timeout.connect(self.slotTimer) self.timer.start(10) def render(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() if self.program is None: functions.glEnable(GL.GL_DEPTH_TEST) functions.glClearColor(0, 0, 0, 1) self.initGl() functions.glViewport(0, 0, self.width(), self.height()) functions.glClear(GL.GL_COLOR_BUFFER_BIT | GL.GL_DEPTH_BUFFER_BIT) self.program.bind() matrix = QMatrix4x4() matrix.perspective(60, 4 / 3, 0.1, 100) matrix.translate(0, 0, -2) matrix.rotate(self.angle, 0, 1, 0) self.program.setUniformValue(self.matrixUniform, matrix) if self.vao.isCreated(): self.vao.bind() else: # no VAO support, set the vertex attribute arrays now self.setupVertexAttribs() functions.glDrawArrays(GL.GL_TRIANGLES, 0, 3) self.vao.release() self.program.release() # swapInterval is 1 by default which means that swapBuffers() will (hopefully) block # and wait for vsync. self.context.swapBuffers(self) self.context.doneCurrent() def slotTimer(self): self.render() self.angle += 1 def glInfo(self): if not self.context.makeCurrent(self): raise Exception("makeCurrent() failed") functions = self.context.functions() text = "Vendor: {}\nRenderer: {}\nVersion: {}\nShading language: {}".format( functions.glGetString(GL.GL_VENDOR), functions.glGetString(GL.GL_RENDERER), functions.glGetString(GL.GL_VERSION), functions.glGetString(GL.GL_SHADING_LANGUAGE_VERSION)) self.context.doneCurrent() return text
def initialize(self, size: QSize, shareContext: QOpenGLContext) -> None: """ Initialize offscreen renderer. Args: size: The size of the area available for rendering. shareContext: OpenGL context used as a share context. Raises: RuntimeError: If the renderer has already been initialized. """ print(f'QmlOffscreenRenderer.initialize: {size}') if self.initialized: raise RuntimeError('Already initialized') context = QOpenGLContext() context.setShareContext(shareContext) context.setFormat(get_default_format(gles=False)) context.create() assert not context.isOpenGLES(), "We need glGetTexImage from OpenGL" self.size = size # Create offscreen surface with initialized format surface = QOffscreenSurface() surface.setFormat(context.format()) surface.create() self.ctx = RenderContext(context, surface) # Set up quick rendering self._control = control = QQuickRenderControl() self._window = window = QQuickWindow(control) self._cursor = self._window.cursor() # Don't polish/sync/render immediately for better performance, use a timer self._renderTimer = renderTimer = QTimer() renderTimer.setSingleShot(True) renderTimer.setInterval(5) renderTimer.timeout.connect(self._onRenderTimer) self._syncTimer = syncTimer = QTimer() syncTimer.setSingleShot(True) syncTimer.setInterval(5) syncTimer.timeout.connect(self._onSyncTimer) syncTimer.destroyed.connect(self._onSyncTimerDestroyed) # Request to create frame buffer window.sceneGraphInitialized.connect(self._onSceneGraphInitialized) # Request to release frame buffer window.sceneGraphInvalidated.connect(self._onSceneGraphInvalidated) # Only render is needed control.renderRequested.connect(self._onRenderRequested) # Polish, sync & render control.sceneChanged.connect(self._onSceneChanged) self.initialized = True # Attach root item self.rootItem.setParentItem(self._window.contentItem()) self._window.contentItem().forceActiveFocus() self._updateSizes() self.ctx.makeCurrent() self._control.initialize(self.ctx.glContext)
class GLWin(QWindow): requestRender = Signal() def __init__(self, parent=None): QWindow.__init__(self, parent) self.setSurfaceType(QSurface.OpenGLSurface) self.gl_format = QSurfaceFormat() self.gl_format.setRenderableType(QSurfaceFormat.OpenGL) self.gl_format.setProfile(QSurfaceFormat.CoreProfile) self.gl_format.setVersion(4, 1) self.setFormat(self.gl_format) if self.supportsOpenGL(): print("OpenGL supported !") self.mouse_x = 0 self.mouse_y = 0 self.mouse_init = False self.animating = False self.requestRender.connect(self.requestUpdate) # self.setMouseGrabEnabled(True) self.mouse_pressed = False def mousePressEvent(self, ev): if ev.button() == Qt.LeftButton: self.mouse_pressed = True def mouseReleaseEvent(self, ev): if ev.button() == Qt.LeftButton: self.mouse_pressed = False self.mouse_init = False def mouseMoveEvent(self, ev): pos = ev.localPos() if self.mouse_pressed: if not self.mouse_init: self.mouse_x = pos.x() self.mouse_y = pos.y() self.mouse_init = True else: dx = self.mouse_x - pos.x() dy = self.mouse_y - pos.y() self.mouse_x = pos.x() self.mouse_y = pos.y() handle_mouse(c_float(dx), c_float(dy), c_float(0.001)) def start(self): self.animating = True self.renderLater() def render(self): display_loop(c_double(0.0)) def renderLater(self): self.requestRender.emit() def renderNow(self): if not self.isExposed(): return self.render() self.gl_context.swapBuffers(self) if self.animating: self.renderLater() def init_context(self): self.gl_context = QOpenGLContext(self) self.gl_context.setFormat(self.gl_format) if self.gl_context.create(): print("Context created !") if self.gl_context.makeCurrent(self): print("Context made current !") def init_scene(self): width = c_double(self.size().width()) height = c_double(self.size().height()) dpi_ratio = c_double(self.devicePixelRatio()) load_gl_symbol() init_gl(width, height, dpi_ratio) print_gl_info() init_scene(width, height, dpi_ratio) def resize(self): if self.isExposed(): width = c_double(self.size().width()) height = c_double(self.size().height()) dpi_ratio = c_double(self.devicePixelRatio()) resize_window(width, height, dpi_ratio) def event(self, ev): if ev.type() == QEvent.UpdateRequest: self.renderNow() return True else: return super().event(ev) def exposeEvent(self, ev): self.renderLater() def resizeEvent(self, ev): self.resize() self.renderLater()
def initialize(self, size: QSize, shareContext: QOpenGLContext) -> None: """ Initialize offscreen renderer. Args: size: The size of the area available for rendering. shareContext: OpenGL context used as a share context. Raises: RuntimeError: If the renderer has already been initialized. """ print(f'QmlOffscreenRenderer.initialize: {size}') if self.initialized: raise RuntimeError('Already initialized') format = QSurfaceFormat() format.setDepthBufferSize(16) format.setStencilBufferSize(8) context = QOpenGLContext() context.setFormat(format) context.setShareContext(shareContext) context.create() self.size = size self.context = context # Create offscreen surface with initialized format self._surface = surface = QOffscreenSurface() surface.setFormat(context.format()) surface.create() # Set up quick rendering self._control = control = QQuickRenderControl() self._window = window = QQuickWindow(control) self._engine = engine = QQmlEngine() if not engine.incubationController(): engine.setIncubationController(window.incubationController()) # Don't polish/sync/render immediately for better performance, use a timer self._renderTimer = renderTimer = QTimer() renderTimer.setSingleShot(True) renderTimer.setInterval(5) renderTimer.timeout.connect(self._onRenderTimer) self._syncTimer = syncTimer = QTimer() syncTimer.setSingleShot(True) syncTimer.setInterval(5) syncTimer.timeout.connect(self._onSyncTimer) syncTimer.destroyed.connect(self._onSyncTimerDestroyed) # Request to create frame buffer window.sceneGraphInitialized.connect(self._onSceneGraphInitialized) # Request to release frame buffer window.sceneGraphInvalidated.connect(self._onSceneGraphInvalidated) # Only render is needed control.renderRequested.connect(self._onRenderRequested) # Polish, sync & render control.sceneChanged.connect(self._onSceneChanged) self.initialized = True # Load QML component self._component = component = QQmlComponent(self._engine, self.qmlUrl) if component.isLoading(): component.statusChanged.connect(self._onComponentStatusChanged) else: self._attachRootItem()