Pygwy - Python binding for Gwyddion
-----------------------------------

Requirements
============
Python >= 2.4
Pygtk >= 2.10, PyGObject >= 2.12, PyCairo >= 1.2: http://www.pygtk.org/

Compilation
===========
By configure script:
  ./configure --enable-pygwy

Script location
================
The Python modules are stored in a Gwyddion user directory (~/.gwyddion/pygwy
or %USER_DIRECTORY%/gwyddion/pygwy). The 'pygwy' directory is automatically
created when it does not exist.

Documentation and API can be generated by using command 'make pygwy-doc' from
current dictionary. Epydoc (http://epydoc.sourceforge.net/) is required for
creating documentation.

Currently there are two types of Python modules:

Process module
==============
Process module is used to manipulate with datafields. Only one function
must be defined:
- run()
  return value: not important
The process module can use predefined variable gwy.data which contains
the current data container.


File module
===========
File module is used to import and export data. Complete module implements
following functions:
- detect_by_name(filename)
  filename: string containing full path to file
  return value: return score <0,100> where value 0 reflects unknown type,
                value 100 reflects known type

- detect_by_content(filename, head, tail, filesize)
  filename: string containing full path to file)
  head: string containing few bytes from beginning of the file
  tail: string containing few bytes from end of the file
  filesize: lenght of file in bytes
  return value: return score <0,100> where value 0 reflects unknown type,
                value 100 reflects known type

- load(filename)
  filename: string containing full path to file
  return value: container of imported data

- save(data, filename)
  data: container of data used for export
  filename: full path to output file
  return value: True when export is successful, False otherwise


Volume module
==============
Volume module is used to manipulate with bricks. Only one function
must be defined:
- run()
  return value: not important
The process module can use predefined variable gwy.data which contains
the current data container.

Module variables
================
Every module must have defined variables which are used to determine
the module type, location in menu and description.

Required variables for file type module (example):
plugin_type = "FILE"
plugin_desc = "High definition stable format store (.hdsf)"

Required variables for process type module (example):
plugin_menu = "/Poodle" # the plugin will be located in Data Process/Poodle
plugin_type = "PROCESS"

Required variables for volume type module (example):
plugin_menu = "/Poodle" # the plugin will be located in Volume Data/Poodle
plugin_type = "VOLUME"

Module debuging
===============
Currently there is not much options to perform module debugging.
Only Python exceptions are written to standard output.

Example process module
======================
import gwy

plugin_menu = "/Correct Data/Invert"
plugin_type = "PROCESS"

def run():
   # create undo point
   key = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD_KEY)
   gwy.gwy_app_undo_qcheckpointv(gwy.data, key)

   # get current datafield
   d = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)

   # call invert function
   d.invert(0, 0, 1)

   # report data change to Gwyddion
   d.data_changed()

Example file module
===================
import gwy, sys

plugin_type = "FILE"
plugin_desc = "High definition stable format store (.hdsf)"

def detect_by_name(filename):
   if (filename.endswith(".hdsf")):
      return 100
   else:
      return 0

def detect_by_content(filename, head, tail, filesize):
   if (head.startswith("HDSF:")):
      return 100
   else:
      return 0

def load(filename):
   c = gwy.Container()
   d = gwy.DataField(100, 100, 100, 100, 1)
   for i in range(100):
      for j in range(100):
         d.set_val(i, j, i) # draws linear gradient
   c.set_object_by_name("/0/data", d)
   return c

def save(data, filename):
   f = open(filename, "w")
   datafield_num = 1
   for key in data.keys():
      if isinstance(data.get_object(key), gwy.DataField):
         d = data.get_object(key)
         f.write("Datafield "+ str(datafield_num) + '\n')
         datafield_num += 1
         for row in range(d.get_yres()):
            for col in range(d.get_xres()):
               f.write(str(d.get_val(col, row))+'\n')
         f.close()
   return True

Example for batch processing
============================
import gwy, os

plugin_type = "process"
plugin_menu = /Resample current directory

def run():
   # get list of current directory
   for filename in os.listdir("."):
      if filename.endswith(".gwy"):
         # load file of given name and save it to container
         container = gwy.gwy_app_file_load(filename)
         # iterate thru loaded file's container
         for key in container.keys_by_name():
            # check if object of key is datafield
            if key.endswith("data"):
               # get datafield
               d = container.get_object_by_name(key)
               # scale datafield by 2 using linear interpolation
               d.resample(d.get_xres()*2, d.get_yres()*2, gwy.INTERPOLATION_LINEAR)
               # send message to Gwyddion to notice the content of datafield has changed
               d.data_changed()
         new_filename = filename.rsplit('.', 1)[0] + "-scaled.gwy"
         # write current file to filename
         gwy.gwy_app_file_write(container, new_filename)


Bending C API to work in Python
===============================

1. Low-level: Writing a C method manually in pygwy.override.

   We can do anything here, add new operators and protocols (using
   override-slot) or just plain methods and functions.  This file is passed to
   pygwy-codegen.py as --override pygwy.override and the code there essentially
   gets directly included in pygwywrap.c, the generated Python module code.

   The syntax is hairy but you can read __parse_override() in
   /usr/share/pygobject/2.0/codegen/override.py for some hints (and the actual
   parsing code).

   Plain functions and methods are called _wrap_THE_REAL_NAME()

2. Code generation level: pygwy-codegen.py.

   Here we mostly define how to handle various argument types.  This may
   include some plain types but is especially suited for handling types
   introduced for wrapping: GwyDoubleOutArg for double out-args or
   GwyDoubleArray for sequences of doubles.  These types of course only
   originate from wrap_calls or fix-defs (below).

   We save work when adapting functions by defining type substitutions
   and then putting the generic code than handles them here.  It will
   automatically appear in methods.

3. Modifying the .defs file: in extra.defs and pygwy-fix-defs-{1,2}.py.

   Extra definition in extra.defs are simply included in pygwy.defs.  Some
   things, such as boxed types, are easier to just write directly here.  For
   some reason boxed types need an explicit definition they are never
   automatically picked up.

   Here we rewrite argument types and add attributes to arguments or functions.
   Arguments and functions can be skipped here or attain default values.

   In addition, pygwy-fix-defs-2.py generates some direct code into
   boxed.override defining setters for plain C structs.  There may be some
   reasonable way of teaching codegen to do this automatically by patching
   Wrapper.write_getsets() but this may be too fragile.

3. Wrapping functions by thin python-friendly wrappers in wrap_calls.c and
   wrap_calls.h.  Also macro-prototypes.h belongs here – the header provides
   prototypes for macros so that the parser knows how to wrap them.

   If the friendly function does no harm in the C API it can be just added
   there.  So this is mostly used for arrays, for which a thin wrapper
   takes/passes a GArray-derived type instead of plain C array.  Then
   pygwy-codegen.py can automatically create the corresponding code.

There are four major classes of things in the C API that do not translate
easily into Python:

A) Plain structs and pointer types.  We handle them by boxing them in C.
   Explicit boxed type record in extra.defs is necessary.  Setters for some
   field types are directly generated by pygwy-fix-defs-2.py, other have to
   be explicitly defined in pygwy.override.

B) Multiple return values, passed as pointers to things-to-fill.  This is now
   relatively simple.  We rewrite them to GwyWhateverOutArg in
   pygwy-fix-defs-2.py and the function writer in pygwy-codegen.py knows how
   to sort arguments to input and output and write a function that returns a
   tuple if necessary.

C) Optional outputs.  Ugly.  Since all arguments are in-args and things like
   arrays are passed by value (copy) this gets complicated.  At present if the
   C function can produce something it will always do so in Python.

D) C arrays.  The C code has always ways to figure out the array size.  Python
   does not what they are.  So the solution is, universally, add a wrap_calls
   function that has GArray-derived arguments/return values.  Fortunately, the
   wrap_calls functions tends to have just a few lines and can be written
   mechanically (by a human).

E) Allocate-if-necessary return values (value formats, sometimes units and
   other things).  These we convert to always-allocate functions, usually by
   a simple skip_args list entry that removes the possibility to pass an
   already allocated thing to the function.
