mirror of
https://github.com/thp/pyotherside.git
synced 2025-02-05 08:08:23 +08:00
OpenGL rendering in python.
This commit is contained in:
parent
289e5ed616
commit
f8de71d449
@ -864,6 +864,67 @@ This module can now be imported in QML and used as ``source`` in the QML
|
||||
}
|
||||
}
|
||||
|
||||
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.
|
||||
|
||||
**rendergl.qml**
|
||||
|
||||
.. code-block:: javascript
|
||||
|
||||
import QtQuick 2.0
|
||||
import io.thp.pyotherside 1.3
|
||||
|
||||
Item {
|
||||
width: 800
|
||||
height: 600
|
||||
|
||||
PyGLArea {
|
||||
id: glArea
|
||||
anchors.fill: parent
|
||||
before: true
|
||||
}
|
||||
|
||||
Text {
|
||||
x: 100
|
||||
y: 100
|
||||
text: "Hello World!"
|
||||
}
|
||||
|
||||
Python {
|
||||
Component.onCompleted: {
|
||||
importModule('myrenderer', function () {
|
||||
glArea.initGL = 'myrenderer.initGL';
|
||||
glArea.paintGL = 'myrenderer.paintGL';
|
||||
glArea.cleanupGL = 'myrenderer.cleanupGL';
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
**myrenderer.py**
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
def initGL():
|
||||
"""Initialize OpenGL stuff like textures, FBOs etc..."""
|
||||
|
||||
def paintGL(x, y, width, height):
|
||||
"""
|
||||
Render to the OpenGL context.
|
||||
|
||||
(x, y, width, height) indicates the area to render on.
|
||||
|
||||
The coordinate system's origin is at the bottom left corner of the
|
||||
window.
|
||||
"""
|
||||
|
||||
def cleanupGL():
|
||||
"""Clean up OpenGL stuff initialized in initGL()."""
|
||||
|
||||
Building PyOtherSide
|
||||
====================
|
||||
|
||||
|
132
src/pyglarea.cpp
Normal file
132
src/pyglarea.cpp
Normal file
@ -0,0 +1,132 @@
|
||||
|
||||
/**
|
||||
* 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_t(0)
|
||||
, m_before(false)
|
||||
, m_renderer(0)
|
||||
{
|
||||
connect(this, SIGNAL(windowChanged(QQuickWindow*)), this, SLOT(handleWindowChanged(QQuickWindow*)));
|
||||
}
|
||||
|
||||
PyGLArea::~PyGLArea()
|
||||
{
|
||||
delete m_renderer;
|
||||
}
|
||||
|
||||
void PyGLArea::setInitGL(QString initGL)
|
||||
{
|
||||
if (initGL == m_initGL)
|
||||
return;
|
||||
m_renderer->setInitGL(initGL);
|
||||
m_initGL = initGL;
|
||||
if (window())
|
||||
window()->update();
|
||||
}
|
||||
|
||||
void PyGLArea::setPaintGL(QString paintGL)
|
||||
{
|
||||
if (paintGL == m_paintGL)
|
||||
return;
|
||||
m_paintGL = paintGL;
|
||||
m_renderer->setPaintGL(paintGL);
|
||||
if (window())
|
||||
window()->update();
|
||||
}
|
||||
|
||||
void PyGLArea::setCleanupGL(QString cleanupGL)
|
||||
{
|
||||
if (cleanupGL == m_cleanupGL)
|
||||
return;
|
||||
m_cleanupGL = cleanupGL;
|
||||
m_renderer->setCleanupGL(cleanupGL);
|
||||
if (window())
|
||||
window()->update();
|
||||
}
|
||||
|
||||
void PyGLArea::setBefore(bool before)
|
||||
{
|
||||
if (before == m_before)
|
||||
return;
|
||||
m_before = before;
|
||||
}
|
||||
|
||||
void PyGLArea::setT(qreal t)
|
||||
{
|
||||
if (t == m_t)
|
||||
return;
|
||||
m_t = t;
|
||||
emit tChanged();
|
||||
if (window())
|
||||
window()->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);
|
||||
// If we allow QML to do the clearing, they would clear what we paint
|
||||
// and nothing would show.
|
||||
win->setClearBeforeRendering(false);
|
||||
}
|
||||
}
|
||||
|
||||
void PyGLArea::update() {
|
||||
window()->update();
|
||||
}
|
||||
|
||||
void PyGLArea::sync()
|
||||
{
|
||||
if (!m_renderer) {
|
||||
m_renderer = new PyGLRenderer();
|
||||
if (m_before)
|
||||
connect(window(), SIGNAL(beforeRendering()), this, SLOT(paint()), Qt::DirectConnection);
|
||||
else
|
||||
connect(window(), SIGNAL(afterRendering()), this, SLOT(paint()), Qt::DirectConnection);
|
||||
}
|
||||
}
|
||||
|
||||
void PyGLArea::paint()
|
||||
{
|
||||
QPointF pos = mapToScene(QPointF(.0, .0));
|
||||
m_renderer->setRect(
|
||||
QRect(
|
||||
(long)pos.x(), (long)(window()->height() - this->height() - pos.y()),
|
||||
(long)this->width(), (long)this->height()
|
||||
)
|
||||
);
|
||||
m_renderer->init();
|
||||
m_renderer->paint();
|
||||
window()->resetOpenGLState();
|
||||
}
|
||||
|
||||
void PyGLArea::cleanup()
|
||||
{
|
||||
m_renderer->cleanup();
|
||||
}
|
77
src/pyglarea.h
Normal file
77
src/pyglarea.h
Normal file
@ -0,0 +1,77 @@
|
||||
|
||||
/**
|
||||
* 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 <QtQuick/QQuickItem>
|
||||
|
||||
#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(bool before READ before WRITE setBefore)
|
||||
Q_PROPERTY(qreal t READ t WRITE setT NOTIFY tChanged)
|
||||
|
||||
public:
|
||||
PyGLArea();
|
||||
~PyGLArea();
|
||||
|
||||
QString initGL() const { return m_initGL; };
|
||||
QString paintGL() const { return m_paintGL; };
|
||||
QString cleanupGL() const { return m_paintGL; };
|
||||
bool before() { return m_before; };
|
||||
void setInitGL(QString initGL);
|
||||
void setPaintGL(QString paintGL);
|
||||
void setCleanupGL(QString cleanupGL);
|
||||
void setBefore(bool before);
|
||||
qreal t() const { return m_t; }
|
||||
void setT(qreal t);
|
||||
|
||||
signals:
|
||||
void tChanged();
|
||||
|
||||
public slots:
|
||||
void sync();
|
||||
void update();
|
||||
|
||||
private slots:
|
||||
void handleWindowChanged(QQuickWindow *win);
|
||||
void paint();
|
||||
void cleanup();
|
||||
|
||||
private:
|
||||
qreal m_t;
|
||||
PyObject *m_paintGLCallable;
|
||||
bool m_initialized;
|
||||
QString m_initGL;
|
||||
QString m_paintGL;
|
||||
QString m_cleanupGL;
|
||||
bool m_before;
|
||||
PyGLRenderer *m_renderer;
|
||||
};
|
||||
|
||||
#endif /* PYOTHERSIDE_PYGLAREA_H */
|
119
src/pyglrenderer.cpp
Normal file
119
src/pyglrenderer.cpp
Normal file
@ -0,0 +1,119 @@
|
||||
#include "qpython_priv.h"
|
||||
#include "pyglrenderer.h"
|
||||
|
||||
|
||||
PyGLRenderer::PyGLRenderer()
|
||||
: m_paintGLCallable(0)
|
||||
, m_initialized(false)
|
||||
, m_initGL("")
|
||||
, m_paintGL("")
|
||||
{
|
||||
}
|
||||
|
||||
PyGLRenderer::~PyGLRenderer()
|
||||
{
|
||||
if (m_paintGLCallable) {
|
||||
QPythonPriv *priv = QPythonPriv::instance();
|
||||
priv->enter();
|
||||
Py_DECREF(m_paintGLCallable);
|
||||
priv->leave();
|
||||
m_paintGLCallable = 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;
|
||||
}
|
||||
|
||||
void PyGLRenderer::init() {
|
||||
if (!m_initialized) {
|
||||
if (!m_initGL.isEmpty()) {
|
||||
QPythonPriv *priv = QPythonPriv::instance();
|
||||
priv->enter();
|
||||
PyObject *initGLCallable = priv->eval(m_initGL);
|
||||
PyObject *args = PyTuple_New(0);
|
||||
PyObject *o = PyObject_Call(initGLCallable, args, NULL);
|
||||
if (o) Py_DECREF(o);
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(initGLCallable);
|
||||
priv->leave();
|
||||
}
|
||||
m_initialized = true;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PyGLRenderer::paint()
|
||||
{
|
||||
if (!m_initialized || m_paintGL.isEmpty())
|
||||
return;
|
||||
|
||||
QPythonPriv *priv = QPythonPriv::instance();
|
||||
priv->enter();
|
||||
if (!m_paintGLCallable)
|
||||
m_paintGLCallable = priv->eval(m_paintGL);
|
||||
|
||||
// Call the paintGL 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 *x = PyLong_FromLong(m_rect.x());
|
||||
PyObject *y = PyLong_FromLong(m_rect.y());
|
||||
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);
|
||||
if (o) Py_DECREF(o);
|
||||
Py_DECREF(x);
|
||||
Py_DECREF(y);
|
||||
Py_DECREF(width);
|
||||
Py_DECREF(height);
|
||||
Py_DECREF(args);
|
||||
priv->leave();
|
||||
}
|
||||
|
||||
void PyGLRenderer::cleanup()
|
||||
{
|
||||
if (!m_cleanupGL.isEmpty()) {
|
||||
QPythonPriv *priv = QPythonPriv::instance();
|
||||
priv->enter();
|
||||
PyObject *cleanupGLCallable = priv->eval(m_cleanupGL);
|
||||
PyObject *args = PyTuple_New(0);
|
||||
PyObject *o = PyObject_Call(cleanupGLCallable, args, NULL);
|
||||
if (o) Py_DECREF(o);
|
||||
m_initialized = true;
|
||||
Py_DECREF(args);
|
||||
Py_DECREF(cleanupGLCallable);
|
||||
priv->leave();
|
||||
}
|
||||
}
|
54
src/pyglrenderer.h
Normal file
54
src/pyglrenderer.h
Normal file
@ -0,0 +1,54 @@
|
||||
|
||||
/**
|
||||
* 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 <QtCore/QObject>
|
||||
#include <QString>
|
||||
#include <QRect>
|
||||
|
||||
|
||||
class PyGLRenderer : public QObject {
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
PyGLRenderer();
|
||||
~PyGLRenderer();
|
||||
|
||||
void init();
|
||||
void paint();
|
||||
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;
|
||||
bool m_initialized;
|
||||
QString m_initGL;
|
||||
QString m_paintGL;
|
||||
QString m_cleanupGL;
|
||||
};
|
||||
|
||||
#endif /* PYOTHERSIDE_PYGLRENDERER_H */
|
@ -18,6 +18,7 @@
|
||||
|
||||
#include "qpython_priv.h"
|
||||
#include "qpython.h"
|
||||
#include "pyglarea.h"
|
||||
#include "qpython_imageprovider.h"
|
||||
#include "global_libpython_loader.h"
|
||||
#include "pythonlib_loader.h"
|
||||
@ -66,4 +67,5 @@ PyOtherSideExtensionPlugin::registerTypes(const char *uri)
|
||||
qmlRegisterType<QPython12>(uri, 1, 2, PYOTHERSIDE_QPYTHON_NAME);
|
||||
qmlRegisterType<QPython13>(uri, 1, 3, PYOTHERSIDE_QPYTHON_NAME);
|
||||
qmlRegisterType<QPython14>(uri, 1, 4, PYOTHERSIDE_QPYTHON_NAME);
|
||||
qmlRegisterType<PyGLArea>(uri, 1, 5, PYOTHERSIDE_QPYGLAREA_NAME);
|
||||
}
|
||||
|
@ -25,6 +25,7 @@
|
||||
#define PYOTHERSIDE_PLUGIN_ID "io.thp.pyotherside"
|
||||
#define PYOTHERSIDE_IMAGEPROVIDER_ID "python"
|
||||
#define PYOTHERSIDE_QPYTHON_NAME "Python"
|
||||
#define PYOTHERSIDE_QPYGLAREA_NAME "PyGLArea"
|
||||
|
||||
class Q_DECL_EXPORT PyOtherSideExtensionPlugin : public QQmlExtensionPlugin {
|
||||
Q_OBJECT
|
||||
|
@ -28,6 +28,10 @@ HEADERS += pyotherside_plugin.h
|
||||
SOURCES += qpython_imageprovider.cpp
|
||||
HEADERS += qpython_imageprovider.h
|
||||
|
||||
# PyGLArea
|
||||
SOURCES += pyglarea.cpp pyglrenderer.cpp
|
||||
HEADERS += pyglarea.h pyglrenderer.h
|
||||
|
||||
# Importer from Qt Resources
|
||||
RESOURCES += qrc_importer.qrc
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user