Important Change

As of revision number 1794 of July '09, the environment variables for building the python bindings have changed. Please read the linked section.

Background

The Syzygy Python bindings are built with SIP. We used to use SWIG, but SIP generates C++ code that's much easier to read, debug, and maintain, and in many instances it also runs faster.

On the minus side, the new bindings can only be compiled at the moment with g++, which means MinGW on Win32. They've been built using SIP 4.7.3, 4.12.2, and 4.13.2 (4.10, on the other hand, has a bug that prevents the build from working); MinGW g++ 3.4.2 and 4.5.2 (and various other versions on Linux); and Python 2.4-2.7.

Building the Bindings

  1. Get a reasonably recent version of Python. If you are using the Cygwin command shell on windows, make sure that it does not include its own version of Python. You must remove it if it exists, since cygwin's Python and Syzygy use incompatible object-file formats (cygwin gcc vs. Visual C++). Install the native Windows version of Python from python.org.

  2. Set the environment variables SZGHOME, SZGBIN, SZGEXTERNAL, PATH, and any dynamic linker variables as described in Getting the Software. The directory containing the Syzygy executables and libraries must be on your PATH and on the dynamic linker search path.

  3. Set the SZG_PYINCLUDE environment variable to the directory containing the Python.h header.

    • Windows: If you e.g. installed Python 2.4 in c:\Python24 (the default), this would be C:/Python24/include (yes, forward slashes).

    • Linux: This would typically be something like /usr/include/python2.5.

  4. Set the SIP_INCLUDE environment variable to the location of the sip.h header.

  5. If you're on Linux or MacOS or using MinGW g++ on Windows, set the SZG_PYLIB environment variable. On Linux and MacOS, this is the word 'python' followed by the Python version number with a decimal point, e.g. python2.5; on Windows it should not include the decimal point, e.g. python24. I know, it's ugly. This is the value for the -l linker argument.

  6. On Windows set the SZG_PYLIB_PATH environment variable to the location of the Python link library: For example, with Python 2.4 in C:\python24 this would be C:/Python24/libs/python24.lib (also forward slashes).

  7. Check that the directory containing the sip executable is on your command search path and add its directory if it isn't already there.

  8. cd to szg/python_sip and type make. make clean cleans (deletes) things up. A number of files are generated/moved.

    1. SIP creates the file python_sip/build/<platform>/sip_szgpart0.cpp containing the C++ source for the bindings.

    2. This gets compiled to _szg.pyd (Windows) or _szg.so (Unix). This file and python_sip/src/szg.py are copied to $SZGBIN.

Before Using the Bindings

...You'll also need to install the Python OpenGL bindings package, PyOpenGL. We've tried PyOpenGL 2.0.1.09 and 3.0.1. For old-style OpenGL programs PyOpenGL 2 is much faster (follow the "View older releases" link on the SourceForge download page).

Figuring Out How to Write Syzygy Python Programs

Um. Sorry, no Python API reference yet. Working on it.

There are a few demos in szg/python_sip/demos.

You can look at a webpage on Syzygy Python Programming. It's a bit out of date, e.g. referring to the old PySZG module instead szg, but mostly still accurate; eventually it'll be integrated with the Syzygy documentation. Refer to the corresponding programs in python_sip/demos/ to see how the new module works.

The only way to figure out what objects and methods exist is to look at the .sip source files in szg/python_sip/src. They're very similar to C++ header files, with some additional annotations. One thing to note, if a method parameter has the /Out/ annotation, it will actually be returned by the Python wrapper method. For example, the arSZGClient object has a method called getAttributeVector3() that returns a Syzygy database parameter as a 3-element vector. The entry in the file szg/python_sip/src/szgclient.sip looks like this:

  bool getAttributeVector3( const string& groupName,
                            const string& parameterName,
                            arVector3& value /Out/ );

This means that the Python method should be called e.g. like this:

  (statusBool, value) = szgClient.getAttributeVector3( 'SZG_HEAD', 'mid_eye_offset' )

Running Syzygy Python Programs in Standalone Mode

Besides looking in a standard location for imported modules (e.g. Python24\lib\site-packages\ on Windows), Python will search for modules in directories listed in the PYTHONPATH environment variable. You must set this variable to the path to the directory containing szg.py and _szg.pyd (or add the path to the variable if it already exists). Of course, it will also look in the same directory as a program for modules imported by that particular program.

We suggest putting data, textures, and sounds used by an application in the same directory (or a subdirectory) and opening them using relative paths.

Running Syzygy Python Programs in Cluster Mode

To understand this section, you should read about the Syzygy Cluster Mode.

Telling szgd Where Python Is

The preferred way to do this is to set the Syzygy database variable SZG_PYTHON/executable to the full path to the python executable, e.g. on Windows it might be C:\Python24\python (omit the '.exe') and on Linux it could be the output of the which python command. It must begin with one of the base paths passed as command-line arguments to szgd (we suggest including the entire path to Python in the base paths szgd argument, minus the '.exe' on Windows).

Telling szgd Where Your Program Is

There is a Syzygy database variable SZG_PYTHON/path that should be set to a semicolon-delimited list of absolute paths to directories. Each of these paths must begin with one of the base paths passed as command-line arguments to szgd. szgd will search for your program in each directory in this list and in all directories each one contains. This search only goes one level down the directory tree, i.e. it doesn't include subdirectories of subdirectories of directories in the list. This allows you to place each program in a separate directory together with its data, without having to modify SZG_PYTHON/path for each new program.

The Python Module Import Path

As mentioned above, Python uses the PYTHONPATH environment variable as a search path for imported modules. The following Syzygy database variables are dynamically prepended to PYTHONPATH (i.e. their current values are prepended when a program is launched, then removed): SZG_PYTHON/path, SZG_PYTHON/lib_path, and SZG_EXEC/path. These paths will be searched in the order given. Note that only the directories listed are searched by Python, not subdirectories.

Where to Put Python Programs' Data, Sounds, etc.

This depends on the type of program. For master/slave programs, everything but sound files are read by the program itself in Cluster Mode; sound files need to be read by the SoundRender program. When szgd launches a program, it sets the current directory to the one containing the program. This means that if you put texture maps, .obj 3-D models, and other kinds of data in the same directory as your program or a subdirectory thereof, as suggested above for Standalone Mode, you're good to go.

For sound files, you need to either (1) Copy them into a directory that's listed in the Syzygy database variable SZG_SOUND/path, or (2) Tell SoundRender where to find them by calling the framework method setDataBundlePath() as described in the Programming chapter.

Using ``dex`` With Python Programs

szgd identifies Python programs by suffix and invokes the Python interpreter. All Python programs must have the suffix '.py'. To run a Python program named 'foo.py' with command-line arguments <args> on a virtual computer named `vc`, type:

  dex vc foo.py <args>

Gotchas

You should be very careful to put the line

  from szg import *

before any imports of PyOpenGL modules in your Python code. This is because the PyOpenGL modules try to load the GLUT dynamic library themselves from their local installation and this may conflict with the version of GLUT linked to Syzygy.

The Python cPickle module (for persistent storage of Python objects, either files or in strings) seems to be less cross-platform than advertised. Actually, we're not sure if this is a platform or a version issue; a file generated with cPickle on a Windows machine running Python 2.2 can't be un-pickled on a Linux machine running Python 2.3. The Python master/slave framework uses cPickle in the setObject method, for packing an arbitrary Python object into a string for transfer from master to slaves, so this implies that Python master/slave applications may run into trouble running on mixed-platform or -version clusters. We've run into a few other cases in which cPickle behaves strangely, throwing exceptions for no apparent reason; some of these can be avoided by using the slower pickle module instead (pickle is written in Python, cPickle in C). Or if security is a concern, you could used twisted.spread.banana and twisted.spread.jelly from the Twisted package. Pickle is a security risk, because an attacker could insert arbitrary commands in a string that would be executed on un-pickling; only use pickle if you control both ends of a transaction.

Writing Python Input Device Drivers

Python input devices only work in Cluster Mode.

This feature is specific to the new (SIP) bindings. Tersely, you implement a sub-class of arPyDeviceServerFramework, minimally overriding the configureInputNode() method. This method can do a few things:

  1. Install one or more driver objects in the input node (required). In a Python program, these should be either instances of arGenericDriver if you're going to do all of the actual event generation in Python code or arSharedLibInputDriver if you want to load one of the C++ "ar..Driver" shared libaries. If it's an arGenericDriver, you'll want to save a reference to it.

  2. Install an event filter. Only really needed if you're loading C++ shared-libary drivers.

  3. Add a network input source. This is what allows daisy-chaining of input devices to work.

In general you'll also want to have a loop that generates the actual events. Of course, these can be based on any information that Python can access: a real device via serial connection, a GLUT or wxPython GUI, or a web page, to name a few possibilities. There are a few simple examples in szg/python_sip/demo/inputdevices:

  1. randombuttons.py just sends random button events.

  2. bird_and_wand.py loads two C++ dynamic libraries, and is most interesting as an example of how to do e.g. axis scaling and button re-mapping in Python.

  3. glutjoystick.py is a GLUT-based joystick simulator, to demonstrate how to start writing alternatives to the standard inputsimulator program. In extending it you might want to look at PyUI.

  4. event_console.py opens a mini command prompt in which you can type commands to send input events.

To use one in the context of a virtual computer, you generally have to do three things:

  1. Make sure you have defined the Syzygy database variable SZG_PYTHON/executable for the computer it's on.
  2. Place it in a directory on your SZG_PYTHON/path (or one level down, see Path Configuration).
  3. Add it to a virtual computer input map, e.g.:
       virtual_computer SZG_INPUT0  map  this_computer/glutjoystick.py
    

Then dex virtual_computer <app> should cause the Python input device to start on this_computer.