******* Hacking ******* .. toctree:: :maxdepth: 2 testing Dependencies ============ 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_. .. _Python Standard Library: http://docs.python.org/library/ .. _Numpy: http://numpy.scipy.org/ .. _Scipy: http://www.scipy.org/ .. _PyYAML: http://pyyaml.org/ 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. .. _multiprocessing: http://docs.python.org/dev/library/multiprocessing.html .. _configparser: http://docs.python.org/library/configparser.html 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 :doc:`testing` section for more information. .. _nose: http://somethingaboutorange.com/mrl/projects/nose/0.11.3/ Principles ========== Hooke aims to be easily modified and extended by new recruits. To make this easier, we try to abide by several programming practices. * `DRY`_ (Don't Repeat Yourself), also phrased as "Every piece of knowledge must have a single, unambiguous, authoritative representation within a system." * `LoD`_ (Law of Demeter): Don't reach through layers, e.g. `a.b.c.d()`. .. _DRY: http://en.wikipedia.org/wiki/Don%27t_repeat_yourself .. _LoD: http://en.wikipedia.org/wiki/Law_of_Demeter Architecture ============ Hooke's main entry point is :class:`~hooke.hooke.Hooke`. :class:`~hooke.hooke.Hooke` reads in the configuration files and loads Plugins_ and Drivers_. Then it forks off a :class:`~hooke.engine.CommandEngine` instance to execute Commands_, and a :class:`~hooke.ui.UserInterface` instance to connect the :class:`~hooke.engine.CommandEngine` with the user. The :class:`~hooke.engine.CommandEngine` runs in a subprocess, which allows command execution to occur in parallel with :class:`~hooke.ui.UserInterface` interaction. The two processes communicate via two :class:`multiprocessing.Queue`\s. There are a number of special classes availiable to structure queue communications. See :mod:`~hooke.interaction` and :class:`~hooke.command.CommandExit` for details. Plugins ------- :class:`~hooke.plugin.Plugin`\s contain bundles of Commands_, representing the various operations a user can carry out through the Hooke interface. :class:`~hooke.plugin.Plugin`\s can depend on other :class:`~hooke.plugin.Plugin`\s, so you shouldn't need to repeat code. One central :class:`~hooke.plugin.Plugin` can provide useful functionality to several dependent :class:`~hooke.plugin.Plugin`\s. There is a :class:`~hooke.plugin.Plugin` subtype :class:`~hooke.plugin.Builtin` which is just like a :class:`~hooke.plugin.Plugin`, but is considered fundamental enough to not be optional. :class:`~hooke.plugin.Builtin`\s are always loaded. Commands ~~~~~~~~ :class:`~hooke.command.Command`\s specify user-actions in an interface-agnostic manner. This makes writing :class:`~hooke.ui.UserInterface`\s easier, because you don't need to know anything about particular :class:`~hooke.plugin.Plugin`\s or :class:`~hooke.command.Command`\s, you just need to be able to explain the base classes for you user and then speak the language of :mod:`~hooke.interaction` and :class:`~hooke.command.CommandExit` with the :class:`~hooke.engine.CommandEngine` process. Drivers ------- :class:`~hooke.driver.Driver`\s are responsible for reading assorted data files into Hooke's Data_ structure. Data ---- Experiments ~~~~~~~~~~~ Force spectroscopy experiments come in several major flavors. Each flavor gets its own subclass of :class:`~hooke.experiment.Experiment` in :mod:`~hooke.experiment`. For example, force clamp experiments are :class:`~hooke.experiment.ForceClamp`. This gives Drivers_ a way to tag experimental data so Commands_ know what they are working with. Curves ~~~~~~ Experiments_ tags need a data-holding container to tag, and :class:`~hooke.curve.Curve`\s are that container. Each :class:`~hooke.curve.Curve` can hole several blocks of :class:`~hooke.curve.Data` (for example approach and retract curves in a :class:`~hooke.experiment.VelocityClamp` experiment would be seperate blocks). :class:`~hooke.curve.Curve`\s also have an :attr:`~hooke.curve.Curve.info` attribute for persistently storing arbitrary data. Playlists ~~~~~~~~~ Normally you'll want to analyze multiple Curves_ in one session. :class:`~hooke.playlist.Playlist`\s provide a convenient container for Curves_, and the subclass :class:`~hooke.playlist.FilePlaylist` add persistant file backing (save, load, etc.). Utilities --------- 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 :mod:`~hooke.util`. There are also a number of features who's standard library support changes with different versions of Python. :mod:`~hooke.compat` provides a uniform interface to those tools so that Hooke will work with several Python versions. Analysis -------- The :class:`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 :func:`~hooke.util.si.split_data_label`). Most data processing consists of manipulating current block information to create additional data columns in the same block. For example, :class:`~hooke.plugin.vclamp.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 :class:`~hooke.command.Argument`\s rather than hard-coding source or target column or info dict names. See :class:`~hooke.plugin.vclamp.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. :class:`~hooke.plugin.flatfilt.FlatPeaksCommand` and :class:`~hooke.plugin.curve.PowerSpectrumCommand`). These commands will either stash information in the :class:`~hooke.curve.Data`'s `.info` dictionary (e.g. a list of peaks) and/or add new :class:`~hooke.curve.Data` blocks to the parent :class:`~hooke.curve.Curve`. The main reason for adding new blocks rather than storing all the data in `.info` is to take advantage of built in :class:`~hooke.curve.Data` processing :class:`~hooke.command.Command`\s. For example, the power spectrum from :class:`~hooke.plugin.curve.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 :class:`~hooke.curve.Curve`-wide level, while `Data.info` output need only be unique on a :class:`~hooke.curve.Data`-wide level. GUI --- :mod:`hooke.ui.gui` contains enough code that you might want a word about its organization before diving into the code. Information flows like this: .. image:: img/gui_flow/gui_flow.svg With the following naming scheme in HookeFrame: =================== ================== callbacks ``_on_*`` response processors ``_postprocess_*`` =================== ==================