Minimal Module — Dissection of a minimal Gwyddion data processing module
In this section we will describe a minimal Gwyddion data-processing module. It provides a function to invert values in a channel about zero. The complete module code is:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 |
#include <libgwyddion/gwymacros.h> #include <libprocess/datafield.h> #include <libgwymodule/gwymodule-process.h> #include <libgwydgets/gwystock.h> #include <app/gwyapp.h> #define INVERT_VALUE_RUN_MODES GWY_RUN_IMMEDIATE static gboolean module_register(void); static void my_invert_value(GwyContainer *data, GwyRunType run); static GwyModuleInfo module_info = { GWY_MODULE_ABI_VERSION, &module_register, N_("Inverts data value."), "J. Random Hacker <hacker.jr@example.org>", "1.0", "Bit Rot Inc.", "2006", }; GWY_MODULE_QUERY(module_info) static gboolean module_register(void) { gwy_process_func_register("my_invert_value", (GwyProcessFunc)&my_invert_value, N_("/_Test/_Invert Value"), GWY_STOCK_VALUE_INVERT, INVERT_VALUE_RUN_MODES, GWY_MENU_FLAG_DATA, N_("Invert data value about origin")); return TRUE; } static void my_invert_value(GwyContainer *data, GwyRunType run) { GwyDataField *dfield; GQuark quark; gint id; g_return_if_fail(run & INVERT_VALUE_RUN_MODES); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield, GWY_APP_DATA_FIELD_KEY, &quark, GWY_APP_DATA_FIELD_ID, &id, 0); gwy_app_undo_qcheckpointv(data, 1, &quark); gwy_data_field_multiply(dfield, -1.0); gwy_data_field_data_changed(dfield); gwy_app_channel_log_add_proc(data, id, id); } |
Though the above example is minimal it still constis of quite a bit of code. We will analyse it piece-by-piece in the following paragraphs.
First of all, of course, some header files.
1 2 3 4 5 |
#include <libgwyddion/gwymacros.h> #include <libprocess/datafield.h> #include <libgwymodule/gwymodule-process.h> #include <libgwydgets/gwystock.h> #include <app/gwyapp.h> |
These four are essential, for a complex modules you may need additional
headers.
gwymacros.h
contains some basic macros,
datafield.h
declares basic GwyDataField methods,
gwystock.h
contains stock icon definitions,
gwymodule-process.h
declares functions essential for registering the module and its data processing functions,
and gwyapp.h
includes everything necessary for interfacing with the application.
Function prototypes of our functions.
1 2 3 |
static gboolean module_register(void); static void my_invert_value(GwyContainer *data, GwyRunType run); |
Note all functions and global variables should be
declared static
, the module should export no symbol
except GWY_MODULE_QUERY
described below.
An attentive reader has probably noticed we omitted the line
1 |
#define INVERT_VALUE_RUN_MODES GWY_RUN_IMMEDIATE |
where so-called run mode is defined. We will describe run modes in
detail in the section about
data processing modules. At this point it suffices to say
GWY_RUN_IMMEDIATE
means the function is non-interactive, executed
immediately.
Here the interesting part starts. The GwyModuleInfo structure contains overall information about the module, most of it is presented in a more-or-less human-readable form in Gwyddion in the module browser.
1 2 3 4 5 6 7 8 9 |
static GwyModuleInfo module_info = { GWY_MODULE_ABI_VERSION, &module_register, N_("Inverts data value."), "J. Random Hacker <hacker.jr@example.org>", "1.0", "Bit Rot Inc.", "2006", }; |
The first item is always GWY_MODULE_ABI_VERSION
. The ABI version
compiled to the module is checked by the loader on load-time and
modules with wrong ABI version are rejected.
The second item is a pointer to module registration function, by
convention called module_register
. It is described
in details below.
The fourth item is module description. It will appear as Description in the module browser. This is a short text (up to a paragraph or two) informing curious humans what the module contains and/or does.
The next item is the module author(s). Under normal circumstances this should be a name of a person (or more people). Including a contact e-mail address here it's a good idea because it will appear in the browser as Authors, so people don't need to look to the module sources to find out how to contact you.
The next item is the module version, a free-form string that will appear as Version in the browser. Though it is free-form, using a versioning scheme with alorithmically comparable versions is preferable.
The last but one and last items are the module copyright and date. The copyright field may be the same as authors field (except without the e-mail address), it may be an organization, or whoever owns the copyright.
A Gwyddion module is loaded in two stages. First, it is queried, the module responds with its module info, Gwyddion then performs some basic sanity checks (e.g., whether module ABI version matches). If it looks all right, Gwyddion continues with the registration of particular module features.
The query function should be always constructed using the
GWY_MODULE_QUERY
macro as follows (note there is no
semicolon after the right parenthesis):
1 |
GWY_MODULE_QUERY(module_info) |
The module_info
parameter is the module info
described above. If you change its name for any reason, change it here
too.
The module registration function is called in the second registration
stage and is responsible for registering particular module functions,
each in one turn. Our sample module registeres only a one function,
my_invert_value
.
Each function type has its own registration function, our function
is a data processing one, so it's registered with
gwy_process_func_register()
.
File loading and/or saving functions are registered with
gwy_file_func_register()
, etc.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
static gboolean module_register(void) { gwy_process_func_register("my_invert_value", (GwyProcessFunc)&my_invert_value, N_("/_Test/_Invert Value"), GWY_STOCK_VALUE_INVERT, INVERT_VALUE_RUN_MODES, GWY_MENU_FLAG_DATA, N_("Invert data value about origin")); return TRUE; } |
The registration function normally returns TRUE
.
Returning FALSE
means the registration failed, and
Gwyddion then attempts to unregister all its
already registered functions, unload the module and proceed as if it
didn't exist.
Now let's do some actuall data processing:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
static void my_invert_value(GwyContainer *data, GwyRunType run) { GwyDataField *dfield; GQuark quark; gint id; g_return_if_fail(run & INVERT_VALUE_RUN_MODES); gwy_app_data_browser_get_current(GWY_APP_DATA_FIELD, &dfield, GWY_APP_DATA_FIELD_KEY, &quark, GWY_APP_DATA_FIELD_ID, &id, 0); gwy_app_undo_qcheckpointv(data, 1, &quark); gwy_data_field_multiply(dfield, -1.0); gwy_data_field_data_changed(dfield); gwy_app_channel_log_add_proc(data, id, id); } |
A few things can be seen here. First, we check the run mode we were executed in. More sofisticated modules can in principle do different things based on the run mode, but we just check whether it looks sane.
Next, we get the data field to operate on. The notion of current object is maintained by the application data browser therefore we us a data browser method (beside the current data field, we also ask for its key that will be useful for undo later).
GwyDataField is the basic data object representing a two-dimensional array of values (typically a height field). Quite a few datafield manipulating functions already exist in libprocess, we will use one of them to perform the value inversion.
Function gwy_app_undo_qcheckpointv()
creates a point in the undo history
we can return to later. It is necessary to call it before we start
modifying the data. Its first argument is the
data container the objects we will
change reside in. Then we pass the number of changed items (1) and
an array of their indentifiers in the data container.
Then we finally invert the value with gwy_data_field_multiply()
and we are
done – almost. To notify views displaying our data field that its data
has changed we call gwy_data_field_data_changed()
. This function should
be called once on each changed data field after all processing is done.
When we perform a data modification, it should be logged to the data
operation log. A new log entry can be added with
gwy_app_channel_log_add_proc()
, where id
is the numeric identifier of the
data field in the container. If the source and target of the operation
is the same, we just pass the same identifier twice. If new data are
created the two identifier can differ, as described in the
logging documentation.