├── .gitignore ├── README.md ├── logo.png └── qpygletwidget.py /.gitignore: -------------------------------------------------------------------------------- 1 | *.py[cod] 2 | 3 | # C extensions 4 | *.so 5 | 6 | # Packages 7 | *.egg 8 | *.egg-info 9 | dist 10 | build 11 | eggs 12 | parts 13 | bin 14 | var 15 | sdist 16 | develop-eggs 17 | .installed.cfg 18 | lib 19 | lib64 20 | 21 | # Installer logs 22 | pip-log.txt 23 | 24 | # Unit test / coverage reports 25 | .coverage 26 | .tox 27 | nosetests.xml 28 | 29 | # Translations 30 | *.mo 31 | 32 | # Mr Developer 33 | .mr.developer.cfg 34 | .project 35 | .pydevproject 36 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | QPygletWidget 2 | ============= 3 | 4 | A widget to easily display **one** pyglet scene in a PySide/Qt application. 5 | 6 | This has been tested on Ubuntu 12.04, Windows XP and Windows 7. 7 | 8 | License 9 | --------- 10 | There is no license as this is more a reminder than anything else. 11 | 12 | You are free to use this file without any restrictions. 13 | 14 | Requirements 15 | ----------------- 16 | 17 | - Python 2.7 18 | - PySide 19 | 20 | 21 | Usage 22 | -------- 23 | Here is a simple example: 24 | 25 | ```python 26 | from qpygletwidget import QPygletWidget 27 | 28 | 29 | class MyPygletWidget(QPygletWidget): 30 | def on_init(self): 31 | self.sprite = pyglet.sprite.Sprite(pyglet.resource.image("logo.png")) 32 | self.label = pyglet.text.Label( 33 | text="This is a pyglet label rendered in a Qt widget :)") 34 | self.setMinimumSize(QSize(640, 480)) 35 | 36 | def on_draw(self): 37 | self.sprite.draw() 38 | self.label.draw() 39 | 40 | 41 | def main(): 42 | app = QtGui.QApplication(sys.argv) 43 | window = QtGui.QMainWindow() 44 | widget = MyPygletWidget() 45 | window.setCentralWidget(widget) 46 | window.show() 47 | app.exec_() 48 | 49 | if __name__ == "__main__": 50 | main() 51 | ``` 52 | 53 | You can run the script by issuing the following command:: 54 | 55 | ```bash 56 | python qpygletwidget.py 57 | ``` 58 | 59 | If you are using PyQt instead of PySide, the port should be really easy as the only thing 60 | you will probably have to do is to replace **from PySide** by **from PyQt4** 61 | 62 | 63 | [![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/ColinDuquesnoy/qpygletwidget/trend.png)](https://bitdeli.com/free "Bitdeli Badge") 64 | 65 | -------------------------------------------------------------------------------- /logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ColinDuquesnoy/QPygletWidget/b51e72eaf941b303002228768191f1d0337fd237/logo.png -------------------------------------------------------------------------------- /qpygletwidget.py: -------------------------------------------------------------------------------- 1 | """ 2 | This module contains the definiton of a pyglet widget for a 3 | PySide application: QPygletWidget 4 | 5 | It also provides a basic usage example. 6 | """ 7 | import sys 8 | import pyglet 9 | pyglet.options['shadow_window'] = False 10 | pyglet.options['debug_gl'] = False 11 | from pyglet import gl 12 | from PySide import QtCore, QtGui, QtOpenGL 13 | 14 | 15 | class ObjectSpace(object): 16 | """ Object space mocker """ 17 | def __init__(self): 18 | # Textures and buffers scheduled for deletion the next time this 19 | # object space is active. 20 | self._doomed_textures = [] 21 | self._doomed_buffers = [] 22 | 23 | 24 | class Context(object): 25 | """ 26 | pyglet.gl.Context mocker. This is used to make pyglet believe that a valid 27 | context has already been setup. (Qt takes care of creating the open gl 28 | context) 29 | 30 | _Most of the methods are empty, there is just the minimum required to make 31 | it look like a duck..._ 32 | """ 33 | # define the same class attribute as pyglet.gl.Context 34 | CONTEXT_SHARE_NONE = None 35 | CONTEXT_SHARE_EXISTING = 1 36 | _gl_begin = False 37 | _info = None 38 | _workaround_checks = [ 39 | ('_workaround_unpack_row_length', 40 | lambda info: info.get_renderer() == 'GDI Generic'), 41 | ('_workaround_vbo', 42 | lambda info: info.get_renderer().startswith('ATI Radeon X')), 43 | ('_workaround_vbo_finish', 44 | lambda info: ('ATI' in info.get_renderer() and 45 | info.have_version(1, 5) and 46 | sys.platform == 'darwin'))] 47 | 48 | def __init__(self, context_share=None): 49 | """ 50 | Setup workaround attr and object spaces (again to mock what is done in 51 | pyglet context) 52 | """ 53 | self.object_space = ObjectSpace() 54 | for attr, check in self._workaround_checks: 55 | setattr(self, attr, None) 56 | 57 | def __repr__(self): 58 | return '%s()' % self.__class__.__name__ 59 | 60 | def set_current(self): 61 | pass 62 | 63 | def destroy(self): 64 | pass 65 | 66 | def delete_texture(self, texture_id): 67 | pass 68 | 69 | def delete_buffer(self, buffer_id): 70 | pass 71 | 72 | 73 | class QPygletWidget(QtOpenGL.QGLWidget): 74 | """ 75 | A simple pyglet widget. 76 | 77 | User can subclass this widget and implement the following methods: 78 | - on_init: called when open gl has been initialised 79 | - on_update: called every dt. 80 | - on_draw: called when paintGL is executed 81 | - on_resize: called when resizeGL is executed 82 | """ 83 | def __init__(self, parent=None, 84 | clear_color=(0.0, 0.0, 0.0, 1.0), 85 | frame_time=32, 86 | dt=16): 87 | """ 88 | :param clear_color: The widget clear color 89 | :type clear_color: tuple(r, g, b, a) 90 | 91 | :param frame_time: The desired frame time [ms] 92 | :type: frame_time: int 93 | 94 | :param dt: The desired update rate [ms] 95 | :type: dt: int 96 | """ 97 | QtOpenGL.QGLWidget.__init__(self, parent) 98 | 99 | # init members 100 | self._clear_color = clear_color 101 | self._dt = dt 102 | self.update_timer = QtCore.QTimer() 103 | self.draw_timer = QtCore.QTimer() 104 | 105 | # configure draw and update timers 106 | self.update_timer.setInterval(dt) 107 | self.update_timer.timeout.connect(self._update) 108 | self.draw_timer.setInterval(frame_time) 109 | self.draw_timer.timeout.connect(self.updateGL) 110 | 111 | # start timers 112 | self.update_timer.start() 113 | self.draw_timer.start() 114 | 115 | def _update(self): 116 | """ 117 | Calls on_update with the choosen dt 118 | """ 119 | self.on_update(self._dt) 120 | 121 | def on_init(self): 122 | """ 123 | Lets the user initialise himself 124 | """ 125 | pass 126 | 127 | def on_draw(self): 128 | """ 129 | Lets the user draw his scene 130 | """ 131 | pass 132 | 133 | def on_update(self, dt): 134 | """ 135 | Lets the user draw his scene 136 | """ 137 | pass 138 | 139 | def on_resize(self, w, h): 140 | """ 141 | Lets the user handle the widget resize event. By default, this method 142 | resizes the view to the widget size. 143 | """ 144 | gl.glViewport(0, 0, w, h) 145 | gl.glMatrixMode(gl.GL_PROJECTION) 146 | gl.glLoadIdentity() 147 | gl.glOrtho(0, w, 0, h, -1, 1) 148 | gl.glMatrixMode(gl.GL_MODELVIEW) 149 | 150 | def initializeGL(self): 151 | """ 152 | Initialises open gl: 153 | - create a mock context to fool pyglet 154 | - setup various opengl rule (only the clear color atm) 155 | """ 156 | gl.current_context = Context() 157 | gl.glClearColor(0.0, 0.0, 0.0, 1.0) 158 | self.on_init() 159 | 160 | def resizeGL(self, w, h): 161 | """ 162 | Resizes the gl camera to match the widget size. 163 | """ 164 | self.on_resize(w, h) 165 | 166 | def paintGL(self): 167 | """ 168 | Clears the back buffer than calls the on_draw method 169 | """ 170 | gl.glClear(gl.GL_COLOR_BUFFER_BIT | gl.GL_DEPTH_BUFFER_BIT) 171 | self.on_draw() 172 | 173 | 174 | class MyPygletWidget(QPygletWidget): 175 | def on_init(self): 176 | self.sprite = pyglet.sprite.Sprite(pyglet.resource.image("logo.png")) 177 | self.label = pyglet.text.Label( 178 | text="This is a pyglet label rendered in a Qt widget :)") 179 | self.setMinimumSize(QtCore.QSize(640, 480)) 180 | 181 | def on_draw(self): 182 | self.sprite.draw() 183 | self.label.draw() 184 | 185 | 186 | def main(): 187 | app = QtGui.QApplication(sys.argv) 188 | window = QtGui.QMainWindow() 189 | widget = MyPygletWidget() 190 | window.setCentralWidget(widget) 191 | window.show() 192 | app.exec_() 193 | 194 | if __name__ == "__main__": 195 | main() 196 | --------------------------------------------------------------------------------