File: module-tutorial-minimal.xml

package info (click to toggle)
gwyddion 2.52-1
  • links: PTS, VCS
  • area: main
  • in suites: buster
  • size: 46,588 kB
  • sloc: ansic: 367,740; python: 7,788; sh: 5,245; makefile: 4,317; xml: 3,631; cpp: 2,550; pascal: 418; perl: 154; ruby: 130
file content (337 lines) | stat: -rw-r--r-- 13,131 bytes parent folder | download | duplicates (4)
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
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
<?xml version="1.0"?>
<!DOCTYPE refentry PUBLIC "-//OASIS//DTD DocBook XML V4.1.2//EN" 
               "http://www.oasis-open.org/docbook/xml/4.1.2/docbookx.dtd">
<refentry id="gwymodule-tutorial-minimal" revision="$Id: module-tutorial-minimal.xml 20682 2017-12-18 18:39:00Z yeti-dn $">
  <refmeta>
    <refentrytitle>Minimal Module</refentrytitle>
    <manvolnum>3</manvolnum>
    <refmiscinfo>Gwyddion</refmiscinfo>
  </refmeta>

  <refnamediv>
    <refname>Minimal Module</refname>
    <refpurpose>
      Dissection of a minimal <application>Gwyddion</application> data
      processing module
    </refpurpose>
  </refnamediv>

  <refsect1>
    <title>Module Overview</title>
    <para>
      In this section we will describe a minimal
      <application>Gwyddion</application> data-processing module.
      It provides a function to invert values in a channel about zero.
      The complete module code is:
    </para>
    <informalexample><programlisting>
&num;include &lt;libgwyddion/gwymacros.h&gt;
&num;include &lt;libprocess/datafield.h&gt;
&num;include &lt;libgwymodule/gwymodule-process.h&gt;
&num;include &lt;libgwydgets/gwystock.h&gt;
&num;include &lt;app/gwyapp.h&gt;

&num;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,
    &amp;module_register,
    N_("Inverts data value."),
    "J. Random Hacker &lt;hacker.jr&commat;example.org&gt;",
    "1.0",
    "Bit Rot Inc.",
    "2006",
};
<![CDATA[
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);
}
]]></programlisting></informalexample>
  <para>
    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.
  </para>
  </refsect1>

  <refsect1>
    <title>Boilerplate</title>
    <para>
      First of all, of course, some header files.
    </para>
    <informalexample><programlisting>
&num;include &lt;libgwyddion/gwymacros.h&gt;
&num;include &lt;libprocess/datafield.h&gt;
&num;include &lt;libgwymodule/gwymodule-process.h&gt;
&num;include &lt;libgwydgets/gwystock.h&gt;
&num;include &lt;app/gwyapp.h&gt;
</programlisting></informalexample>
    <para>
      These four are essential, for a complex modules you may need additional
      headers.
      <filename><link linkend="libgwyddion-gwymacros">gwymacros.h</link></filename>
      contains some basic macros,
      <filename><link linkend="GwyDataField">datafield.h</link></filename>
      declares basic #GwyDataField methods,
      <filename><link linkend="libgwydgets-gwystock">gwystock.h</link></filename>
      contains stock icon definitions,
      <filename><link linkend="libgwymodule-gwymodule-process">gwymodule-process.h</link></filename>
      declares functions essential for registering the module and its data processing functions,
      and <filename><link linkend="libgwyapp-gwyapp">gwyapp.h</link></filename>
      includes everything necessary for interfacing with the application.
    </para>
  </refsect1>

  <refsect1>
    <title>Function Prototypes</title>
    <para>
      Function prototypes of our functions.
    </para>
    <informalexample><programlisting><![CDATA[
static gboolean module_register(void);
static void     my_invert_value(GwyContainer *data,
                                GwyRunType run);
]]></programlisting></informalexample>
    <para>
      Note <emphasis>all</emphasis> functions and global variables should be
      declared <literal>static</literal>, the module should export no symbol
      except %GWY_MODULE_QUERY described below.
    </para>
    <para>
      An attentive reader has probably noticed we omitted the line
    </para>
    <informalexample><programlisting>
&num;define INVERT_VALUE_RUN_MODES GWY_RUN_IMMEDIATE
</programlisting></informalexample>
    <para>
      where so-called run mode is defined.  We will describe run modes in
      detail in the <link linkend="gwymodule-tutorial-process">section about
      data processing modules</link>.  At this point it suffices to say
      %GWY_RUN_IMMEDIATE means the function is non-interactive, executed
      immediately.
    </para>
  </refsect1>

  <refsect1>
    <title>The Module Info Structure</title>
    <para>
      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 <application>Gwyddion</application>
      in the module browser.
    </para>
    <informalexample><programlisting>
static GwyModuleInfo module_info = {
    GWY_MODULE_ABI_VERSION,
    &amp;module_register,
    N_("Inverts data value."),
    "J. Random Hacker &lt;hacker.jr&commat;example.org&gt;",
    "1.0",
    "Bit Rot Inc.",
    "2006",
};
</programlisting></informalexample>
    <para>
      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.
    </para>
    <para>
      The second item is a pointer to module registration function, by
      convention called <function>module_register</function>.  It is described
      in details below.
    </para>
    <para>
      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.
    </para>
    <para>
      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.
    </para>
    <para>
      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.
    </para>
    <para>
      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.
    </para>
  </refsect1>

  <refsect1>
    <title>The Module Query Function</title>
    <para>
      A <application>Gwyddion</application> module is loaded in two stages.
      First, it is queried, the module responds with its module info,
      <application>Gwyddion</application> then performs some basic sanity
      checks (e.g., whether module ABI version matches).  If it looks all
      right, <application>Gwyddion</application> continues with the
      registration of particular module features.
    </para>
    <para>
      The query function should be always constructed using the
      %GWY_MODULE_QUERY macro as follows (note there is <emphasis>no</emphasis>
      semicolon after the right parenthesis):
    </para>
    <informalexample><programlisting><![CDATA[
GWY_MODULE_QUERY(module_info)
]]></programlisting></informalexample>
    <para>
      The <parameter>module_info</parameter> parameter is the module info
      described above.  If you change its name for any reason, change it here
      too.
    </para>
  </refsect1>

  <refsect1>
    <title>Module Feature Registration</title>
    <para>
      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,
      <function>my_invert_value</function>.
    </para>
    <para>
      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.
    </para>
    <informalexample><programlisting><![CDATA[
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;
}
]]></programlisting></informalexample>
   <para>
     The registration function normally returns <literal>TRUE</literal>.
     Returning <literal>FALSE</literal> means the registration failed, and
     <application>Gwyddion</application> then attempts to unregister all its
     already registered functions, unload the module and proceed as if it
     didn't exist.
   </para>
  </refsect1>

  <refsect1>
    <title>Executive</title>
    <para>
      Now let's do some actuall data processing:
    </para>
    <informalexample><programlisting><![CDATA[
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);
}
]]></programlisting></informalexample>
   <para>
     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.
   </para>
   <para>
     Next, we get the data field to operate on.  The notion of current object
     is maintained by the application
     <link linkend="libgwyapp-data-browser">data browser</link> 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).
   </para>
   <para>
     #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 <link
     linkend="libprocess">libprocess</link>, we will use one of them
     to perform the value inversion.
   </para>
   <para>
     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
     <link linkend="GwyContainer">data container</link> 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.
   </para>
   <para>
     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.
   </para>
   <para>
     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
     <link linkend='libgwyapp-log'>logging documentation</link>.
   </para>
  </refsect1>
</refentry>