I’ve had the goal for a while now to be able to put beautiful, translucent, fluid gui widgets on top of an OpenGL scene. Now, I’ve never been much of a gui person insofar as I haven’t actually made many guis (although I do take a keen interest in good design and what constitutes good design), and so I’m not set in my ways regarding the merits of one library over another. I had a quick tinker with Clutter, but decided that I needed something a little more cross platform and I got a bit scared by the lack of actual widgets. It was suggested to me that QML might provide the desired beautiful and fluid UI elements so off I went to investigate (as it turns out, QML lacks much in the way of widgets too, but I’d already drunk the kool-aid by that point).
There’s stacks of pretty good documentation on the web all about Qt and QML and I’m not intending to repeat it here, but I think that what I’ve learnt is worth telling the world about. I got the bulk of the actual solution from here, with the caveat that I wanted to do everything in Python, so I also looked at the pyside OpenGL demos and the declarative demos. What I produced is an attempt to replicate the hellogl.py demo included with pyside examples, but using QML for the widgets. I’m a complete Qt and OpenGL noob, so the code may be nothing like a good solution. Still, it seems to work.
The full source code for little program can be found here. It includes the (unmodified) Slider.qml file from the Qt site that’s used with the flickr demo app.
I think Python is actually pretty readable to understand what’s going on and it should be mostly pretty easy to interpret in light of the Qt docs on overlaying widgets on OpenGL scenes. I also maintain that having a program that actually works is far more useful then huge piles of tutorials that don’t quite do what you think they’re going to do. That said, there are a few points that I think are worth pointing out…
The OpenGL interface
The combining of the QML widgets with the OpenGL stuff is done using an instance of OpenGLScene, which inherits from QGraphicsScene. The problem with this is that you get presented with a pretty raw entry point to actually draw the OpenGL, given by the QGraphicsScene.drawBackground() method, which gets called when windows needs updating. Now if you intend on drawing lots of vertices, the overhead of Python will start to drag at this point. The problem is that you can’t define a glGenList as is done with the hellogl.py app as you don’t really seem to have full control over the GL context (and its really easy to bugger up the rendering of the QML widget by putting the GL context into some funny state).
To get round this problem I used a vertex list, generated using my VertexList class (unfortunately I can’t seem to find the handy tutorial that taught me about using vertex lists). The VertexList class holds off making any OpenGL calls until the render() method is called. At this point, it renders the vertex list that hopefully contains the object of interest. The VertexList class presents methods equivalent to the usual OpenGL calls: glBegin(mode), glEnd, glVertex3d and glColor4f (I stopped there, as it was enough for my purposes).
The GLWidge.resizeGL() method that is in the original hellogl.py app is used to maintain a square viewport. Unfortunately, simply setting the viewport on a resize causes the QML overlay to bugger up (it gets resized too, but not in a sensible way – the mouse events seem to respond at the wrong locations). The solution that is used is to save the viewport at the beginning of my rendering (in Model.render()) using
viewport = GL.glGetIntegerv(GL.GL_VIEWPORT)
and then modifying the viewport as desired, finally restoring it at the end with GL.glViewport(*viewport).
Transparent QML widgets
The thing that took me a little bit of fiddling was getting the QML widget to be transparent. Its simple to make the entire QML view transparent using the setOpacity(opacity) method on the QDeclarativeView object, but to get it to work as desired, with the transparency set by the QML code requires setting the base color of the palette that is used by the QDeclarativeView widget to be transparent, using the following code in the initialisation of OverlayWidget:
palette = QtGui.QPalette() palette.setColor(QtGui.QPalette.Base, QtCore.Qt.transparent) qml_view = QDeclarativeView(self) qml_view.setPalette(palette)
Update: My next blog post discusses incorporating textures into this example using GLSL.