Gwyddion – Free SPM (AFM, SNOM/NSOM, STM, MFM, …) data analysis software

Pygwy tutorial

Warning: Python scripting is described in the user guide.

This is the old pygwy tutorial, currently preserved for reference. Many things written here do not represent the best practice and may be excessively unpythonic, convoluted or inefficient.

Some things written here are plain wrong and do not work.

Pygwy is a Python binding to Gwyddion's objects, methods and functions. It is suitable for writing modules (process and import/export) and simplifying repetitive or complex tasks. This document contains described examples of Pygwy usage.

The main reference of pygwy module is generated from Gwyddion documentation. Although I tried to catch main differences between C and Pygwy documentation, there is still a lot of inaccuracy.

How to make a data processing module

Data processing module is used for manipulation with datafield (an "image"). It may compute some statistic values or alter datafield itself. The processing module is located in an application menu Data Process. Modules can be written in C or Python language. This sectionl covers a creation of data processing module in Python. For creating modules in C please refer to the module tutorial.

Variables plugin_type and plugin_menu must be defined at the beginning of a process module file. They are metadata used for determining module type and location in the application menu. For the data processing module the values are following:

plugin_type = "PROCESS"
plugin_menu= "/path/to/required/(sub)menu"

Then a run function follows. This function is executed everytime the module is invoked from application menu.

def run():
   # main body of module function

Although it has no arguments there is predefined global variable (gwy.data) which contains an active container object. The container may hold a scale of values from object (for example datafields) to strings and numbers. However using the gwy.data variable is not always required.

Following example shows how to create a simple process module which inverts values in active datafield.

Data processing module example

# This is required for usage of pygwy functions
import gwy

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

def run():
   # Create undo point, the detailed explanation of this step is beyond this
   # tutorial. Basically the active container and datafield key are used for
   # storing information to undo stack.
   key = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD_KEY)
   gwy.gwy_app_undo_qcheckpointv(gwy.data, key)

   # Get active datafield and store it to 'd' variable. The variable is object
   # of type gwy.DataField
   d = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)

   # Call invert function (do not invert x and y axes, invert only z axis)
   d.invert(0, 0, 1)

   # Report data change to Gwyddion, this is required to update (redraw)
   # graphical presentation of datafield in application window.
   d.data_changed()


Volume data processing modules

Volume data processing modules are very similar to image data processing modules. They declare plugin_type as VOLUME and they will appear in the Volume toolbox menu:

plugin_type = "VOLUME"
plugin_menu= "/path/to/required/(sub)menu"

def run():
   # main body of module function

How to make an import/export module

File module is used to import and export data. Variables plugin_type and plugin_desc must be defined at the beginning of a import/export module file. They are metadata used for determining module type and description of file format. For the import/export module the values are following:

plugin_type = "FILE"
plugin_desc = "Description of file format (.extension)"
Complete module implements following functions:
detect_by_name(filename)
filename : A string containing full path to file.
Returns: A score <0,100> where value 0 reflects unknown type, value 100 reflects known type
detect_by_content(filename, head, tail, filesize)
filename : A string containing full path to file
head : A string containing few bytes from beginning of the file
tail : A string containing few bytes from end of the file
filesize : A length of file in bytes
Returns: A score <0,100> where value 0 reflects unknown type, value 100 reflects known type
load(filename, mode=None)
filename : A string containing full path to file
mode : The run mode, either gwy.RUN_INTERACTIVE or gwy.RUN_NONINTERACTIVE.
Returns: a new container of imported data.
save(data, filename, mode=None)
data : A container of data used for export
filename : A string containing full path to output file
mode : The run mode, either gwy.RUN_INTERACTIVE or gwy.RUN_NONINTERACTIVE.
Returns: True when export is successful, False otherwise.

Note: the mode parameter for load() and save() is available since version 2.26. To maintain backward compatibility with earlier versions, include the default value None in the function definitions. Scripts written for version 2.25 and earlier that do not define the mode parameter are still supported by version 2.26 and later.

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, mode=None):
   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, mode=None):
   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



Module location

The created module must be stored in $HOME/.gwyddion/pygwy directory. When This location is searched and processed during Gwyddion starts. When content of module file is changed the code is automatically reloaded but when new module file is created it is not loaded until application is restarted.

Using the pygwy console

Pygwy console is special module suitable for batch tasks inside of Gwyddion. It has no predefined variables (like gwy.data in processing module) but it still can control Gwyddion and it is more flexible. The console can be invoked from Data process menu (Data process/Pygwy console) (but some file must be opened before because console is technically process module).

The console looks like simplified text editor. It has main toolbar which serve for basic operation with files (open, save, save as and execute). Below is located editor which contains user-written code. On the bottom of window is textview with output and at the very bottom there is single input line. The console accepts some keyboard shortcuts for more productivity:

Examples of macro-like scripts

Following section contains examples for Pygwy console.

Creating new datafield

Create new datafield with semisphere from active datafield and append it to active file.


# semisphere radius
radius = 100.0
# position in datafield resolution
x0 = 100
y0 = 100
z0 = -30
# scaling factor
factor = 10e-12

# get active datafield
d = gwy.gwy_app_data_browser_get_current(gwy.APP_DATA_FIELD)
# create new datafield by using active datafield and
# set its values to zero
sphere_datafield = d.new_alike(True)
for y in range(d.get_yres()):
  for x in range(d.get_xres()):
    z = pow(radius, 2) - pow(x - x0, 2) - pow(y - y0, 2)
    if (z > 0):
      # set datafield's value
      sphere_datafield.set_val(x, y, (z-z0)*factor)

# get active container
c = gwy.gwy_app_data_browser_get_current(gwy.APP_CONTAINER)
# add sphere datafield to active container
gwy.gwy_app_data_browser_add_data_field(sphere_datafield, c, 3)

Apply gaussian filter to data

Compute root mean square (RMS) of specified number of iteration and step of gauss filter and store them to file.

# Gauss filter size
size = 1.0
number_of_iteration = 21

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

# filename for storing statistical values
filename = "gaussian_filter_stats.txt"
f = open(filename, "w")

for i in range(number_of_iteration):
  # create copy of original datafield
  to_filter = d.duplicate()
  # apply gaussian filter to copy of datafield
  to_filter.filter_gaussian(i*size)
  # collect RMS to file
  f.write("iteration %d rms %e\n" % (i, to_filter.get_rms()))
  # get histogram
  histogram = gwy.DataLine(1, 1, False)
  to_filter.dh(histogram, -1)
  # write histogram to file
  f.write("histogram: ")
  for v in histogram.get_data():
    f.write("%e \n" % v)
f.write("\n" )
f.close()

Calling process module and export

Modify datafield by calling modules level and remove scars. Change datafield palette, export to PNG and store computed statistical values (HHCF, PSDF, DH, etc.) to separate files.

import gwyutils

def write_to_file(dataline, filename):
   # write content of dataline to specified file
   f = open(filename, "w")
   data = line.get_data()
   for num in data:
      f.write(str(num)+"\n")
   f.close()


# get active container
c = gwy.gwy_app_data_browser_get_current(gwy.APP_CONTAINER)
# get filename of active container
filename = c.get_string_by_name("/filename")
# remove extension from filename
filebase = filename[0:-4]
# call 'level' process module as interactive
gwy.gwy_process_func_run("level", c, gwy.RUN_INTERACTIVE)
# call 'remove scars' process module
gwy.gwy_process_func_run("scars_remove", c, gwy.RUN_IMMEDIATE)

# set colors of first datafield in active container
c.set_string_by_name("/0/base/palette", "Green")

# save fixed file
gwy.gwy_file_save(c, filebase+"_fixed.gwy", gwy.RUN_NONINTERACTIVE)

# export datafield from container c specified by name to png file
gwyutils.save_dfield_to_png(c, "/0/data", filebase+"_fixed.png", gwy.RUN_INTERACTIVE)

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

# create dataline for storing results of following methods
line = gwy.DataLine(1, 1, True)

# Calculate one-dimensional autocorrelation function of a data field.
d.hhcf(line, gwy.ORIENTATION_HORIZONTAL, gwy.INTERPOLATION_LINEAR, -1)
# write values to file
write_to_file(line, filebase+"_hhcf.txt")

# Calculates one-dimensional power spectrum density function of a data field.
d.psdf(line, gwy.ORIENTATION_HORIZONTAL, gwy.INTERPOLATION_LINEAR, gwy.WINDOWING_HANN, -1)
# write values to file
write_to_file(line, filebase+"_psdf.txt")

# Calculates distribution of heights in a data field.
d.dh(line, -1)
# write values to file
write_to_file(line, filebase+"_dh.txt")

# Computes basic statistical quantities of a data field.
avg, ra, rms, skew, kurtosis = d.get_stats()
# Save statistical quantities to file
f = open(filebase+"_stat.txt", "w")
f.write("avg "+str(avg) +"\nra "+str(ra)+"\nrms "+str(rms)+"\nskew "\
+str(skew)+"\nkurtosis "+ str(kurtosis))

# close file
f.close()

Iteration over open files

This script iterate thru open files (containers). Each datafield in every container is inverted, the pattern is changed and exported to PNG file.

import gwyutils
# base name
basename = "exported_png_%d.png"
i = 0
# get list of available containers
cons = gwy.gwy_app_data_browser_get_containers()
# iterate thru containers and datafields
for c in cons:
  # get directory of datafields where key is key in container
  dfields = gwyutils.get_data_fields_dir(c)
  for key in dfields.keys():
    # get processed datafield object
    datafield = dfields[key]
    # retrieve datafield number in container from key (for example '/0/data')
    datafield_id = key.split('/')[1]
    # set palette of datafield
    c.set_string_by_name("/%s/base/palette" % datafield_id, "Spectral")
    # invert datafield values (processing section)
    datafield.invert(0, 0, 1)
    # request redraw before export
    datafield.data_changed()
    # export datafield to png, for the first time show export dialog
    if i == 0:
      gwyutils.save_dfield_to_png(c, key, basename % i, gwy.RUN_INTERACTIVE)
    else :
      gwyutils.save_dfield_to_png(c, key, basename % i, gwy.RUN_NONINTERACTIVE)
    i += 1
    # request redraw datawindows with datafields
    datafield.data_changed()
1.20 (yeti, 2018-02-08 20:42:50)
© David Nečas and Petr Klapetek

Home Download News Features Screenshots Documentation English guide French guide Russian guide FAQ Communicate Participate Resources Publications Applications Site Map

Valid XHTML 1.0 Valid CSS