To clean up the internals, were going to go crazy on the object-oriented front, and try to keep the core functionality free of any dependencies other than the Python Standard Library, Numpy, Scipy, and PyYAML.
To make a responsive user interface in parallel with data processing and possible GUIs, we’ll use Python’s multiprocessing module. This module is new in Python 2.6, but 2.6 usage is becoming fairly widespread. Certainly more widespread than any alternative queue module that I know of. Since we’re leveraging the power of the standard library, we use configparser for the config files.
On the testing side, the need to stick to the standard library relaxes (developers can install extra packages), so we can use nose. See the Testing section for more information.
Hooke aims to be easily modified and extended by new recruits. To make this easier, we try to abide by several programming practices.
Hooke’s main entry point is Hooke. Hooke reads in the configuration files and loads Plugins and Drivers. Then it forks off a CommandEngine instance to execute Commands, and a UserInterface instance to connect the CommandEngine with the user. The CommandEngine runs in a subprocess, which allows command execution to occur in parallel with UserInterface interaction. The two processes communicate via two multiprocessing.Queues.
There are a number of special classes availiable to structure queue communications. See interaction and CommandExit for details.
Plugins contain bundles of Commands, representing the various operations a user can carry out through the Hooke interface.
Plugins can depend on other Plugins, so you shouldn’t need to repeat code. One central Plugin can provide useful functionality to several dependent Plugins.
There is a Plugin subtype Builtin which is just like a Plugin, but is considered fundamental enough to not be optional. Builtins are always loaded.
Commands specify user-actions in an interface-agnostic manner. This makes writing UserInterfaces easier, because you don’t need to know anything about particular Plugins or Commands, you just need to be able to explain the base classes for you user and then speak the language of interaction and CommandExit with the CommandEngine process.
Force spectroscopy experiments come in several major flavors. Each flavor gets its own subclass of Experiment in experiment. For example, force clamp experiments are ForceClamp. This gives Drivers a way to tag experimental data so Commands know what they are working with.
Experiments tags need a data-holding container to tag, and Curves are that container. Each Curve can hole several blocks of Data (for example approach and retract curves in a VelocityClamp experiment would be seperate blocks). Curves also have an info attribute for persistently storing arbitrary data.
Normally you’ll want to analyze multiple Curves in one session. Playlists provide a convenient container for Curves, and the subclass FilePlaylist add persistant file backing (save, load, etc.).
There are a number of general coding features we need for Hooke that are not provided by Python’s standard library. We factor those features out into util.
There are also a number of features who’s standard library support changes with different versions of Python. compat provides a uniform interface to those tools so that Hooke will work with several Python versions.
The hooke.curve.Data blocks store data in various states of processing. For example, velocity clamp drivers will load two columns of data: z piezo (m) and deflection (m) (all column names have the name (unit) format, see split_data_label()). Most data processing consists of manipulating current block information to create additional data columns in the same block. For example, SurfaceContactCommand usually uses the z piezo (m) and deflection (m) columns to add surface distance (m) and surface adjusted deflection (m) columns. However, you might want to use e.g. median filtered deflection (m) instead of deflection (m). Because of this, analysis plugins should use Arguments rather than hard-coding source or target column or info dict names. See SurfaceContactCommand for an example of the recommended approach.
Also keep in mind that some analyses will not generate columns that are the same size as the source data (e.g. FlatPeaksCommand and PowerSpectrumCommand). These commands will either stash information in the Data‘s .info dictionary (e.g. a list of peaks) and/or add new Data blocks to the parent Curve. The main reason for adding new blocks rather than storing all the data in .info is to take advantage of built in Data processing Commands. For example, the power spectrum from PowerSpectrumCommand can be easily exported to a text file. However, extra care must be taken to avoid name collisions or ambiguity, since the output blocks must be unique on a Curve-wide level, while Data.info output need only be unique on a Data-wide level.
hooke.ui.gui contains enough code that you might want a word about its organization before diving into the code. Information flows like this:
With the following naming scheme in HookeFrame:
callbacks _on_* response processors _postprocess_*