mirror of
https://github.com/thp/pyotherside.git
synced 2025-02-05 08:08:23 +08:00
commit
3ce90c46ec
193
docs/index.rst
193
docs/index.rst
@ -525,6 +525,44 @@ deleted (there's no way for PyOtherSide to prevent referenced QObjects from
|
|||||||
being deleted, but PyOtherSide tries hard to detect the deletion of objects
|
being deleted, but PyOtherSide tries hard to detect the deletion of objects
|
||||||
and give meaningful error messages in case the reference is accessed).
|
and give meaningful error messages in case the reference is accessed).
|
||||||
|
|
||||||
|
OpenGL rendering in Python
|
||||||
|
==========================
|
||||||
|
|
||||||
|
You can render directly to a QML application's OpenGL context in your Python
|
||||||
|
code (i.e. via PyOpenGL or vispy.gloo) by using a ``PyGLArea`` item with the
|
||||||
|
following properties:
|
||||||
|
|
||||||
|
**before**
|
||||||
|
Whether to draw the ``PyGLArea`` before (under) or after (over) the scene.
|
||||||
|
Defaults to ``false``.
|
||||||
|
|
||||||
|
**renderer**
|
||||||
|
Reference to a python object providing the ``IRenderer`` interface:
|
||||||
|
|
||||||
|
.. function:: IRenderer.init()
|
||||||
|
|
||||||
|
Initialize OpenGL resources required for rendering.
|
||||||
|
This method is optional.
|
||||||
|
|
||||||
|
.. function:: IRenderer.reshape(x, y, width, height)
|
||||||
|
|
||||||
|
Called when the geometry has changed.
|
||||||
|
|
||||||
|
``(x, y)`` is the position of the bottom left corner of the area, in
|
||||||
|
window coordinates, e.g. (0, 0) is the bottom left corner of the window.
|
||||||
|
|
||||||
|
.. function:: IRenderer.render()
|
||||||
|
|
||||||
|
Render to the OpenGL context.
|
||||||
|
|
||||||
|
It is the renderer's responsibility to unbind any used resources to leave
|
||||||
|
the context in a clean state.
|
||||||
|
|
||||||
|
.. function:: IRenderer.cleanup()
|
||||||
|
|
||||||
|
Free any resources allocated by :func:`IRenderer.init`.
|
||||||
|
This method is optional.
|
||||||
|
|
||||||
|
|
||||||
Cookbook
|
Cookbook
|
||||||
========
|
========
|
||||||
@ -864,6 +902,161 @@ This module can now be imported in QML and used as ``source`` in the QML
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Rendering with PyOpenGL
|
||||||
|
-----------------------
|
||||||
|
|
||||||
|
The example below shows how to do raw OpenGL rendering in PyOpenGL using a
|
||||||
|
PyGLArea. It has been adapted from the tutorial in the Qt documentation at
|
||||||
|
http://qt-project.org/doc/qt-5/qtquick-scenegraph-openglunderqml-example.html.
|
||||||
|
|
||||||
|
**renderer.py**
|
||||||
|
|
||||||
|
.. code-block:: python
|
||||||
|
|
||||||
|
import numpy
|
||||||
|
|
||||||
|
from OpenGL.GL import *
|
||||||
|
from OpenGL.GL.shaders import compileShader, compileProgram
|
||||||
|
|
||||||
|
VERTEX_SHADER = """#version 130
|
||||||
|
attribute highp vec4 vertices;
|
||||||
|
varying highp vec2 coords;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vertices;
|
||||||
|
coords = vertices.xy;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
FRAGMENT_SHADER = """#version 130
|
||||||
|
uniform lowp float t;
|
||||||
|
varying highp vec2 coords;
|
||||||
|
void main() {
|
||||||
|
lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));
|
||||||
|
i = smoothstep(t - 0.8, t + 0.8, i);
|
||||||
|
i = floor(i * 20.) / 20.;
|
||||||
|
gl_FragColor = vec4(coords * .5 + .5, i, i);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Renderer(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.t = 0.0
|
||||||
|
self.values = numpy.array([
|
||||||
|
-1.0, -1.0,
|
||||||
|
1.0, -1.0,
|
||||||
|
-1.0, 1.0,
|
||||||
|
1.0, 1.0
|
||||||
|
], dtype=numpy.float32)
|
||||||
|
|
||||||
|
def set_t(self, t):
|
||||||
|
self.t = t
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.vertexbuffer = glGenBuffers(1)
|
||||||
|
vertex_shader = compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
|
||||||
|
fragment_shader = compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
|
||||||
|
self.program = compileProgram(vertex_shader, fragment_shader)
|
||||||
|
self.vertices_attr = glGetAttribLocation(self.program, b'vertices')
|
||||||
|
self.t_attr = glGetUniformLocation(self.program, b't')
|
||||||
|
|
||||||
|
def reshape(self, x, y, width, height):
|
||||||
|
glViewport(x, y, width, height)
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
glUseProgram(self.program)
|
||||||
|
try:
|
||||||
|
glDisable(GL_DEPTH_TEST)
|
||||||
|
glClearColor(0, 0, 0, 1)
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT)
|
||||||
|
glEnable(GL_BLEND)
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE)
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, self.vertexbuffer)
|
||||||
|
glEnableVertexAttribArray(self.vertices_attr)
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, self.values, GL_STATIC_DRAW)
|
||||||
|
glVertexAttribPointer(self.vertices_attr, 2, GL_FLOAT, GL_FALSE, 0, None)
|
||||||
|
glUniform1f(self.t_attr, self.t)
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
|
||||||
|
finally:
|
||||||
|
glDisableVertexAttribArray(0)
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||||
|
glUseProgram(0)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
glDeleteProgram(self.program)
|
||||||
|
glDeleteBuffers(1, [self.vertexbuffer])
|
||||||
|
|
||||||
|
**pyglarea.qml**
|
||||||
|
|
||||||
|
.. code-block:: javascript
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
import io.thp.pyotherside 1.3
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 320
|
||||||
|
height: 480
|
||||||
|
|
||||||
|
PyGLArea {
|
||||||
|
id: glArea
|
||||||
|
anchors.fill: parent
|
||||||
|
before: true
|
||||||
|
property var t: 0
|
||||||
|
|
||||||
|
SequentialAnimation on t {
|
||||||
|
NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
|
||||||
|
NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
|
||||||
|
loops: Animation.Infinite
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
|
||||||
|
onTChanged: {
|
||||||
|
if (renderer) {
|
||||||
|
py.call(py.getattr(renderer, 'set_t'), [t], update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: Qt.rgba(1, 1, 1, 0.7)
|
||||||
|
radius: 10
|
||||||
|
border.width: 1
|
||||||
|
border.color: "white"
|
||||||
|
anchors.fill: label
|
||||||
|
anchors.margins: -10
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
color: "black"
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: "The background here is a squircle rendered with raw OpenGL using a PyGLArea. This text label and its border is rendered using QML"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
Python {
|
||||||
|
id: py
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
addImportPath(Qt.resolvedUrl('.'));
|
||||||
|
importModule('renderer', function () {
|
||||||
|
call('renderer', [], function (renderer) {
|
||||||
|
glArea.renderer = renderer;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onError: console.log(traceback);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Building PyOtherSide
|
Building PyOtherSide
|
||||||
====================
|
====================
|
||||||
|
|
||||||
|
160
examples/pyfbo.qml
Normal file
160
examples/pyfbo.qml
Normal file
@ -0,0 +1,160 @@
|
|||||||
|
/****************************************************************************
|
||||||
|
**
|
||||||
|
** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
|
||||||
|
** Contact: http://www.qt-project.org/legal
|
||||||
|
**
|
||||||
|
** This file is part of the examples of the Qt Toolkit.
|
||||||
|
**
|
||||||
|
** $QT_BEGIN_LICENSE:BSD$
|
||||||
|
** You may use this file under the terms of the BSD license as follows:
|
||||||
|
**
|
||||||
|
** "Redistribution and use in source and binary forms, with or without
|
||||||
|
** modification, are permitted provided that the following conditions are
|
||||||
|
** met:
|
||||||
|
** * Redistributions of source code must retain the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer.
|
||||||
|
** * Redistributions in binary form must reproduce the above copyright
|
||||||
|
** notice, this list of conditions and the following disclaimer in
|
||||||
|
** the documentation and/or other materials provided with the
|
||||||
|
** distribution.
|
||||||
|
** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
|
||||||
|
** of its contributors may be used to endorse or promote products derived
|
||||||
|
** from this software without specific prior written permission.
|
||||||
|
**
|
||||||
|
**
|
||||||
|
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||||
|
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||||
|
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||||
|
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||||
|
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||||
|
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||||
|
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||||
|
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||||
|
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||||
|
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
|
||||||
|
**
|
||||||
|
** $QT_END_LICENSE$
|
||||||
|
**
|
||||||
|
****************************************************************************/
|
||||||
|
|
||||||
|
import QtQuick 2.0
|
||||||
|
import io.thp.pyotherside 1.3
|
||||||
|
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 400
|
||||||
|
height: 400
|
||||||
|
|
||||||
|
// The checkers background
|
||||||
|
ShaderEffect {
|
||||||
|
id: tileBackground
|
||||||
|
anchors.fill: parent
|
||||||
|
|
||||||
|
property real tileSize: 16
|
||||||
|
property color color1: Qt.rgba(0.9, 0.9, 0.9, 1);
|
||||||
|
property color color2: Qt.rgba(0.85, 0.85, 0.85, 1);
|
||||||
|
|
||||||
|
property size pixelSize: Qt.size(width / tileSize, height / tileSize);
|
||||||
|
|
||||||
|
fragmentShader:
|
||||||
|
"
|
||||||
|
uniform lowp vec4 color1;
|
||||||
|
uniform lowp vec4 color2;
|
||||||
|
uniform highp vec2 pixelSize;
|
||||||
|
varying highp vec2 qt_TexCoord0;
|
||||||
|
void main() {
|
||||||
|
highp vec2 tc = sign(sin(3.14152 * qt_TexCoord0 * pixelSize));
|
||||||
|
if (tc.x != tc.y)
|
||||||
|
gl_FragColor = color1;
|
||||||
|
else
|
||||||
|
gl_FragColor = color2;
|
||||||
|
}
|
||||||
|
"
|
||||||
|
}
|
||||||
|
|
||||||
|
PyFBO {
|
||||||
|
id: fbo
|
||||||
|
anchors.fill: parent
|
||||||
|
anchors.margins: 10
|
||||||
|
property var t: 0
|
||||||
|
|
||||||
|
SequentialAnimation on t {
|
||||||
|
NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
|
||||||
|
NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
|
||||||
|
loops: Animation.Infinite
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
|
||||||
|
// The transform is just to show something interesting..
|
||||||
|
transform: [
|
||||||
|
Rotation { id: rotation; axis.x: 0; axis.z: 0; axis.y: 1; angle: 0; origin.x: fbo.width / 2; origin.y: fbo.height / 2; },
|
||||||
|
Translate { id: txOut; x: -fbo.width / 2; y: -fbo.height / 2 },
|
||||||
|
Scale { id: scale; },
|
||||||
|
Translate { id: txIn; x: fbo.width / 2; y: fbo.height / 2 }
|
||||||
|
]
|
||||||
|
|
||||||
|
onTChanged: {
|
||||||
|
if (renderer) {
|
||||||
|
py.callMethod(renderer, 'set_t', [t], update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Just to show something interesting
|
||||||
|
SequentialAnimation {
|
||||||
|
PauseAnimation { duration: 5000 }
|
||||||
|
ParallelAnimation {
|
||||||
|
NumberAnimation { target: scale; property: "xScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
|
||||||
|
NumberAnimation { target: scale; property: "yScale"; to: 0.6; duration: 1000; easing.type: Easing.InOutBack }
|
||||||
|
}
|
||||||
|
NumberAnimation { target: rotation; property: "angle"; to: 80; duration: 1000; easing.type: Easing.InOutCubic }
|
||||||
|
NumberAnimation { target: rotation; property: "angle"; to: -80; duration: 1000; easing.type: Easing.InOutCubic }
|
||||||
|
NumberAnimation { target: rotation; property: "angle"; to: 0; duration: 1000; easing.type: Easing.InOutCubic }
|
||||||
|
NumberAnimation { target: fbo; property: "opacity"; to: 0.5; duration: 1000; easing.type: Easing.InOutCubic }
|
||||||
|
PauseAnimation { duration: 1000 }
|
||||||
|
NumberAnimation { target: fbo; property: "opacity"; to: 0.8; duration: 1000; easing.type: Easing.InOutCubic }
|
||||||
|
ParallelAnimation {
|
||||||
|
NumberAnimation { target: scale; property: "xScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
|
||||||
|
NumberAnimation { target: scale; property: "yScale"; to: 1; duration: 1000; easing.type: Easing.InOutBack }
|
||||||
|
}
|
||||||
|
running: true
|
||||||
|
loops: Animation.Infinite
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
id: labelFrame
|
||||||
|
anchors.margins: -10
|
||||||
|
radius: 5
|
||||||
|
color: "white"
|
||||||
|
border.color: "black"
|
||||||
|
opacity: 0.8
|
||||||
|
anchors.fill: label
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
anchors.bottom: fbo.bottom
|
||||||
|
anchors.left: fbo.left
|
||||||
|
anchors.right: fbo.right
|
||||||
|
anchors.margins: 20
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: "The squircle is an FBO, rendered by the application on the scene graph rendering thread. The FBO is managed and displayed using a PyFBO item."
|
||||||
|
}
|
||||||
|
|
||||||
|
Python {
|
||||||
|
id: py
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
addImportPath(Qt.resolvedUrl('.'));
|
||||||
|
importModule('renderer', function () {
|
||||||
|
call('renderer.Renderer', [], function (renderer) {
|
||||||
|
fbo.renderer = renderer;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onError: console.log(traceback);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
62
examples/pyglarea.qml
Normal file
62
examples/pyglarea.qml
Normal file
@ -0,0 +1,62 @@
|
|||||||
|
import QtQuick 2.0
|
||||||
|
import io.thp.pyotherside 1.3
|
||||||
|
|
||||||
|
Item {
|
||||||
|
width: 320
|
||||||
|
height: 480
|
||||||
|
|
||||||
|
PyGLArea {
|
||||||
|
id: glArea
|
||||||
|
anchors.fill: parent
|
||||||
|
before: true
|
||||||
|
property var t: 0
|
||||||
|
|
||||||
|
SequentialAnimation on t {
|
||||||
|
NumberAnimation { to: 1; duration: 2500; easing.type: Easing.InQuad }
|
||||||
|
NumberAnimation { to: 0; duration: 2500; easing.type: Easing.OutQuad }
|
||||||
|
loops: Animation.Infinite
|
||||||
|
running: true
|
||||||
|
}
|
||||||
|
|
||||||
|
onTChanged: {
|
||||||
|
if (renderer) {
|
||||||
|
py.callMethod(renderer, 'set_t', [t], update);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Rectangle {
|
||||||
|
color: Qt.rgba(1, 1, 1, 0.7)
|
||||||
|
radius: 10
|
||||||
|
border.width: 1
|
||||||
|
border.color: "white"
|
||||||
|
anchors.fill: label
|
||||||
|
anchors.margins: -10
|
||||||
|
}
|
||||||
|
|
||||||
|
Text {
|
||||||
|
id: label
|
||||||
|
color: "black"
|
||||||
|
wrapMode: Text.WordWrap
|
||||||
|
text: "The background here is a squircle rendered with raw OpenGL using a PyGLArea. This text label and its border is rendered using QML"
|
||||||
|
anchors.right: parent.right
|
||||||
|
anchors.left: parent.left
|
||||||
|
anchors.bottom: parent.bottom
|
||||||
|
anchors.margins: 20
|
||||||
|
}
|
||||||
|
|
||||||
|
Python {
|
||||||
|
id: py
|
||||||
|
|
||||||
|
Component.onCompleted: {
|
||||||
|
addImportPath(Qt.resolvedUrl('.'));
|
||||||
|
importModule('renderer', function () {
|
||||||
|
call('renderer.Renderer', [], function (renderer) {
|
||||||
|
glArea.renderer = renderer;
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
onError: console.log(traceback);
|
||||||
|
}
|
||||||
|
}
|
75
examples/renderer.py
Normal file
75
examples/renderer.py
Normal file
@ -0,0 +1,75 @@
|
|||||||
|
import numpy
|
||||||
|
|
||||||
|
from OpenGL.GL import *
|
||||||
|
from OpenGL.GL.shaders import compileShader, compileProgram
|
||||||
|
|
||||||
|
VERTEX_SHADER = """#version 130
|
||||||
|
attribute highp vec4 vertices;
|
||||||
|
varying highp vec2 coords;
|
||||||
|
|
||||||
|
void main() {
|
||||||
|
gl_Position = vertices;
|
||||||
|
coords = vertices.xy;
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
FRAGMENT_SHADER = """#version 130
|
||||||
|
uniform lowp float t;
|
||||||
|
varying highp vec2 coords;
|
||||||
|
void main() {
|
||||||
|
lowp float i = 1. - (pow(abs(coords.x), 4.) + pow(abs(coords.y), 4.));
|
||||||
|
i = smoothstep(t - 0.8, t + 0.8, i);
|
||||||
|
i = floor(i * 20.) / 20.;
|
||||||
|
gl_FragColor = vec4(coords * .5 + .5, i, i);
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
class Renderer(object):
|
||||||
|
|
||||||
|
def __init__(self):
|
||||||
|
self.t = 0.0
|
||||||
|
self.values = numpy.array([
|
||||||
|
-1.0, -1.0,
|
||||||
|
1.0, -1.0,
|
||||||
|
-1.0, 1.0,
|
||||||
|
1.0, 1.0
|
||||||
|
], dtype=numpy.float32)
|
||||||
|
|
||||||
|
def set_t(self, t):
|
||||||
|
self.t = t
|
||||||
|
|
||||||
|
def init(self):
|
||||||
|
self.vertexbuffer = glGenBuffers(1)
|
||||||
|
vertex_shader = compileShader(VERTEX_SHADER, GL_VERTEX_SHADER)
|
||||||
|
fragment_shader = compileShader(FRAGMENT_SHADER, GL_FRAGMENT_SHADER)
|
||||||
|
self.program = compileProgram(vertex_shader, fragment_shader)
|
||||||
|
self.vertices_attr = glGetAttribLocation(self.program, b'vertices')
|
||||||
|
self.t_attr = glGetUniformLocation(self.program, b't')
|
||||||
|
|
||||||
|
def reshape(self, x, y, width, height):
|
||||||
|
glViewport(x, y, width, height)
|
||||||
|
|
||||||
|
def render(self):
|
||||||
|
glUseProgram(self.program)
|
||||||
|
try:
|
||||||
|
glDisable(GL_DEPTH_TEST)
|
||||||
|
glClearColor(0, 0, 0, 1)
|
||||||
|
glClear(GL_COLOR_BUFFER_BIT)
|
||||||
|
glEnable(GL_BLEND)
|
||||||
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE)
|
||||||
|
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, self.vertexbuffer)
|
||||||
|
glEnableVertexAttribArray(self.vertices_attr)
|
||||||
|
glBufferData(GL_ARRAY_BUFFER, self.values, GL_STATIC_DRAW)
|
||||||
|
glVertexAttribPointer(self.vertices_attr, 2, GL_FLOAT, GL_FALSE, 0, None)
|
||||||
|
glUniform1f(self.t_attr, self.t)
|
||||||
|
|
||||||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4)
|
||||||
|
finally:
|
||||||
|
glDisableVertexAttribArray(0)
|
||||||
|
glBindBuffer(GL_ARRAY_BUFFER, 0)
|
||||||
|
glUseProgram(0)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
glDeleteProgram(self.program)
|
||||||
|
glDeleteBuffers(1, [self.vertexbuffer])
|
101
src/pyfbo.cpp
Normal file
101
src/pyfbo.cpp
Normal file
@ -0,0 +1,101 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* PyOtherSide: Asynchronous Python 3 Bindings for Qt 5
|
||||||
|
* Copyright (c) 2014, Dennis Tomas <den.t@gmx.de>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "pyfbo.h"
|
||||||
|
|
||||||
|
#include <QtGui/QOpenGLFramebufferObject>
|
||||||
|
#include <QSize>
|
||||||
|
|
||||||
|
|
||||||
|
class PyFboRenderer : public QQuickFramebufferObject::Renderer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PyFboRenderer()
|
||||||
|
: m_renderer(0)
|
||||||
|
, m_size(0, 0)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
~PyFboRenderer()
|
||||||
|
{
|
||||||
|
if (m_renderer) {
|
||||||
|
delete m_renderer;
|
||||||
|
m_renderer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void render()
|
||||||
|
{
|
||||||
|
if (m_renderer)
|
||||||
|
m_renderer->render();
|
||||||
|
}
|
||||||
|
|
||||||
|
void synchronize(QQuickFramebufferObject *item)
|
||||||
|
{
|
||||||
|
PyFbo *pyFbo = static_cast<PyFbo *>(item);
|
||||||
|
|
||||||
|
if (pyFbo->renderer() != m_rendererRef) {
|
||||||
|
// The renderer has changed.
|
||||||
|
if (m_renderer) {
|
||||||
|
m_renderer->cleanup();
|
||||||
|
delete m_renderer;
|
||||||
|
m_renderer = 0;
|
||||||
|
}
|
||||||
|
m_rendererRef = pyFbo->renderer();
|
||||||
|
if (!m_rendererRef.isNull()) {
|
||||||
|
m_renderer = new PyGLRenderer(m_rendererRef);
|
||||||
|
m_renderer->init();
|
||||||
|
m_sizeChanged = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_renderer && m_sizeChanged) {
|
||||||
|
// The size has changed.
|
||||||
|
m_renderer->reshape(QRect(QPoint(0, 0), m_size));
|
||||||
|
m_sizeChanged = false;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QOpenGLFramebufferObject *createFramebufferObject(const QSize &size)
|
||||||
|
{
|
||||||
|
m_size = size;
|
||||||
|
m_sizeChanged = true;
|
||||||
|
QOpenGLFramebufferObjectFormat format;
|
||||||
|
// TODO: get the FBO format from the PyGLRenderer.
|
||||||
|
return new QOpenGLFramebufferObject(size, format);
|
||||||
|
}
|
||||||
|
private:
|
||||||
|
QVariant m_rendererRef;
|
||||||
|
PyGLRenderer *m_renderer;
|
||||||
|
QSize m_size;
|
||||||
|
bool m_sizeChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
void PyFbo::setRenderer(QVariant rendererRef)
|
||||||
|
{
|
||||||
|
if (rendererRef == m_rendererRef)
|
||||||
|
return;
|
||||||
|
m_rendererRef = rendererRef;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
QQuickFramebufferObject::Renderer *PyFbo::createRenderer() const
|
||||||
|
{
|
||||||
|
return new PyFboRenderer();
|
||||||
|
}
|
41
src/pyfbo.h
Normal file
41
src/pyfbo.h
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* PyOtherSide: Asynchronous Python 3 Bindings for Qt 5
|
||||||
|
* Copyright (c) 2014, Dennis Tomas <den.t@gmx.de>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef PYFBO_H
|
||||||
|
#define PYFBO_H
|
||||||
|
|
||||||
|
#include "pyglrenderer.h"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QtQuick/QQuickFramebufferObject>
|
||||||
|
|
||||||
|
class PyFbo : public QQuickFramebufferObject
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QVariant renderer READ renderer WRITE setRenderer)
|
||||||
|
|
||||||
|
public:
|
||||||
|
Renderer *createRenderer() const;
|
||||||
|
|
||||||
|
QVariant renderer() const { return m_rendererRef; };
|
||||||
|
void setRenderer(QVariant rendererRef);
|
||||||
|
private:
|
||||||
|
QVariant m_rendererRef;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
131
src/pyglarea.cpp
Normal file
131
src/pyglarea.cpp
Normal file
@ -0,0 +1,131 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* PyOtherSide: Asynchronous Python 3 Bindings for Qt 5
|
||||||
|
* Copyright (c) 2014, Dennis Tomas <den.t@gmx.de>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "qpython_priv.h"
|
||||||
|
#include "pyglarea.h"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QPointF>
|
||||||
|
#include <QtQuick/qquickwindow.h>
|
||||||
|
#include <QtGui/QOpenGLShaderProgram>
|
||||||
|
#include <QtGui/QOpenGLContext>
|
||||||
|
|
||||||
|
|
||||||
|
PyGLArea::PyGLArea()
|
||||||
|
: m_before(false)
|
||||||
|
, m_renderer(0)
|
||||||
|
, m_rendererChanged(false)
|
||||||
|
, m_beforeChanged(true)
|
||||||
|
{
|
||||||
|
connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*)));
|
||||||
|
}
|
||||||
|
|
||||||
|
PyGLArea::~PyGLArea()
|
||||||
|
{
|
||||||
|
if (m_renderer) {
|
||||||
|
delete m_renderer;
|
||||||
|
m_renderer = 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLArea::setRenderer(QVariant renderer)
|
||||||
|
{
|
||||||
|
if (renderer == m_pyRenderer)
|
||||||
|
return;
|
||||||
|
m_pyRenderer = renderer;
|
||||||
|
|
||||||
|
// Defer creating the PyGLRenderer until sync() is called,
|
||||||
|
// when we have an OpenGL context.
|
||||||
|
m_rendererChanged = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLArea::setBefore(bool before)
|
||||||
|
{
|
||||||
|
if (before == m_before)
|
||||||
|
return;
|
||||||
|
m_before = before;
|
||||||
|
|
||||||
|
m_beforeChanged = true;
|
||||||
|
update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLArea::handleWindowChanged(QQuickWindow *win)
|
||||||
|
{
|
||||||
|
if (win) {
|
||||||
|
connect(win, SIGNAL(beforeSynchronizing()), this, SLOT(sync()), Qt::DirectConnection);
|
||||||
|
connect(win, SIGNAL(sceneGraphInvalidated()), this, SLOT(cleanup()), Qt::DirectConnection);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLArea::update() {
|
||||||
|
if (window())
|
||||||
|
window()->update();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLArea::sync()
|
||||||
|
{
|
||||||
|
if (m_beforeChanged) {
|
||||||
|
disconnect(window(), SIGNAL(beforeRendering()), this, SLOT(render()));
|
||||||
|
disconnect(window(), SIGNAL(afterRendering()), this, SLOT(render()));
|
||||||
|
if (m_before) {
|
||||||
|
// If we allow QML to do the clearing, they would clear what we paint
|
||||||
|
// and nothing would show.
|
||||||
|
window()->setClearBeforeRendering(false);
|
||||||
|
connect(window(), SIGNAL(beforeRendering()), this, SLOT(render()), Qt::DirectConnection);
|
||||||
|
} else {
|
||||||
|
window()->setClearBeforeRendering(true);
|
||||||
|
connect(window(), SIGNAL(afterRendering()), this, SLOT(render()), Qt::DirectConnection);
|
||||||
|
}
|
||||||
|
m_beforeChanged = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (m_rendererChanged) {
|
||||||
|
if (m_renderer) {
|
||||||
|
m_renderer->cleanup();
|
||||||
|
delete m_renderer;
|
||||||
|
m_renderer = 0;
|
||||||
|
}
|
||||||
|
if (!m_pyRenderer.isNull()) {
|
||||||
|
m_renderer = new PyGLRenderer(m_pyRenderer);
|
||||||
|
m_renderer->init();
|
||||||
|
window()->resetOpenGLState();
|
||||||
|
}
|
||||||
|
m_rendererChanged = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLArea::render()
|
||||||
|
{
|
||||||
|
if (!m_renderer)
|
||||||
|
return;
|
||||||
|
QPointF pos = mapToScene(QPointF(.0, .0));
|
||||||
|
m_renderer->reshape(
|
||||||
|
QRect(
|
||||||
|
(long)pos.x(), (long)(window()->height() - this->height() - pos.y()),
|
||||||
|
this->width(), this->height()
|
||||||
|
)
|
||||||
|
);
|
||||||
|
m_renderer->render();
|
||||||
|
window()->resetOpenGLState();
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLArea::cleanup()
|
||||||
|
{
|
||||||
|
if (m_renderer) m_renderer->cleanup();
|
||||||
|
}
|
65
src/pyglarea.h
Normal file
65
src/pyglarea.h
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* PyOtherSide: Asynchronous Python 3 Bindings for Qt 5
|
||||||
|
* Copyright (c) 2014, Dennis Tomas <den.t@gmx.de>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef PYOTHERSIDE_PYGLAREA_H
|
||||||
|
#define PYOTHERSIDE_PYGLAREA_H
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
#include <QString>
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QtQuick/QQuickItem>
|
||||||
|
|
||||||
|
#include "pyobject_ref.h"
|
||||||
|
|
||||||
|
#include "pyglrenderer.h"
|
||||||
|
|
||||||
|
|
||||||
|
class PyGLArea : public QQuickItem
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(QVariant renderer READ renderer WRITE setRenderer)
|
||||||
|
Q_PROPERTY(bool before READ before WRITE setBefore)
|
||||||
|
|
||||||
|
public:
|
||||||
|
PyGLArea();
|
||||||
|
~PyGLArea();
|
||||||
|
|
||||||
|
QVariant renderer() const { return m_pyRenderer; };
|
||||||
|
bool before() { return m_before; };
|
||||||
|
void setRenderer(QVariant renderer);
|
||||||
|
void setBefore(bool before);
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void sync();
|
||||||
|
void update();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void handleWindowChanged(QQuickWindow *win);
|
||||||
|
void render();
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
QVariant m_pyRenderer;
|
||||||
|
bool m_before;
|
||||||
|
PyGLRenderer *m_renderer;
|
||||||
|
bool m_rendererChanged;
|
||||||
|
bool m_beforeChanged;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* PYOTHERSIDE_PYGLAREA_H */
|
152
src/pyglrenderer.cpp
Normal file
152
src/pyglrenderer.cpp
Normal file
@ -0,0 +1,152 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* PyOtherSide: Asynchronous Python 3 Bindings for Qt 5
|
||||||
|
* Copyright (c) 2014, Dennis Tomas <den.t@gmx.de>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#include "qpython_priv.h"
|
||||||
|
#include "converter.h"
|
||||||
|
#include "pyobject_ref.h"
|
||||||
|
#include "pyglrenderer.h"
|
||||||
|
#include "ensure_gil_state.h"
|
||||||
|
|
||||||
|
#include <QDebug>
|
||||||
|
#include <QMetaType>
|
||||||
|
|
||||||
|
|
||||||
|
PyGLRenderer::PyGLRenderer(QVariant pyRenderer)
|
||||||
|
: m_pyRendererObject(0)
|
||||||
|
, m_initMethod(0)
|
||||||
|
, m_reshapeMethod(0)
|
||||||
|
, m_renderMethod(0)
|
||||||
|
, m_cleanupMethod(0)
|
||||||
|
, m_initialized(false)
|
||||||
|
{
|
||||||
|
|
||||||
|
ENSURE_GIL_STATE;
|
||||||
|
|
||||||
|
if (pyRenderer.userType() != qMetaTypeId<PyObjectRef>()) {
|
||||||
|
qWarning() << "Renderer must be of type PyObjectRef (got "
|
||||||
|
<< pyRenderer << ").";
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_pyRendererObject = pyRenderer.value<PyObjectRef>().newRef();
|
||||||
|
|
||||||
|
if (PyObject_HasAttrString(m_pyRendererObject, "render")) {
|
||||||
|
m_renderMethod = PyObject_GetAttrString(m_pyRendererObject, "render");
|
||||||
|
if (!m_renderMethod) {
|
||||||
|
qWarning() << "Error getting render method of renderer.";
|
||||||
|
PyErr_PrintEx(0);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
qWarning() << "Renderer has no render method.";
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyObject_HasAttrString(m_pyRendererObject, "init")) {
|
||||||
|
m_initMethod = PyObject_GetAttrString(m_pyRendererObject, "init");
|
||||||
|
if (!m_initMethod) {
|
||||||
|
qWarning() << "Error getting init method of renderer.";
|
||||||
|
PyErr_PrintEx(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyObject_HasAttrString(m_pyRendererObject, "reshape")) {
|
||||||
|
m_reshapeMethod = PyObject_GetAttrString(m_pyRendererObject, "reshape");
|
||||||
|
if (!m_reshapeMethod) {
|
||||||
|
qWarning() << "Error getting reshape method of renderer.";
|
||||||
|
PyErr_PrintEx(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (PyObject_HasAttrString(m_pyRendererObject, "cleanup")) {
|
||||||
|
m_cleanupMethod = PyObject_GetAttrString(m_pyRendererObject, "cleanup");
|
||||||
|
if (!m_cleanupMethod) {
|
||||||
|
qWarning() << "Error getting cleanup method of renderer.";
|
||||||
|
PyErr_PrintEx(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
PyGLRenderer::~PyGLRenderer()
|
||||||
|
{
|
||||||
|
ENSURE_GIL_STATE;
|
||||||
|
Py_CLEAR(m_initMethod);
|
||||||
|
Py_CLEAR(m_reshapeMethod);
|
||||||
|
Py_CLEAR(m_renderMethod);
|
||||||
|
Py_CLEAR(m_cleanupMethod);
|
||||||
|
Py_CLEAR(m_pyRendererObject);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLRenderer::init() {
|
||||||
|
if (m_initialized || !m_initMethod)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ENSURE_GIL_STATE;
|
||||||
|
|
||||||
|
PyObject *args = PyTuple_New(0);
|
||||||
|
PyObject *o = PyObject_Call(m_initMethod, args, NULL);
|
||||||
|
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||||
|
Py_DECREF(args);
|
||||||
|
m_initialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLRenderer::reshape(QRect geometry)
|
||||||
|
{
|
||||||
|
if (!m_initialized || !m_reshapeMethod)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ENSURE_GIL_STATE;
|
||||||
|
|
||||||
|
// Call the reshape callback with arguments x, y, width, height.
|
||||||
|
// These are the boundaries in which the callback should render,
|
||||||
|
// though it may choose to ignore them and simply paint anywhere over
|
||||||
|
// (or below) the QML scene.
|
||||||
|
// (x, y) is the bottom left corner.
|
||||||
|
// (x + width, y + height) is the top right corner.
|
||||||
|
PyObject *args = Py_BuildValue(
|
||||||
|
"llll", geometry.x(), geometry.y(), geometry.width(), geometry.height()
|
||||||
|
);
|
||||||
|
PyObject *o = PyObject_Call(m_reshapeMethod, args, NULL);
|
||||||
|
Py_DECREF(args);
|
||||||
|
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLRenderer::render()
|
||||||
|
{
|
||||||
|
if (!m_initialized || !m_renderMethod)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ENSURE_GIL_STATE;
|
||||||
|
|
||||||
|
PyObject *args = PyTuple_New(0);
|
||||||
|
PyObject *o = PyObject_Call(m_renderMethod, args, NULL);
|
||||||
|
Py_DECREF(args);
|
||||||
|
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void PyGLRenderer::cleanup()
|
||||||
|
{
|
||||||
|
if (!m_initialized || !m_cleanupMethod)
|
||||||
|
return;
|
||||||
|
|
||||||
|
ENSURE_GIL_STATE;
|
||||||
|
|
||||||
|
PyObject *args = PyTuple_New(0);
|
||||||
|
PyObject *o = PyObject_Call(m_cleanupMethod, args, NULL);
|
||||||
|
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
|
||||||
|
m_initialized = false;
|
||||||
|
Py_DECREF(args);
|
||||||
|
}
|
49
src/pyglrenderer.h
Normal file
49
src/pyglrenderer.h
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
|
||||||
|
/**
|
||||||
|
* PyOtherSide: Asynchronous Python 3 Bindings for Qt 5
|
||||||
|
* Copyright (c) 2014, Dennis Tomas <den.t@gmx.de>
|
||||||
|
*
|
||||||
|
* Permission to use, copy, modify, and/or distribute this software for any
|
||||||
|
* purpose with or without fee is hereby granted, provided that the above
|
||||||
|
* copyright notice and this permission notice appear in all copies.
|
||||||
|
*
|
||||||
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
|
||||||
|
* REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND
|
||||||
|
* FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
|
||||||
|
* INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
|
||||||
|
* LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
|
||||||
|
* OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
|
||||||
|
* PERFORMANCE OF THIS SOFTWARE.
|
||||||
|
**/
|
||||||
|
|
||||||
|
#ifndef PYOTHERSIDE_PYGLRENDERER_H
|
||||||
|
#define PYOTHERSIDE_PYGLRENDERER_H
|
||||||
|
|
||||||
|
#include "Python.h"
|
||||||
|
|
||||||
|
#include <QVariant>
|
||||||
|
#include <QString>
|
||||||
|
#include <QRect>
|
||||||
|
|
||||||
|
|
||||||
|
class PyGLRenderer {
|
||||||
|
|
||||||
|
public:
|
||||||
|
PyGLRenderer(QVariant pyRenderer);
|
||||||
|
~PyGLRenderer();
|
||||||
|
|
||||||
|
void init();
|
||||||
|
void reshape(QRect geometry);
|
||||||
|
void render();
|
||||||
|
void cleanup();
|
||||||
|
|
||||||
|
private:
|
||||||
|
PyObject *m_pyRendererObject;
|
||||||
|
PyObject *m_initMethod;
|
||||||
|
PyObject *m_reshapeMethod;
|
||||||
|
PyObject *m_renderMethod;
|
||||||
|
PyObject *m_cleanupMethod;
|
||||||
|
bool m_initialized;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif /* PYOTHERSIDE_PYGLRENDERER_H */
|
@ -67,6 +67,12 @@ PyObjectRef::operator=(const PyObjectRef &other)
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool
|
||||||
|
PyObjectRef::operator==(const PyObjectRef &other)
|
||||||
|
{
|
||||||
|
return pyobject == other.pyobject;
|
||||||
|
}
|
||||||
|
|
||||||
PyObject *
|
PyObject *
|
||||||
PyObjectRef::newRef() const
|
PyObjectRef::newRef() const
|
||||||
{
|
{
|
||||||
|
@ -30,6 +30,7 @@ class PyObjectRef {
|
|||||||
PyObjectRef(const PyObjectRef &other);
|
PyObjectRef(const PyObjectRef &other);
|
||||||
virtual ~PyObjectRef();
|
virtual ~PyObjectRef();
|
||||||
PyObjectRef &operator=(const PyObjectRef &other);
|
PyObjectRef &operator=(const PyObjectRef &other);
|
||||||
|
bool operator==(const PyObjectRef &other);
|
||||||
|
|
||||||
PyObject *newRef() const;
|
PyObject *newRef() const;
|
||||||
|
|
||||||
|
@ -18,6 +18,8 @@
|
|||||||
|
|
||||||
#include "qpython_priv.h"
|
#include "qpython_priv.h"
|
||||||
#include "qpython.h"
|
#include "qpython.h"
|
||||||
|
#include "pyglarea.h"
|
||||||
|
#include "pyfbo.h"
|
||||||
#include "qpython_imageprovider.h"
|
#include "qpython_imageprovider.h"
|
||||||
#include "global_libpython_loader.h"
|
#include "global_libpython_loader.h"
|
||||||
#include "pythonlib_loader.h"
|
#include "pythonlib_loader.h"
|
||||||
@ -66,4 +68,6 @@ PyOtherSideExtensionPlugin::registerTypes(const char *uri)
|
|||||||
qmlRegisterType<QPython12>(uri, 1, 2, PYOTHERSIDE_QPYTHON_NAME);
|
qmlRegisterType<QPython12>(uri, 1, 2, PYOTHERSIDE_QPYTHON_NAME);
|
||||||
qmlRegisterType<QPython13>(uri, 1, 3, PYOTHERSIDE_QPYTHON_NAME);
|
qmlRegisterType<QPython13>(uri, 1, 3, PYOTHERSIDE_QPYTHON_NAME);
|
||||||
qmlRegisterType<QPython14>(uri, 1, 4, PYOTHERSIDE_QPYTHON_NAME);
|
qmlRegisterType<QPython14>(uri, 1, 4, PYOTHERSIDE_QPYTHON_NAME);
|
||||||
|
qmlRegisterType<PyGLArea>(uri, 1, 5, PYOTHERSIDE_QPYGLAREA_NAME);
|
||||||
|
qmlRegisterType<PyFbo>(uri, 1, 5, PYOTHERSIDE_PYFBO_NAME);
|
||||||
}
|
}
|
||||||
|
@ -25,6 +25,8 @@
|
|||||||
#define PYOTHERSIDE_PLUGIN_ID "io.thp.pyotherside"
|
#define PYOTHERSIDE_PLUGIN_ID "io.thp.pyotherside"
|
||||||
#define PYOTHERSIDE_IMAGEPROVIDER_ID "python"
|
#define PYOTHERSIDE_IMAGEPROVIDER_ID "python"
|
||||||
#define PYOTHERSIDE_QPYTHON_NAME "Python"
|
#define PYOTHERSIDE_QPYTHON_NAME "Python"
|
||||||
|
#define PYOTHERSIDE_QPYGLAREA_NAME "PyGLArea"
|
||||||
|
#define PYOTHERSIDE_PYFBO_NAME "PyFBO"
|
||||||
|
|
||||||
class Q_DECL_EXPORT PyOtherSideExtensionPlugin : public QQmlExtensionPlugin {
|
class Q_DECL_EXPORT PyOtherSideExtensionPlugin : public QQmlExtensionPlugin {
|
||||||
Q_OBJECT
|
Q_OBJECT
|
||||||
|
@ -28,6 +28,14 @@ HEADERS += pyotherside_plugin.h
|
|||||||
SOURCES += qpython_imageprovider.cpp
|
SOURCES += qpython_imageprovider.cpp
|
||||||
HEADERS += qpython_imageprovider.h
|
HEADERS += qpython_imageprovider.h
|
||||||
|
|
||||||
|
# PyGLArea
|
||||||
|
SOURCES += pyglarea.cpp pyglrenderer.cpp
|
||||||
|
HEADERS += pyglarea.h pyglrenderer.h
|
||||||
|
|
||||||
|
# PyFBO
|
||||||
|
SOURCES += pyfbo.cpp
|
||||||
|
HEADERS += pyfbo.h
|
||||||
|
|
||||||
# Importer from Qt Resources
|
# Importer from Qt Resources
|
||||||
RESOURCES += qrc_importer.qrc
|
RESOURCES += qrc_importer.qrc
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user