1
0
mirror of https://github.com/thp/pyotherside.git synced 2025-02-05 08:08:23 +08:00

PyGLArea: Pass renderer object instead of function names.

This commit is contained in:
Dennis Tomas 2014-09-06 11:21:39 +02:00
parent 52e75e918f
commit f08ab6be25
5 changed files with 167 additions and 143 deletions

View File

@ -868,9 +868,11 @@ OpenGL rendering in Python
==========================
You can render directly to Qt's OpenGL context in your Python code (i.e. via
PyOpenGL or vispy.gloo) using a ``PyGLArea`` item and supplying ``paintGL``
and optional ``initGL`` and ``cleanupGL`` functions. The ``before`` property
controls whether to render before or after the QML scene is rendered.
PyOpenGL or vispy.gloo) using a ``PyGLArea`` item and supplying a ``renderer``
which must provide a ``render()`` method and may optionally provide ``init()``
and ``cleanup()`` methods.
The ``before`` property controls whether to render before or after the QML
scene is rendered.
**rendergl.qml**
@ -898,9 +900,9 @@ controls whether to render before or after the QML scene is rendered.
Python {
Component.onCompleted: {
importModule('myrenderer', function () {
glArea.initGL = 'myrenderer.initGL';
glArea.paintGL = 'myrenderer.paintGL';
glArea.cleanupGL = 'myrenderer.cleanupGL';
call('myrenderer.Renderer', [], function (renderer) {
glArea.renderer = renderer;
});
});
}
}
@ -909,21 +911,23 @@ controls whether to render before or after the QML scene is rendered.
.. code-block:: python
def initGL():
"""Initialize OpenGL stuff like textures, FBOs etc..."""
class Renderer(object):
def paintGL(x, y, width, height):
"""
Render to the OpenGL context.
def init(self):
"""Initialize OpenGL stuff like textures, FBOs etc..."""
(x, y, width, height) indicates the area to render on.
def render(self, x, y, width, height):
"""
Render to the OpenGL context.
The coordinate system's origin is at the bottom left corner of the
window.
"""
(x, y, width, height) indicates the area to render on.
def cleanupGL():
"""Clean up OpenGL stuff initialized in initGL()."""
The coordinate system's origin is at the bottom left corner of the
window.
"""
def cleanup(self):
"""Clean up OpenGL stuff initialized in initGL()."""
Building PyOtherSide
====================

View File

@ -36,34 +36,21 @@ PyGLArea::PyGLArea()
PyGLArea::~PyGLArea()
{
delete m_renderer;
if (m_renderer) {
delete m_renderer;
m_renderer = 0;
}
}
void PyGLArea::setInitGL(QString initGL)
void PyGLArea::setRenderer(QVariant renderer)
{
if (initGL == m_initGL)
if (renderer == m_pyRenderer)
return;
m_initGL = initGL;
delete m_renderer;
m_renderer = 0;
}
void PyGLArea::setPaintGL(QString paintGL)
{
if (paintGL == m_paintGL)
return;
m_paintGL = paintGL;
delete m_renderer;
m_renderer = 0;
}
void PyGLArea::setCleanupGL(QString cleanupGL)
{
if (cleanupGL == m_cleanupGL)
return;
m_cleanupGL = cleanupGL;
delete m_renderer;
m_renderer = 0;
m_pyRenderer = renderer;
if (m_renderer) {
delete m_renderer;
m_renderer = 0;
}
}
void PyGLArea::setBefore(bool before)
@ -71,8 +58,10 @@ void PyGLArea::setBefore(bool before)
if (before == m_before)
return;
m_before = before;
delete m_renderer;
m_renderer = 0;
if (m_renderer) {
delete m_renderer;
m_renderer = 0;
}
}
void PyGLArea::setT(qreal t)
@ -101,21 +90,21 @@ void PyGLArea::update() {
void PyGLArea::sync()
{
if (!m_renderer) {
disconnect(window(), SIGNAL(beforeRendering()), this, SLOT(paint()));
disconnect(window(), SIGNAL(afterRendering()), this, SLOT(paint()));
m_renderer = new PyGLRenderer();
m_renderer->setInitGL(m_initGL);
if (!m_renderer && !m_pyRenderer.isNull()) {
disconnect(window(), SIGNAL(beforeRendering()), this, SLOT(render()));
disconnect(window(), SIGNAL(afterRendering()), this, SLOT(render()));
m_renderer = new PyGLRenderer(m_pyRenderer);
/*m_renderer->setInitGL(m_initGL);
m_renderer->setPaintGL(m_paintGL);
m_renderer->setCleanupGL(m_cleanupGL);
m_renderer->setCleanupGL(m_cleanupGL);*/
if (m_before)
connect(window(), SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
connect(window(), SIGNAL(beforeRendering()), this, SLOT(render()), Qt::DirectConnection);
else
connect(window(), SIGNAL(afterRendering()), this, SLOT(paint()), Qt::DirectConnection);
connect(window(), SIGNAL(afterRendering()), this, SLOT(render()), Qt::DirectConnection);
}
}
void PyGLArea::paint()
void PyGLArea::render()
{
QPointF pos = mapToScene(QPointF(.0, .0));
m_renderer->setRect(
@ -125,7 +114,7 @@ void PyGLArea::paint()
)
);
m_renderer->init();
m_renderer->paint();
m_renderer->render();
window()->resetOpenGLState();
}

View File

@ -22,17 +22,18 @@
#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(QString initGL READ initGL WRITE setInitGL)
Q_PROPERTY(QString paintGL READ paintGL WRITE setPaintGL)
Q_PROPERTY(QString cleanupGL READ cleanupGL WRITE setCleanupGL)
Q_PROPERTY(QVariant renderer READ renderer WRITE setRenderer)
Q_PROPERTY(bool before READ before WRITE setBefore)
Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
@ -40,13 +41,9 @@ public:
PyGLArea();
~PyGLArea();
QString initGL() const { return m_initGL; };
QString paintGL() const { return m_paintGL; };
QString cleanupGL() const { return m_paintGL; };
QVariant renderer() const { return m_pyRenderer; };
bool before() { return m_before; };
void setInitGL(QString initGL);
void setPaintGL(QString paintGL);
void setCleanupGL(QString cleanupGL);
void setRenderer(QVariant renderer);
void setBefore(bool before);
qreal t() const { return m_t; }
void setT(qreal t);
@ -60,14 +57,12 @@ public slots:
private slots:
void handleWindowChanged(QQuickWindow *win);
void paint();
void render();
void cleanup();
private:
qreal m_t;
QString m_initGL;
QString m_paintGL;
QString m_cleanupGL;
QVariant m_pyRenderer;
bool m_before;
PyGLRenderer *m_renderer;
};

View File

@ -17,58 +17,36 @@
**/
#include "qpython_priv.h"
#include "converter.h"
#include "pyobject_ref.h"
#include "pyglrenderer.h"
#include <QDebug>
#include <QMetaType>
PyGLRenderer::PyGLRenderer()
: m_paintGLCallable(0)
PyGLRenderer::PyGLRenderer(QVariant pyRenderer)
: m_pyRendererObject(0)
, m_renderMethod(0)
, m_initialized(false)
, m_initGL("")
, m_paintGL("")
{
m_pyRenderer = pyRenderer;
}
PyGLRenderer::~PyGLRenderer()
{
if (m_paintGLCallable) {
if (m_pyRendererObject) {
QPythonPriv *priv = QPythonPriv::instance();
priv->enter();
Py_DECREF(m_paintGLCallable);
Py_CLEAR(m_pyRendererObject);
if (m_renderMethod)
Py_DECREF(m_renderMethod);
priv->leave();
m_paintGLCallable = 0;
m_pyRendererObject = 0;
m_renderMethod = 0;
}
}
void PyGLRenderer::setInitGL(QString initGL)
{
if (initGL == m_initGL)
return;
m_initGL = initGL;
m_initialized = false;
}
void PyGLRenderer::setPaintGL(QString paintGL)
{
if (paintGL == m_paintGL)
return;
m_paintGL = paintGL;
if (m_paintGLCallable) {
QPythonPriv *priv = QPythonPriv::instance();
priv->enter();
Py_DECREF(m_paintGLCallable);
priv->leave();
}
}
void PyGLRenderer::setCleanupGL(QString cleanupGL)
{
if (cleanupGL == m_cleanupGL)
return;
m_cleanupGL = cleanupGL;
}
void PyGLRenderer::setRect(QRect rect) {
m_rect = rect;
}
@ -77,41 +55,54 @@ void PyGLRenderer::init() {
if (m_initialized)
return;
if (!m_initGL.isEmpty()) {
QPythonPriv *priv = QPythonPriv::instance();
priv->enter();
PyObject *initGLCallable = priv->eval(m_initGL);
if (!initGLCallable) {
qWarning() << "Init callback " << m_initGL << " not defined.";
priv->leave();
return;
}
PyObject *args = PyTuple_New(0);
PyObject *o = PyObject_Call(initGLCallable, args, NULL);
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
Py_DECREF(args);
Py_DECREF(initGLCallable);
QPythonPriv *priv = QPythonPriv::instance();
priv->enter();
PyObject *pyRendererObject = getPyRendererObject();
if (!pyRendererObject || pyRendererObject == Py_None) {
priv->leave();
return;
}
if (!PyObject_HasAttrString(m_pyRendererObject, "init")) {
// Optional init() method not found, consider the renderer initialized.
priv->leave();
m_initialized = true;
return;
}
PyObject *initMethod = PyObject_GetAttrString(m_pyRendererObject, "init");
if (!initMethod) {
qWarning() << "Failed to get init method of renderer.";
PyErr_PrintEx(0);
priv->leave();
return;
}
PyObject *args = PyTuple_New(0);
PyObject *o = PyObject_Call(initMethod, args, NULL);
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
Py_DECREF(args);
Py_DECREF(initMethod);
priv->leave();
m_initialized = true;
}
void PyGLRenderer::paint()
void PyGLRenderer::render()
{
if (!m_initialized || m_paintGL.isEmpty())
if (!m_initialized)
return;
QPythonPriv *priv = QPythonPriv::instance();
priv->enter();
if (!m_paintGLCallable) {
m_paintGLCallable = priv->eval(m_paintGL);
if (!m_paintGLCallable) {
qWarning() << "Paint callback " << m_paintGL << " not defined.";
priv->leave();
return;
}
PyObject *renderMethod = getRenderMethod();
if (!renderMethod) {
qWarning() << "Failed to get render method of renderer.";
PyErr_PrintEx(0);
priv->leave();
return;
}
// Call the paintGL callback with arguments x, y, width, height.
@ -125,7 +116,7 @@ void PyGLRenderer::paint()
PyObject *width = PyLong_FromLong(m_rect.width());
PyObject *height = PyLong_FromLong(m_rect.height());
PyObject *args = PyTuple_Pack(4, x, y, width, height);
PyObject *o = PyObject_Call(m_paintGLCallable, args, NULL);
PyObject *o = PyObject_Call(renderMethod, args, NULL);
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
Py_DECREF(x);
Py_DECREF(y);
@ -137,22 +128,67 @@ void PyGLRenderer::paint()
void PyGLRenderer::cleanup()
{
if (m_cleanupGL.isEmpty())
return;
QPythonPriv *priv = QPythonPriv::instance();
priv->enter();
PyObject *cleanupGLCallable = priv->eval(m_cleanupGL);
if (!cleanupGLCallable) {
qWarning() << "Cleanup callback " << m_cleanupGL << " not defined.";
PyObject *pyRendererObject = getPyRendererObject();
if (!pyRendererObject || pyRendererObject == Py_None ||
!PyObject_HasAttrString(m_pyRendererObject, "cleanup")) {
priv->leave();
return;
}
PyObject *cleanupMethod = PyObject_GetAttrString(m_pyRendererObject, "cleanup");
if (!cleanupMethod) {
qWarning() << "Failed to get cleanup method of renderer.";
PyErr_PrintEx(0);
priv->leave();
return;
}
PyObject *args = PyTuple_New(0);
PyObject *o = PyObject_Call(cleanupGLCallable, args, NULL);
PyObject *o = PyObject_Call(cleanupMethod, args, NULL);
if (o) Py_DECREF(o); else PyErr_PrintEx(0);
m_initialized = true;
Py_DECREF(args);
Py_DECREF(cleanupGLCallable);
Py_DECREF(cleanupMethod);
priv->leave();
}
PyObject *PyGLRenderer::getPyRendererObject() {
if (m_pyRendererObject) {
return m_pyRendererObject;
}
if (m_pyRenderer.userType() != qMetaTypeId<PyObjectRef>()) {
qWarning() << "Renderer must be of type PyObjectRef (got "
<< m_pyRenderer << ").";
return NULL;
}
m_pyRendererObject = m_pyRenderer.value<PyObjectRef>().newRef();
return m_pyRendererObject;
}
PyObject *PyGLRenderer::getRenderMethod() {
if (m_renderMethod) {
return m_renderMethod;
}
PyObject *pyRendererObject = getPyRendererObject();
if (!pyRendererObject || pyRendererObject == Py_None) {
return NULL;
}
if (!PyObject_HasAttrString(m_pyRendererObject, "render")) {
qWarning() << "Renderer has no render method.";
return NULL;
}
PyObject *m_renderMethod = PyObject_GetAttrString(m_pyRendererObject, "render");
if (!m_renderMethod) {
qWarning() << "Failed to get render method of renderer.";
PyErr_PrintEx(0);
return NULL;
}
return m_renderMethod;
}

View File

@ -21,34 +21,34 @@
#include "Python.h"
#include <QtCore/QObject>
#include <QVariant>
#include <QString>
#include <QRect>
#include <QtCore/QObject>
class PyGLRenderer : public QObject {
Q_OBJECT
public:
PyGLRenderer();
PyGLRenderer(QVariant pyRenderer);
~PyGLRenderer();
void init();
void paint();
void render();
void cleanup();
void setInitGL(QString initGL);
void setPaintGL(QString paintGL);
void setCleanupGL(QString cleanupGL);
void setRect(QRect rect);
private:
QRect m_rect;
PyObject *m_paintGLCallable;
QVariant m_pyRenderer;
PyObject *m_pyRendererObject;
PyObject *m_renderMethod;
bool m_initialized;
QString m_initGL;
QString m_paintGL;
QString m_cleanupGL;
PyObject *getPyRendererObject();
PyObject *getRenderMethod();
};
#endif /* PYOTHERSIDE_PYGLRENDERER_H */