Since the release of CEGUI 0.7.5, official Python bindings have been provided, primarily prompted by the fact that the new CEGUI tools are written in Python. A beneficial side effect of this is that the bindings are free for anyone to use in their Python, or embedded Python, applications.
- Windows: PyPi repository
- For other platforms, download the SDK and use the bindings contained within.
- If you use embedded Python, it may be easier to use the Win32 bindings from the SDK.
The PyCEGUI API resembles the C++ version as closely as possible - mainly to avoid confusion and to provide familiarity between the two. The doxygen API docs (found here) apply for most of the classes; additionally, doxygen comments are extracted and added as docstrings to all Python objects. This means that using Python docstrings is possible, although not perfect.
PyCEGUI uses Py++ and Boost.Python - this allows rapid development of the bindings and easy maintenance. It may be a bit slower than SWIG, and certainly slower than hand written bindings; but, since neither of those will happen, be content with what is provided. The slowness is very unlikely to be noticeable at all unless you do synthetic tests.
One major thing that isn't documented in doxygen or other wiki docs and that is important is how subscriptions work in PyCEGUI.
Subscribing to listenerClass:
wnd.subscribeEvent(PyCEGUI.Window.EventMouseEnters, listenerClass, 'methodInThatClass')
Subscribing to a free function:
Future release (0.8+)
TODO: Resolve repetition.
In the future, the signatures have been changed to be much more flexible and Pythonic. 'subscribeEvent' now takes any callable object (bound member function, free function, lambda, functor, etc). Moving from the old syntax to the new syntax, the following
wnd.subscribeEvent(PyCEGUI.Window.EventMouseEnters, instance, "someMethod")
Note: Fixed; pending release (0.8+)
C++, like C, gives the programmer a pretty free hand in allocating memory, passing it around, and possibly forgetting that it ever exists; while certainly not the recommended way of doing things, it is possible. Python, however, is a different creature altogether - by default, it has a garbage collection system which is implemented via reference counting (the specifics of this are not important, and left as an exercise to the reader if they are that interested). Basically, what this means, is that when an object is no longer needed, it is deleted.
This can play havoc with a C++ binding, if not considered carefully. For example, if a Python application has an object it wishes to let PyCEGUI (and, by association, C++ CEGUI) know about, it must take care to remember that Python is tracking the number of times the object in question has been referenced, but C++ is not. Thus, the conflict here is that Python will destroy (or garbage collect, if you prefer) the object in question, while C++ will be none the wiser; the inevitable conclusion here is an invalid pointer, segmentation fault, or other equally nasty bug.
Let us illustrate just exactly what we are talking about here; consider the following:
def outsideContext(argListbox): item1 = PyCEGUI.ListboxTextItem('item1') item2 = PyCEGUI.ListboxTextItem('item2') argListbox.addItem(item1) argListbox.addItem(item2) return def main(): theListbox = PyCEGUI.WindowManager.getSingleton().createWindow('TaharezLook/Listbox', 'theListbox') outsideContext(theListbox) return if __name__ == '__main__': sys.exit(main())
In the function `outsideContext`, two items are created in that scope (and registered with the Python garbage collection system), and then added to a Listbox; when the function is finished, Python is under the impression that it no longer needs the items and so it destroys the objects. Under the hood, however, pointers have been arranged that refer to these two items; in the future, when the Listbox tries to manipulate the items or when the Listbox itself is garbage collected, segmentation faults will more than likely occur.
One solution to this issue, if possible, is to bind the items to an object - preferably, the one which the Listbox is bound to, so that when the Listbox itself is no longer needed, the items will be cleaned up at the same time. Such a strategy might go as follows:
class someObject(object): def __init__(self): super(Object, self).__init__() self.memory =  return def anInitializationMethod(self): self.listbox = PyCEGUI.WindowManager.getSingleton().createWindow('TaharezLook/Listbox', 'someListbox') return def anotherMethod(self, stringList): for s in stringList: buffer = PyCEGUI.ListboxTextItem(s) self.memory.append(buffer) self.listbox.addItem(buffer) return
The key point here is to have Python keep the object alive until the Listbox in question is no longer needed; the implementation of it is irrelevant.
Another issue is almost the opposite of what was outlined previously - that is to say, PyCEGUI objects can be destroyed and Python will still think they are valid (in some sense, they are - just not functional). An example of this is creating a window, storing it in a variable, then destroying the window; a code snippet:
rootWindow = PyCEGUI.WindowManager.getSingleton().createWindow('DefaultWindow', 'root') PyCEGUI.System.getSingleton().setGUISheet(rootWindow) someWindow = PyCEGUI.WindowManager.getSingleton().createWindow('TaharezLook/FrameWindow', 'someWindow') rootWindow.addChildWindow(someWindow) # ... things happen ... PyCEGUI.WindowManager.getSingleton().destroyWindow(someWindow) PyCEGUI.WindowManager.getSingleton().cleanDeadPool() # Death # - Any functional method will crash the program; the choice of `disable` is totally arbitrary. someWindow.disable()
This is a contrived example, and most programs will have some form of logic that will prevent this, but it is worth noting.
morgul has integrated CEGUI with Panda via these bindings.
OpenGL driver bugs
If an Intel 915GM, 910GML, or similar chipset is used, it might be necessary to create/destroy a window before the display mode is initialized via OpenGL. The following illustrates:
glutInit() # This will segfault the application glutInitDisplayMode(0)
Instead, the following can be done to circumvent this problem:
glutInit() # Kludge glutDestroyWindow(glutCreateWindow('')) # This will no longer segfault the application glutInitDisplayMode(0)
Additional complications may arise via this chipset, which are outside the scope of [Py]CEGUI to deal with.
- A demonstration of a basic application using PyCEGUI and OpenGL can be found here.
- A simple modification, which adds rudimentary keyboard support, can be found here.
- A further modification, which adds special keyboard support, can be found here.
- A basic demonstration of subscribing to events can be found here.
- A further demonstration of event subscriptions can be found here.
Application from scratch
- See here.