Low-level C++ API bindings

Low level module containing Sip bindings of Anatomist C++ API.

Introduction

This module is mostly bindings to the C++ library of Anatomist. A few classes have been slightly modified or rewritten, either to hide garbage or to appear more pythonic.

For typical use, this module will not be called directly but throught general API. But it can be needed for using advanced features.

The main entry point is the Anatomist class which must be instantiated before any operation can be performed.

>>> import anatomist.cpp as anatomist
>>> a = anatomist.Anatomist()

Note that instantiating Anatomist also instantiates the Qt QApplication, but does not run the Qt event loop, so python interactive shells are still accessible after that operation, but the GUI is frozen until the event loop is executed, using the following PyQt code:

>>> # from PyQt4 import Qt
>>> # or, to switch to the correct Qt implementation and bindings (Qt4/Qt5)
>>> from soma.qt_gui.qt_backend import Qt
>>> Qt.qApp.exec_loop()

but then the loop does not return until the GUI application is over, so you should start it at the end of your code.

Another comfortable alternative to the GUI loop problem in interactive python shells is to use IPython with the option --gui=qt: IPython is an interactive python shell with many improvements, and which can run in a different thread than the GUI, so that the GUI can run without hanging the shell.

Contrarily to the Qt application, Anatomist can be instantiated multiple times (it is not a singleton, but contains a singleton).

In addition to providing bindings to the Anatomist C++ API, the anatomist module also loads some python modules in anatomist: every python module found in the following locations are loaded:

os.path.join(str(anatomist.Anatomist().anatomistSharedPath()), 'python_plugins')
os.path.join(str(anatomist.Anatomist().anatomistHomePath()), 'python_plugins')

Main concepts

There are many pieces in Anatomist library, but a few are really needed to begin with.

  • the Anatomist application, the L{Anatomist} class instance, is responsible for many global variables, the application state, and the startup procedure (including plugins loading). The application is used internally in many functions.
  • objects: AObject class and subclasses
  • windows: AWindow class and subclasses
  • the commands processor: a singleton Processor instance obtained via the application: Anatomist.theProcessor(). The processor is a commands interpreter for the rudimentary language of Anatomist. It is also responsible of saving every command executed in the history file $HOME/.anatomist/history.ana so most of the session can be replayed. Many operations are done via commands and the processor: creating windows, loading objects, displaying them, etc. so this element is probably the most important part of Anatomist.
  • conversions between AIMS object and Anatomist objects: AObjectConverter is here to perform this task.
class anatomist.cpp.AObjectConverter

Aims / Anatomist objects converter

Only two static methods are useful in this class:

  • AObjectConverter.anatomist() converts an AIMS (or possibly other) object into an Anatomist object (AObject subclass), if possible
  • AObjectConverter.aims() converts an Anatomist object to an AIMS object, if possible

Conversions are generally done by wrapping or embedding, or by extracting a reference to an internal object, so objects data are genrally shared between the AIMS and the Anatomist objects layers. This allows internal modification of Anatomist objects data.

After a modification through the Aims (lower level) layer API, modification notification must be issued to the Anatomist layer to update the display of the object in Anatomist. This is generally done via the AObject.setChanged() and AObject.notifyObservers() methods.

class anatomist.cpp.Anatomist(*args)[source]

Anatomist class: entry point to anatomist

class anatomist.cpp.Command

Commands are the execution units of the Processor.

Commands are used as subclasses of Command. They can be built either on the fly by the programmer, or using the commands factory via the Processor.execute() function.

Never call Command.execute() or Command.doit() directly: only the processor will do so. Once built, a command must always be executed by the processor:

>>> a = anatomist.Anatomist()
>>> c = anatomist.CreateWindowCommand('Axial')
>>> a.theProcessor().execute(c)

But in any case there is a more handy shortcut in the higher-level API: anatomist.base.Anatomist.execute()

Comamnds can be subclassed in Python. To be valid, a new command must define at least the name() and doit() methods. doit() will actually do the execution and your program may make it do anything. Later, read() and write() should also be redefined to allow proper IO for this command (when the commands IO and factory are exported from C++ to python).

Command.doit() receives no parameters (apart from self). All execution arguments must be set in the command itself upon construction (either by the constructor or by setting some instance variables).

One important parameter which a command would often use is the CommandContext, which specifies some IO streams for output printing and communication with external programs, and an identifiers set used to name and identify objects (in a general meaning: including windows etc.) through this IO streams.