Difference between revisions of "PyCEGUI"

From CEGUI Wiki - Crazy Eddie's GUI System (Open Source)
Jump to: navigation, search
(strip hard coded paths, delete stuff I tested for baribal)
Line 129: Line 129:
 
== Documentation ==
 
== Documentation ==
 
Since the API is resembling the C++ version as closely as possible (mainly to avoid confusion and as easy switch between C++ and Python as possible), doxygen API docs apply for most of the classes. Doxygen comments are also extracted and added to __doc__ of all the python classes and methods, using python docstrings is therefore possible but not perfect.
 
Since the API is resembling the C++ version as closely as possible (mainly to avoid confusion and as easy switch between C++ and Python as possible), doxygen API docs apply for most of the classes. Doxygen comments are also extracted and added to __doc__ of all the python classes and methods, using python docstrings is therefore possible but not perfect.
 +
 +
== Subscriptions ==
 +
One major thing that isn't documented in doxygen or other wiki docs and that is important in python is how subscriptions work.
 +
 +
Subscribing to listenerClass:
 +
<code>
 +
wnd.subscribeEvent(PyCEGUI.Window.EventMouseEnters, listenerClass, "methodInThatClass")
 +
</code>
 +
 +
Subscribing to a free function:
 +
<code>
 +
wnd.subscribeEvent(PyCEGUI.Window.EventMouseEnters, PythonModule.freeFunction)
 +
</code>
  
 
== Implementation ==
 
== Implementation ==

Revision as of 16:44, 20 November 2010

Written for CEGUI 0.7


Works with versions 0.7.x (obsolete)

Requires at least version
0.7.5

Since CEGUI 0.7.5 official python bindings are provided. The main reason for that is that the new CEGUI tools are written completely in python. As a nice side effect, everybody can use the bindings standalone in their python only apps as well as embedded python apps for scripting and what not.

Where to get the bindings?

We provide ready to go packages for win32 platform since it's fairly hard to get things going there. You can get them at the pypi repository. For other platforms, please just download the SDK and use bindings you find there. You can also use Win32 bindings from the SDK if you use embedded python, it can be easier than installing the aforementioned package.

Base app

I will provide a little base application below so you know the basics of how to use CEGUI in python environment. OpenGL is used in the base app.

import os, sys

from OpenGL.GL import * from OpenGL.GLU import * from OpenGL.GLUT import *

  1. you must have PyCEGUI python package installed (see pypi repository)
  2. or you must make it work yourself using binary bindings from SDK

import PyCEGUI import PyCEGUIOpenGLRenderer

CEGUI_PATH = ""

class BaseApp(object):

   def __init__(self):
       glutInit(sys.argv)
       glutInitDisplayMode(GLUT_DEPTH|GLUT_DOUBLE|GLUT_RGBA)
       glutInitWindowSize(1280, 1024)
       glutInitWindowPosition(100, 100)
       glutCreateWindow("Crazy Eddie's GUI Mk-2 - glut Base Application")
       glutSetCursor(GLUT_CURSOR_NONE)
       
       glutDisplayFunc(self.displayFunc)
       glutReshapeFunc(self.reshapeFunc)
       glutMouseFunc(self.mouseFunc)
       glutMotionFunc(self.mouseMotionFunc)
       glutPassiveMotionFunc(self.mouseMotionFunc)
   
       PyCEGUIOpenGLRenderer.OpenGLRenderer.bootstrapSystem()
       
   def __del__(self):
       PyCEGUIOpenGLRenderer.OpenGLRenderer.destroySystem()
       
   def initialiseResources(self):
       rp = PyCEGUI.System.getSingleton().getResourceProvider()
       rp.setResourceGroupDirectory("schemes", CEGUI_PATH + "/datafiles/schemes")
       rp.setResourceGroupDirectory("imagesets", CEGUI_PATH + "/datafiles/imagesets")
       rp.setResourceGroupDirectory("fonts", CEGUI_PATH + "/datafiles/fonts")
       rp.setResourceGroupDirectory("layouts", CEGUI_PATH + "/datafiles/layouts")
       rp.setResourceGroupDirectory("looknfeels", CEGUI_PATH + "/datafiles/looknfeel")
       rp.setResourceGroupDirectory("schemas", CEGUI_PATH + "/datafiles/xml_schemas")
       
       PyCEGUI.Imageset.setDefaultResourceGroup("imagesets")
       PyCEGUI.Font.setDefaultResourceGroup("fonts")
       PyCEGUI.Scheme.setDefaultResourceGroup("schemes")
       PyCEGUI.WidgetLookManager.setDefaultResourceGroup("looknfeels")
       PyCEGUI.WindowManager.setDefaultResourceGroup("layouts")
       
       parser = PyCEGUI.System.getSingleton().getXMLParser()
       if parser.isPropertyPresent("SchemaDefaultResourceGroup"):
           parser.setProperty("SchemaDefaultResourceGroup", "schemas")     
       
   def setupUI(self):
       PyCEGUI.SchemeManager.getSingleton().create("VanillaSkin.scheme")
       PyCEGUI.SchemeManager.getSingleton().create("TaharezLook.scheme")
       PyCEGUI.System.getSingleton().setDefaultMouseCursor("Vanilla-Images", "MouseArrow")
       root = PyCEGUI.WindowManager.getSingleton().loadWindowLayout("VanillaWindows.layout")
       PyCEGUI.System.getSingleton().setGUISheet(root)
       
       self.wnd = PyCEGUI.WindowManager.getSingleton().createWindow("TaharezLook/FrameWindow", "Demo Window")
       root.addChildWindow(self.wnd)
        
   def run(self):
       self.initialiseResources()
       self.setupUI()
   
       self.lastFrameTime = glutGet(GLUT_ELAPSED_TIME)
       self.updateFPS = 0
       glutMainLoop()
       
   def displayFunc(self):
       thisTime = glutGet(GLUT_ELAPSED_TIME)
       elapsed = (thisTime - self.lastFrameTime) / 1000.0
       self.lastFrameTime = thisTime
       self.updateFPS = self.updateFPS - elapsed
       
       PyCEGUI.System.getSingleton().injectTimePulse(elapsed)
       # do rendering for this frame.
       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT)
       PyCEGUI.System.getSingleton().renderGUI()
       glutPostRedisplay()
       glutSwapBuffers()
   
   def reshapeFunc(self, width, height):
       glViewport(0, 0, width, height)
       glMatrixMode(GL_PROJECTION)
       glLoadIdentity()
       gluPerspective(60.0, width / height, 1.0, 50.0)
       glMatrixMode(GL_MODELVIEW)
       PyCEGUI.System.getSingleton().notifyDisplaySizeChanged(PyCEGUI.Size(width, height))
       
   def mouseFunc(self, button, state, x, y):
       if button == GLUT_LEFT_BUTTON:
           if state == GLUT_UP:
               PyCEGUI.System.getSingleton().injectMouseButtonUp(PyCEGUI.LeftButton)
           else:
               PyCEGUI.System.getSingleton().injectMouseButtonDown(PyCEGUI.LeftButton)
               
       elif button == GLUT_RIGHT_BUTTON:
           if state == GLUT_UP:
               PyCEGUI.System.getSingleton().injectMouseButtonUp(PyCEGUI.RightButton)
           else:
               PyCEGUI.System.getSingleton().injectMouseButtonDown(PyCEGUI.RightButton)
       
   def mouseMotionFunc(self, x, y):
       PyCEGUI.System.getSingleton().injectMousePosition(x, y)

app = BaseApp() app.run() del app

I must say that this app isn't perfect or complete but it will get you going. You might want to make it exception safe and add keyboard injection. If you do that, please post it back here for others to benefit.

Documentation

Since the API is resembling the C++ version as closely as possible (mainly to avoid confusion and as easy switch between C++ and Python as possible), doxygen API docs apply for most of the classes. Doxygen comments are also extracted and added to __doc__ of all the python classes and methods, using python docstrings is therefore possible but not perfect.

Subscriptions

One major thing that isn't documented in doxygen or other wiki docs and that is important in python is how subscriptions work.

Subscribing to listenerClass: wnd.subscribeEvent(PyCEGUI.Window.EventMouseEnters, listenerClass, "methodInThatClass")

Subscribing to a free function: wnd.subscribeEvent(PyCEGUI.Window.EventMouseEnters, PythonModule.freeFunction)

Implementation

We use py++ and boost::python. It allows rapid development of the bindings and a very easy maintenance. It may be a bit slower than SWIG and definitely slower than hand written bindings but since both of these won't happen, please don't cry about that fact (unless you are willing to create and maintain those). The slowness is very unlikely to be noticeable at all unless you do synthetic tests.

Caveats

C++ allows you to shoot yourself in a lot of ways whilst python is very limited in this area. For example if you create a window, get it and store the reference in python variable, then destroy the window, the python variable will hold a dangling pointer. This is something you have to take care of unfortunately, there is no way for us to handle this unless we affect the C++ interface which would hinder performance for C++ users. It's not that hard to handle this and unless you do something really unusual, you probably aren't going to trigger it.