Version 0.11, March 22, 2012
Albert Gräf <Dr.Graef@t-online.de>
Gnumeric/Pure is a Gnumeric extension which lets you use Pure functions in Gnumeric, the Gnome spreadsheet. It offers better execution speed than the existing Perl and Python plugins, and provides some powerful features not found in other Gnumeric scripting plugins, such as asynchronous data sources created from Pure streams and OpenGL rendering in Gnumeric frame widgets via Pure’s OpenGL module.
This package provides a Gnumeric extension which gives you access to the Pure programming language in Gnumeric. It works pretty much like the Perl and Python plugin loaders which are distributed with Gnumeric, but Gnumeric/Pure offers some powerful features which aren’t found in other Gnumeric scripting plugins:
Adding Pure functions to Gnumeric is quite easy. Once the plugin is installed and enabled, you can simply start adding Pure functions to the provided pure_func.pure script, or you can create your own Pure plugin folders. Gnumeric/Pure also provides a programming interface for use in Pure which gives you access to various advanced features, such as modifying entire ranges of cells with one Pure call, calling Gnumeric functions from Pure, and setting up asynchronous data sources and OpenGL frames. This is all explained in detail below.
Get the latest source from http://pure-lang.googlecode.com/files/gnumeric-pure-0.11.tar.gz.
Obviously, you need to have both Pure and Gnumeric installed. Pure 0.36 and Gnumeric 1.9.13 are known to work. We recommend Gnumeric 1.9.14 or later since it has improved support for GUI widgets. (Older Gnumeric versions probably work as well if you’re willing to fiddle with the Makefile and/or the sources. See the beginning of the Makefile for related information.)
As shipped, the Makefile is set up to build Gnumeric/Pure with OpenGL support, which requires that you have the OpenGL libraries as well as GtkGLExt (the Gtk OpenGL extension) installed. These should be readily available on most systems, but you can also disable this feature by invoking make as make GLDEPS=.
Run make to compile the software. You might have to adjust the settings at the beginning of the Makefile to make this work. If you’re lucky and the compile goes through, you should now have a pure_loader.so file in the pure-loader subdirectory. You can install the plugin and related stuff with sudo make install in the global Gnumeric plugin directory, or if you prefer to install it into your personal plugin directory then run make install-local instead. (We recommend the latter since it lets you adjust pure_func.pure for your purposes more easily.) Optionally, you might also want to copy the gnumeric-pure.html file to your Pure library directory so that you can read it with the help command of the Pure interpreter or in Emacs Pure mode.
Typically, make install and make install-local will install the plugins into the following directories by default:
The Makefile tries to guess the installation path and version number of Gnumeric on its own. If it guesses wrong, you can change these using the Makefile variables prefix and gnmversion, respectively. For instance:
$ make prefix=/usr gnmversion=1.9.13
If make install doesn’t work for some reason, you can also just copy the pure-func, pure-glfunc and pure-loader directories manually to your Gnumeric plugin directory.
Once Gnumeric/Pure has been properly installed, you should see it in Gnumeric’s Tools/Plug-ins dialog. There are actually two main entries, one labelled “Pure functions” and the other one labelled “Pure plugin loader”. You need to enable both before you can start using Pure functions in your Gnumeric spreadsheets. There’s also a third entry labelled “Pure OpenGL functions” which you might want to enable if you want to try the OpenGL capabilities (this will only work if you built Gnumeric/Pure with OpenGL support and have Pure’s OpenGL module installed; see OpenGL Interface for details).
Gnumeric doesn’t provide much in the way of GUI customization options right now, but at least it’s possible for plugins to install and configure additional menu and toolbar options. Gnumeric/Pure adds three additional options to the Tools menu which allow you to stop asynchronous data sources, reload Pure scripts and edit them. After installation, the definitions of these items can be found in the pure-loader/pure-ui.xml file in your Gnumeric plugin directory. Have a look at this file and edit is as desired. E.g., if you want to put the Pure-related options into a submenu and enable toolbar buttons for these options, then your pure-ui.xml file should look as follows:
<ui>
<menubar>
<menu name="Tools" action="MenuTools">
<separator/>
<menu name="Pure" action="PureMenu">
<menuitem action="PureStop"/>
<menuitem action="PureReload"/>
<menuitem action="PureEdit"/>
</menu>
</menu>
</menubar>
<toolbar name="StandardToolbar">
<separator/>
<toolitem action="PureStop"/>
<toolitem action="PureReload"/>
<toolitem action="PureEdit"/>
</toolbar>
</ui>
With Pure/Gnumeric installed and enabled, you should be ready to join the fun now. Start up Gnumeric, click on a cell and invoke the “f(x)” dialog. The Pure functions available for use are shown in the “Pure” category. E.g., click on pure_hello. Now the Pure interpreter will be loaded and the function description displayed. Click “Insert” and then “Ok”. You should now be able to read the friendly greeting returned by the pure_hello function.
Of course, you can also enter the function call directly as a formula into a cell as usual. Click on a cell, then enter the following:
=pure_hello(getenv("USER"))
The greeting should now be displayed with your login name in it.
Play around a bit with the other Pure functions. These functions are nothing special; they are just ordinary Pure functions which are defined by the pure_func.pure script in the pure-func subdirectory of your Gnumeric plugin directory. You can have a look at them by invoking the “Edit Pure Script” option which gets added to the Tools/Pure menu once the Pure plugin loader is enabled. (This will invoke the emacs editor by default, or the editor named by the EDITOR environment variable. You can set this environment variable in your shell’s startup files.) The Tools/Pure menu contains a second Pure-related option, “Reload Pure Scripts” which can be used to quickly reload all loaded Pure scripts after edits; more about that later.
Please note that most of the functions in pure_func.pure are rather useless, they are only provided for illustrative purposes. However, there are some useful examples in there, too, in particular:
A spreadsheet showing most of the predefined functions in action can be found in pure-examples.gnumeric example distributed with Gnumeric/Pure.
The pure-examples.gnumeric spreadsheet also includes an instance of pure_shell which lets you evaluate arbitrary Pure code in the same interpreter instance that executes Gnumeric/Pure functions. This is very helpful if you’re developing new Pure functions to be used in Gnumeric. It also lets you use Gnumeric as a kind of GUI frontend to the Pure interpreter. You can try this now. Open the pure-examples spreadsheet in Gnumeric and enter the following into the input cell of the Pure shell:
> scanl (+) 0 (1..20)
[0,1,3,6,10,15,21,28,36,45,55,66,78,91,105,120,136,153,171,190,210]
Note that here and in the following the prompt > indicates a Pure expression to be evaluated in Gnumeric (rather than the standalone Pure interpreter), which is followed by another line indicating the result (printed in the output cell below the input cell of the Pure shell). You can find the Pure shell at the bottom of the first sheet in pure-examples, see the screenshot below. For your convenience, there’s also a second, bigger one on the second sheet. You might want to copy this over to a separate spreadsheet which you can use as a scratchpad for experimentation purposes.
The Pure shell.
Also note that this is in fact Pure code (not a Gnumeric formula) being evaluated there. You can execute any Pure code, including Pure declarations, so you can type:
> using system; puts "Hello, world!";
14
This prints the string "Hello, world!" on standard output, visible in the terminal window where you launched Gnumeric. Here is another example, showing how you can invoke any function from the C library, by declaring it as a Pure extern function:
> extern int rand(); [rand | i = 1..5];
[1810821799,2106746672,1436605662,1363610028,695042099]
All functions in the Pure prelude are readily available in the Gnumeric Pure shell, as well as the functions defined in pure_func.pure and its imports, including the programming interface described in Advanced Features. For instance, here’s how you can retrieve a cell value from the current sheet:
> get_cell "A1"
"Gnumeric/Pure Examples"
Using call (see Calling Gnumeric from Pure), you can also invoke any Gnumeric function:
> call "product" (1..10)
3628800.0
After playing around with pure_func.pure and the interactive Pure shell for a while, of course you will want to write your own functions, that’s what this plugin is about after all! For the beginning, you can just add your definitions to the existing pure_func.pure script. Use the “Edit Pure Script” option to edit the script in your favourite editor, and see the comments and the examples in the script for guidance. (This document assumes that you’re already familiar with Pure, if not then you should consult the available Pure documentation.)
Note that if you delete or rename any functions in this file, or add new ones to it, then you also have to change the list of function names in the plugin.xml file in the same directory accordingly. This file tells Gnumeric which functions are provided by the script. Unfortunately, you’ll have to restart Gnumeric to make changes in this file take effect. If you only change the definition of an existing function then it’s usually sufficient to just invoke “Reload Pure Scripts” afterwards, and maybe run “Recalculate” (F9) to recompute the spreadsheet. However, if you also made changes to the function descriptions provided via gnm_info (see the following section for explanation), then you’ll also have to restart Gnumeric so that it picks up the changes.
Once you understand how this works, you can also create your own plugin directories with your personal collections of Gnumeric/Pure functions, using the pure-func directory as a template. For instance, let’s assume that your Gnumeric/Pure stuff is in a script named gnumeric.pure under /some/path/pure/gnumeric. The plugin.xml file in that directory might look as follows:
<?xml version="1.0" encoding="UTF-8"?>
<plugin id="Gnumeric_MyPureFunc">
<information>
<name>My Pure functions</name>
<description>My Pure functions for use in Gnumeric.</description>
<require_explicit_enabling/>
</information>
<loader type="Gnumeric_PureLoader:pure">
<attribute value="gnumeric" name="module_name"/>
</loader>
<services>
<service type="function_group" id="my_pure_func">
<category>Pure</category>
<functions>
<!-- My Pure functions go here, e.g.: -->
<function name="my_pure_func"/>
</functions>
</service>
</services>
</plugin>
The following steps are needed to tell Gnumeric about your new Pure plugin:
The Pure loader can load multiple Pure plugins. You only need to tell Gnumeric about them after creating the scripts and plugin.xml files and placing them into corresponding plugin directories. Just enable the ones that you want in Tools/Plug-ins. All scripts are loaded in the same Pure interpreter (and thus are treated like one big script) so that functions in one script can use the function and variable definitions in another. If you need to access the definitions in the pure_func.pure “mother script”, you can also just import it into your scripts with a using clause, i.e.: using pure_func;
By default, when a Pure function is called from Gnumeric, it receives its arguments in a list. However, it is possible to tell Gnumeric about the expected arguments of the function and also specify a help text to be displayed in the “f(x)” dialog, by giving a definition of the special gnm_info function as explained below.
To describe a given function to Gnumeric, define gnm_info "<name>" (where <name> is the name of the function) as a pair with the following elements:
Both the signature and the function description are optional. That is, gnm_info may return either just a signature string, or a list of hash pairs with the function description, or both. The signature defaults to a variadic function which takes any number of parameters of any type (see below), and the description defaults to some boilerplate text which says that the function hasn’t been documented yet.
Note that if no signature is given, then the function accepts any number of parameters of any type. In that case, or if there are optional parameters, the function becomes variadic and the (optional) parameters are passed as a Pure list (in addition to the non-optional parameters).
Here’s the list of valid parameter types, as they are documented in the Gnumeric sources:
f : float no errors, string conversion attempted
b : boolean identical to f
s : string no errors
S : scalar any non-error scalar
E : scalar any scalar, including errors
r : cell range content may not be evaluated yet
A : area array, range (as above), or scalar
? : anything any value (scalars, non-scalars, errors, whatever)
The keys used in the function description may be any of the following, along with sample text for each type of field:
GNM_FUNC_HELP_NAME => "name:synopsis"
GNM_FUNC_HELP_ARG => "name:parameter description"
GNM_FUNC_HELP_DESCRIPTION => "Long description."
GNM_FUNC_HELP_NOTE => "Note."
GNM_FUNC_HELP_EXAMPLES => "=sample_formula()"
GNM_FUNC_HELP_SEEALSO => "foo,bar,..."
The following keys are only supported in the latest Gnumeric versions:
GNM_FUNC_HELP_EXTREF => "wiki:en:Trigonometric_functions"
GNM_FUNC_HELP_EXCEL => "Excel compatibility information."
GNM_FUNC_HELP_ODF => "OpenOffice compatibility information."
Note that inside the descriptions, the notation @{arg} (@arg in older Gnumeric versions) can be used to refer to a parameter value. For instance, here’s a sample description for a binary function which also includes a help text:
gnm_info "pure_max" = "ff",
[GNM_FUNC_HELP_NAME => "pure_max:maximum of two numbers",
GNM_FUNC_HELP_ARG => "x:number",
GNM_FUNC_HELP_ARG => "y:number",
GNM_FUNC_HELP_DESCRIPTION =>
"Computes the maximum of two numbers @{x} and @{y}.",
GNM_FUNC_HELP_EXAMPLES => "=pure_max(17,22)"];
As you can see, the function descriptions are a bit unwieldy, so it’s convenient to construct them using this little helper function defined in pure_func.pure:
gnm_help name::string args descr::string notes examples see_also =
[GNM_FUNC_HELP_NAME => name] +
[GNM_FUNC_HELP_ARG => x | x::string = args ] +
[GNM_FUNC_HELP_DESCRIPTION => descr ] +
[GNM_FUNC_HELP_NOTE => x | x::string = notes ] +
[GNM_FUNC_HELP_EXAMPLES => x | x::string = examples ] +
(if null see_also then [] else
[GNM_FUNC_HELP_SEEALSO => join "," see_also]);
Now the description can be written simply as follows:
gnm_info "pure_max" = "ff", gnm_help "pure_max:maximum of two numbers"
["x:number", "y:number"]
"Computes the maximum of two numbers @{x} and @{y}."
[] ["=pure_max(17,22)"] [];
Since this function only has fixed arguments, it will be called in curried form, i.e., as pure_max x y. For instance, the actual definition of pure_max may look as follows:
pure_max x y = max x y;
Conversely, if no signature is given, then the function accepts any number of parameters of any type, which are passed as a list. For instance:
gnm_info "pure_sum" = gnm_help "pure_sum:sum of a collection of numbers"
[] "Computes the sum of a collection of numbers."
[] ["=pure_sum(1,2,3,4,5,6)"] ["pure_sums"];
Here the function will be called as pure_sum [x1,x2,...], where x1, x2, etc. are the arguments the function is invoked with. Note that in this case there may be any number of arguments (including zero) of any type, so your function definition must be prepared to handle this. If a function does not have a gnm_info description at all then it is treated in the same fashion. The pure_func.pure script contains some examples showing how to write functions which can deal with any numbers of scalars, arrays or ranges, see the pure_sum and pure_sums examples. These employ the following ranges function to “flatten” a parameter list to a list holding all denoted values:
ranges xs = cat [ case x of _::matrix = list x; _ = [x] end | x = xs ];
E.g., the pure_sum function can now be defined as follows:
pure_sum xs = foldl (+) 0 (ranges xs);
A function may also have both fixed and optional arguments (note that in what follows we’re going to omit the detailed function descriptions for brevity):
gnm_info "foo" = "ff|ff";
In this case the fixed arguments are passed in curried form as usual, while the optional parameters are passed as a list. That is, foo may be called as foo x y [], foo x y [z] or foo x y [z,t], depending on whether it is invoked with two, three or four arguments.
The marshalling of types between Gnumeric and Pure is pretty straightforward; basically, Pure numbers, strings and matrices map to Gnumeric numbers, strings and arrays, respectively. The following table summarizes the available conversions:
Pure | Gnumeric |
---|---|
gnm_error "#N/A" | error |
4711, 4711L, 4711.0 | scalar (number) |
"Hello world" | string |
() | empty |
(1,2,3) | array |
[1,2,3] | array |
{1,2,3;4,5,6} | array (or cell range) |
"A1:B10" | cell range ("r" conversion) |
These conversions mostly work both ways. Note that on input, cell ranges are usually passed as matrices to Pure functions (i.e., they are passed “by value”), unless the function signature specifies a "r" conversion in which case the cell ranges themselves are passed to the function in string form. (Such values can also be passed on to Gnumeric functions which expect a cell range ("r" ) parameter, see Calling Gnumeric from Pure below.)
Conversely, matrices, lists and tuples all become Gnumeric arrays on output, so usually you’ll want to enter these as array functions (Ctrl-Shift-Enter in Gnumeric). As a special case, the empty tuple can be used to denote empty cell values (but note that empty Gnumeric values may become zeros when passed as float or array arguments to Pure functions).
If a Pure function returns a value that doesn’t match any of the above then it is converted to a string in Pure expression syntax and that string is returned as the result of the function invocation in Gnumeric. This makes it possible to return any kind of symbolic Pure value, but note that if such a value is then fed into another Pure function, that function will have to convert the string value back to the internal representation if needed; this can be done very conveniently using Pure’s eval function, see the Pure documentation for details.
This section explains various additional features provided by the Gnumeric/Pure interface that should be useful for writing your own functions. Note that, for your convenience all functions discussed in this section are declared in pure_func.pure.
It is possible to call Gnumeric functions from Pure using the call function which takes the name of the function (a string) as its first, and the parameters as the second (list) argument. For instance:
gnm_info "gnm_bitand" = "ff";
gnm_bitand x y = call "bitand" [x,y];
Note that call is an external C function provided by Gnumeric/Pure. If you want to use it, it must be declared in your Pure script as follows:
extern expr* pure_gnmcall(char* name, expr* args) = call;
However, pure_func.pure already contains the above declaration, so you don’t have to do this yourself if you import pure_func.pure in your scripts.
Also note that call doesn’t do any of Gnumeric’s automatic conversions on the parameters, so you have to pass the proper types of arguments as required by the function.
Gnumeric/Pure provides the following functions to retrieve and modify the contents of spreadsheet cells and ranges of such cells:
extern expr* pure_get_cell(char* s) = get_cell;
extern expr* pure_get_cell_text(char* s) = get_cell_text;
extern expr* pure_get_cell_format(char* s) = get_cell_format;
extern expr* pure_set_cell(char* s, expr *x) = set_cell;
extern expr* pure_set_cell_text(char* s, expr *x) = set_cell_text;
extern expr* pure_set_cell_format(char* s, expr *x) = set_cell_format;
extern expr* pure_get_range(char* s) = get_range;
extern expr* pure_get_range_text(char* s) = get_range_text;
extern expr* pure_get_range_format(char* s) = get_range_format;
extern expr* pure_set_range(char* s, expr *x) = set_range;
extern expr* pure_set_range_text(char* s, expr *x) = set_range_text;
extern expr* pure_set_range_format(char* s, expr *x) = set_range_format;
For instance, here’s how you use these functions to write and then read some cell values (try this in the interactive Pure shell):
> set_cell "A14" 42
()
> get_cell "A14"
42.0
> set_range "A14:G14" $ scanl (*) 1 (1..6)
()
> get_range "A14:G14"
{1.0,1.0,2.0,6.0,24.0,120.0,720.0}
> set_cell_text "A14" "=sum(B14:G14)"
()
> get_cell "A14"
873.0
> get_cell_text "A14"
"=sum(B14:G14)"
> get_range_text "A14:G14"
{"=sum(B14:G14)","1","2","6","24","120","720"}
Note that while the set_cell function sets the given cell to a constant value, set_cell_text also allows you to store a formula in a cell which will then be evaluated as usual. Similarly, get_cell retrieves the cell value, while get_cell_text yields the text in the cell, as entered by the user (which will either be a formula or the textual representation of a constant value). The set_range, set_range_text, get_range and get_range_text functions work analogously, but are used to manipulate entire ranges of cells, which can be set from Pure tuples, lists or matrices, and retrieved as Pure matrices.
Functions to retrieve and change the cell format are also provided (watch the contents of the cell A14 change its color to blue on entering the first expression):
> set_cell_format "A14" "[Blue]0.00"
()
> get_range_format "A14:C14"
{"[Blue]0.00","General","General"}
There are also functions to get the position of the “current” cell (i.e., the cell from which a Pure function was called), and to translate between cell ranges in Gnumeric syntax and the corresponding internal representation consisting of a pointer to a Gnumeric sheet and the cell or range coordinates:
extern expr* pure_this_cell() = this_cell;
extern expr* pure_parse_range(char* s) = parse_range;
extern expr* pure_make_range(expr* x) = make_range;
Examples:
> this_cell
"B4"
> parse_range this_cell
#<pointer 0x875220>,1,3
> make_range (NULL,0,0,10,10)
"Sheet2!A1:K11"
Gnumeric/Pure makes it easy to set up asynchronous data sources which draw values from a Pure computation executed in a background process. This facility is useful to carry out lengthy computations in the background while you can continue to work with your spreadsheet. It also allows you to process incoming data and asynchronous events from special devices (MIDI, sensors, stock tickers, etc.) in (soft) realtime.
To do this, you simply pass an expression to the datasource function. This is another external C function provided by Gnumeric/Pure, which is declared in pure_func.pure as follows:
extern expr* pure_datasource(expr* x) = datasource;
The argument to datasource is typically a thunk or stream (lazy list) which is to be evaluated in the background. The call to datasource initially returns a #N/A value (gnm_error "#N/A") while the computation is still in progress. The cell containing the data source then gets updated automatically as soon as the value becomes available, at which point the datasource call now returns the computed value. E.g., here’s how you would wrap up a lengthy calculation as a thunk and submit it to datasource which carries out the computation as a background task:
gnm_info "pure_frob" = "f";
pure_frob x = datasource (lengthy_calculation x&);
lengthy_calculation x = sleep 3 $$ foldl (*) 1 (1..x);
Note that a cell value may draw values from as many independent data sources as you want, so the definition of a cell may also involve multiple invocations of datasource:
gnm_info "pure_frob2" = "ff";
pure_frob2 x y = datasource (lengthy_calculation x&),
datasource (lengthy_calculation y&);
Special treatment is given to (lazy) lists, in this case datasource returns a new value each time a list element becomes available. For instance, the following function uses an infinite stream to count off the seconds starting from a given initial value:
gnm_info "pure_counter" = "f";
pure_counter x = datasource [sleep (i>x) $$ i | i = x..inf];
You can also try this interactively in the Pure shell:
> datasource [sleep (i>0) $$ i | i = 0..inf]
0
1
...
Here’s another example for the Pure shell which prints the prime numbers:
> datasource primes with primes = sieve (2..inf);
sieve (p:qs) = p : (sleep 1 $$ sieve [q | q = qs; q mod p])& end
2
3
5
...
Note that when processing a lazy list, the cell containing the call will keep changing as long as new values are produced (i.e., forever in this example). The “Stop Data Sources” option in the Tools/Pure menu can be used to stop all active data sources. “Reload Pure Scripts” also does this. You can then restart the data sources at any time by using “Recalculate” (F9) to recompute the spreadsheet.
Also note that because of the special way that datasource handles list values, you cannot return a list directly as the result of datasource, if it is to be treated as a single result. Instead, you’ll have to wrap the result in a singleton list (e.g., datasource [[lengthy_calculation x,lengthy_calculation y]&]), or return another aggregate (i.e., a matrix or a tuple).
Finally, note that when the arguments of a call involving datasource change (because they depend on other cells which may have been updated), the computation is automatically restarted with the new parameters. The default behaviour in this case is that the entire computation will be redone from scratch, but it’s also possible to wrap up calls to datasource in a manner which enables more elaborate communication between Gnumeric and background tasks initiated with datasource. This is beyond the scope of this manual, however, so we leave this as an exercise to the interested reader.
In addition to asynchronous data sources, the trigger function is provided to compute values or take actions depending on some external condition, such as the availability of data on a special device or the creation of some widget (see the next section):
extern expr* pure_trigger(int timeout, expr* cond, expr *val, expr *data)
= trigger;
Thus a typical invocation of the function looks as follows:
trigger timeout condition value data
The condition and value arguments are callback functions which get invoked by trigger, passing them the given data argument. The trigger reevaluates the given condition in regular intervals (1 second in the current implementation) and, as soon as it becomes true, computes the given value and returns that value as the result of the trigger call. As long as the condition doesn’t hold, trigger returns a #N/A value (gnm_error "#N/A"). Note that, in difference to datasource, both the condition and the value are computed in Gnumeric (rather than a child process), so that it is possible to access the current information in the loaded spreadsheet.
The timeout value determines how often the condition is checked. If it is positive, the condition will be reevaluated timeout+1 times (once initially, and then once per second for a total duration of timeout seconds). If it is negative, the trigger never times out and the condition will be checked repeatedly until the trigger expression is removed (or Gnumeric is exited). In either case value data will be recomputed each time condition data yields true. (This is most useful if the computed value, as a side effect, arranges for the condition to become false again afterwards.) Finally, if timeout is zero then the trigger fires at most once, as soon as the condition becomes true, at which point value data is computed just once.
Here’s a (rather useless) example of a trigger which fires exactly once, as soon as a certain cell goes to a certain value, and then modifies another cell value accordingly:
> trigger 0 (\_->get_cell "A14"==="Hello") (\_->set_cell "A15" "World") ()
Now, as soon as you type Hello in the cell A14, the trigger will print World in cell A15. Note that the data argument isn’t used here. A more useful example will be discussed in the following section.
Gnumeric offers some kinds of special objects which can be placed on a sheet. This comprises the chart and image objects which can be found in the “Insert” menu, as well as a number of useful graphical elements and GUI widgets on the “Object” toolbar, accessible via “View/Toolbars”. The latter are also useful for providing control input to Pure functions.
Gnumeric/Pure provides the following function to retrieve information about the special objects in a spreadsheet:
extern expr* pure_sheet_objects() = sheet_objects;
For instance, with one button object in your spreadsheet, the output of sheet_objects might look like this:
> sheet_objects
[("Sheet1","button","Push Me!","A11",[#<pointer 0x2a1dcd0>])]
Each object is described by a tuple which lists the name of the sheet on which the object is located, the type of object, the object’s content or label (if applicable), the cell which the object is linked to (if applicable), and a list of pointers to the corresponding GtkWidgets (if any). Note that in general a GUI object may be associated with several widgets, as Gnumeric allows you to have multiple views on the same spreadsheet, so there will be one widget for each view an object is visible in. Also note that the content/label information depends on the particular type of object:
The sheet_objects function is a bit tricky to use, since some of the objects or their associated widgets might not have been created yet when the spreadsheet is loaded. Therefore it is necessary to use a trigger to make sure that the information is updated once all objects are fully displayed. The pure_func.pure script contains the following little wrapper around sheet_objects which does this:
pure_objects = trigger 0 (\_->all realized sheet_objects)
(\_->matrix$map list sheet_objects) ()
with realized (_,_,_,_,w) = ~listp w || ~null w && ~any null w end;
See the widgets.gnumeric spreadsheet in the distribution for an example.
Possible uses of this facility are left to your imagination. Using Gnumeric’s internal APIs and Pure’s Gtk interface, you might manipulate the GUI widgets in various ways (add icons to buttons or custom child widgets to frames, etc.). One particularly useful case, for which Gnumeric/Pure has built-in support, is rendering an OpenGL scene in a Gnumeric frame widget, see below.
Gnumeric/Pure provides special support for rendering OpenGL scenes into Gnumeric frame widgets. To actually use this, you must have Pure’s OpenGL module installed. The following function is provided to equip a Gnumeric frame with the OpenGL rendering capability:
extern expr *pure_gl_window(char *name, int timeout,
expr *setup_cb, expr *config_cb,
expr *display_cb, expr *timer_cb,
expr *user_data) = gl_window;
The meaning of the parameters is as follows:
The different callbacks are:
You’ll need at least either the display_cb or the timer_cb function to render anything, but typically all of these callbacks will be needed for animated scenes. Callback functions which aren’t needed can be specified as ().
There’s also a related helper function which can be used as a trigger condition to defer rendering until the target frame widget has been realized:
extern bool pure_check_window(char *name) = check_window;
This function returns true as soon as the named frame widget is ready to go, at wich time gl_windows can be called on the widget. So your call to gl_windows should usually be wrapped up like this:
trigger 0 check_window
(\frame->gl_window frame timeout setup config display timer user_data) frame
Here’s an example from pure_glfunc.pure which shows how these functions are to be used:
using pure_func, GL, GLU;
extern void gdk_gl_draw_teapot(bool solid, double scale);
gnm_info "gltest" = "sbfff";
gltest frame m a b c = trigger 0 check_window
(\frame->gl_window frame (m*40) setup config display timer ()) frame
with
setup _ _ = () when
// Initialize.
GL::ClearColor 0.1 0.1 0.3 1.0;
GL::ShadeModel GL::SMOOTH;
GL::Enable GL::DEPTH_TEST;
// Initial projection and modelview matrices.
GL::MatrixMode GL::PROJECTION;
GL::LoadIdentity;
GL::Rotatef 20.0 (-1.0) 0.0 0.0;
GL::MatrixMode GL::MODELVIEW;
GL::LoadIdentity;
// Lighting.
GL::Lightfv GL::LIGHT0 GL::DIFFUSE {1.0,0.0,0.0,1.0};
GL::Lightfv GL::LIGHT0 GL::POSITION {2.0,2.0,-5.0,1.0};
GL::Enable GL::LIGHTING;
GL::Enable GL::LIGHT0;
end;
config _ (w,h) = GL::Viewport 0 0 w h;
display _ _ = () when
GL::Clear (GL::DEPTH_BUFFER_BIT or GL::COLOR_BUFFER_BIT);
gdk_gl_draw_teapot true 0.5;
end if m;
display _ _ = () when
GL::Clear (GL::DEPTH_BUFFER_BIT or GL::COLOR_BUFFER_BIT);
GL::LoadIdentity;
GL::Rotatef (scale 360 a) 0.0 1.0 0.0;
GL::Rotatef (scale 360 b) 1.0 0.0 0.0;
GL::Rotatef (scale 360 c) 0.0 0.0 1.0;
gdk_gl_draw_teapot true 0.5;
end;
timer _ _ = () when
GL::Rotatef (scale 36 a) 0.0 1.0 0.0;
GL::Rotatef (scale 36 b) 1.0 0.0 0.0;
GL::Rotatef (scale 36 c) 0.0 0.0 1.0;
end;
scale step x = (x/100*step);
end;
Have a look at the gl-example.gnumeric spreadsheet included in the distribution to see this example in action. (You first need to enable the “Pure OpenGL functions” in the Plugin Manager to make this work.) The screenshot below shows how the example looks like in Gnumeric.
Gnumeric/Pure OpenGL example.
Copyright (c) 2009 by Albert Graef.
Gnumeric/Pure is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 2 of the License, or (at your option) any later version.
Gnumeric/Pure is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with this program. If not, see <http://www.gnu.org/licenses/>.