home *** CD-ROM | disk | FTP | other *** search
/ Freelog 65 / Freelog065.iso / BAS / Bureautique / Gnumeric / gnumeric-1.3.92-rc1.exe / extending-python.xml < prev    next >
Text File  |  2004-11-01  |  40KB  |  1,229 lines

  1. <sect1 id="sect-extending-python">
  2.   <title>Programming Gnumeric using Python</title>
  3.  
  4.   <para>
  5.     A powerful way to access and manipulate data in
  6.     <application>Gnumeric</application> involves using the Python
  7.     programming language. As <application>Gnumeric</application>
  8.     develops from version 1.2, the scripting methods will become
  9.     increasingly powerful. Since <application>Gnumeric</application>
  10.     is free software, you could extend it directly using the source
  11.     code and adding C language functions to the code. Python offers a
  12.     higher level abstraction through which to interact with the
  13.     spreadsheet.
  14.   </para>
  15.  
  16.   <para>
  17.     Python and <application>Gnumeric</application> can be used in
  18.     several ways. This section will describe how to obtain
  19.     <application>Gnumeric</application>, install it and get things
  20.     configured correctly for access with Python. If you already have
  21.     the pieces in place, you can skip the section <xref
  22.     linkend="sect-extending-python-install"/>.
  23.   </para>
  24.  
  25.  
  26.   <para>
  27.     This section was written by Charles Twardy. It owes a great deal
  28.     to the nice guide Travis Whitton wrote: <ulink type="http"
  29.     url="http://grub.ath.cx/gnumeric-python/">Python/Gnumeric guide
  30.     for the old API in Gnumeric 1.0.</ulink> Jon Käre Hellan
  31.     contributed most of the code to enable Python in
  32.     <application>Gnumeric</application> and wrote the file
  33.     <literal>python-gnumeric.txt</literal> in the source
  34.     tree. Nathan Hurst provided the idea and support.
  35.   </para>
  36.  
  37.   <warning>
  38.     <para>
  39.       The Python API, that is the list of methods available in Python,
  40.       is still experimental and may change!
  41.     </para>
  42.   </warning>
  43.  
  44.   <para>
  45.     For further information, the web page maintained by Jon Käre
  46.     Hellan's has some python plugins and other useful
  47.     information. That page can be found through <ulink type="http"
  48.     url="http://domen.uninett.no/~jk/gnumeric/">this link</ulink>. The
  49.     main <ulink type="http"
  50.     url="http://www.gnome.org/projects/gnumeric/">Gnumeric
  51.     page</ulink> may also have useful information.
  52.   </para>
  53.  
  54.   <para>
  55.     If you need help online, you may want to check out:
  56.  
  57.       <itemizedlist>
  58.  
  59.         <listitem>
  60.           <para>
  61.             The Gnumeric Function-Writer's Guide. Until I write one
  62.             for Python, you'll have to settle for
  63.             <literal>doc/developer/writing-functions.sgml</literal> in
  64.             the Gnumeric source tree.
  65.           </para>
  66.         </listitem>
  67.  
  68.         <listitem>
  69.           <para>
  70.             The files that actually define the Python interface. In
  71.             particular,
  72.             <literal>plugins/python-loader/py-gnumeric.c</literal> has
  73.             good comments at the beginning.
  74.           </para>
  75.         </listitem>
  76.  
  77.         <listitem>
  78.           <para>
  79.             The instructions on how to use GNOME CVS can be found <ulink
  80.             url="http://developer.gnome.org/tools/cvs.html">here</ulink>.
  81.           </para>
  82.         </listitem>
  83.  
  84.         <listitem>
  85.           <para>
  86.             The gnumeric discussion list:
  87.             <literal><gnumeric-list@gnome.org></literal>
  88.           </para>
  89.         </listitem>
  90.  
  91.         <listitem>
  92.           <para>
  93.             The IRC channel #gnumeric on the GIMPnet server.  Right
  94.             now, the project leader is Jody Goldberg (jody) and the
  95.             Debianizer is: J.H.M. Dassen (jhm). Jody, Jon K. Hellan,
  96.             and Zbigniew Chyla appear prominently in the Python
  97.             ChangeLog.
  98.           </para>
  99.         </listitem>
  100.  
  101.       </itemizedlist>
  102.  
  103.   </para>
  104.   
  105.   <sect2 id="sect-extending-python-install">
  106.     <title>Installing and Building Gnumeric for Python</title>
  107.  
  108.     <para>
  109.       This section describes how to obtain the
  110.       <application>Gnumeric</application> source code, configure it
  111.       for Python and build it. This section will eventually be
  112.       removed as Python becomes supported by default.
  113.     </para>
  114.  
  115.   <sect3 id="sect-extending-python-install-prelim">
  116.     <title>Preliminaries</title>
  117.     
  118.     <para>
  119.       I'm going to define some variables here so that you can insert
  120.       the appropriate command or item for your system when they
  121.       occur. I'll prefix them all with '$'.
  122.     </para>
  123.     
  124.     <itemizedlist>
  125.  
  126.       <listitem>
  127.         <para>
  128.         <emphasis>$root</emphasis>: Do whatever you do to become
  129.         root. The usual options are:
  130.  
  131.         <itemizedlist>
  132.  
  133.           <listitem>
  134.             <para>
  135.               <literal>su -</literal> and hit <keycap>Enter</keycap>
  136.             </para>
  137.           </listitem>
  138.  
  139.           <listitem>
  140.             <para>
  141.             <literal>sudo</literal>
  142.             </para>
  143.           </listitem>
  144.  
  145.           <listitem>
  146.             <para>
  147.             <literal>fakeroot</literal> (works in some situations, but
  148.             not all)
  149.             </para>
  150.           </listitem>
  151.  
  152.         </itemizedlist>
  153.         </para>
  154.       </listitem>
  155.         
  156.        <listitem>
  157.          <para>
  158.            <emphasis>$version</emphasis>: Whatever your current
  159.            Gnumeric version is. Some examples:
  160.  
  161.            <itemizedlist>
  162.              <listitem><para>1.1.20</para></listitem>
  163.              <listitem><para>1.1.20-bonobo</para></listitem>
  164.              <listitem><para>1.1.90</para></listitem>
  165.            </itemizedlist>
  166.          </para>
  167.        </listitem>
  168.      </itemizedlist>
  169.     
  170.   </sect3>
  171.   
  172.   <sect3 id="sect-extending-python-install-build">
  173.     <title>In the Beginning (Installing and Building)</title>
  174.  
  175.     <para>
  176.       You need to get Python and Gnumeric, and the Python plugin for
  177.       Gnumeric. You can get the binaries, the packaged source, or the
  178.       developing edge CVS.
  179.     </para>
  180.     
  181.     <sect4 id="sect-extending-python-install-binaries">
  182.       <title>Getting the binaries (Debian)</title>
  183.  
  184.       <para>
  185.         I've only tested this on sid (unstable). The version you get
  186.         from stable (woody) may not act quite the same.
  187.       </para>
  188.  
  189.       <procedure>
  190.         <step>
  191.           <para>
  192.             <emphasis>$root</emphasis> <literal>apt-get install
  193.             gnumeric gnumeric-python python</literal>
  194.           </para>
  195.         </step>
  196.       </procedure>
  197.     </sect4>
  198.  
  199.     <sect4 id="sect-extending-python-install-debsource">
  200.       <title>
  201.         Getting and building the current Debianized source
  202.       </title>
  203.  
  204.       <para>
  205.         If you have Debian, and don't need the bleeding edge, this is
  206.         <emphasis>by far</emphasis> the easiest way to get and build
  207.         the source.
  208.       </para>
  209.  
  210.       <procedure>
  211.  
  212.           <step>
  213.             <para>
  214.               Change to a directory where you want to hang the source
  215.               directory.
  216.             </para>
  217.           </step>
  218.  
  219.           <step>
  220.             <para>
  221.               <emphasis>$root</emphasis> <literal>apt-get build-dep
  222.               gnumeric</literal>
  223.             </para>
  224.           </step>
  225.  
  226.           <step>
  227.             <para>
  228.               <literal>apt-get source gnumeric</literal>
  229.             </para>
  230.           </step>
  231.  
  232.           <step>
  233.             <para>
  234.               <literal>cd gnumeric-</literal><emphasis>$version</emphasis>
  235.             </para>
  236.           </step>
  237.  
  238.           <step>
  239.             <para>
  240.               <literal>debian/rules build</literal>
  241.             </para>
  242.           </step>
  243.  
  244.           <step>
  245.             <para>
  246.               To make the .deb packages: <emphasis>$root</emphasis>
  247.             <literal>./debian/rules binary</literal>
  248.             </para>
  249.           </step>
  250.  
  251.           <step>
  252.             <para>
  253.               To install those .deb packages:
  254.             </para>
  255.             <procedure>
  256.  
  257.               <step>
  258.                 <para>
  259.                   <literal>cd ..</literal> to change to that directory.
  260.                 </para>
  261.               </step>
  262.  
  263.               <step>
  264.                 <para>
  265.                   <emphasis>$root</emphasis> <literal>dpkg -i
  266.                   gnum*deb</literal> (presuming you don't have other
  267.                   .deb packages beginning with "gnum" lying around
  268.                   here.
  269.                 </para>
  270.               </step>
  271.  
  272.             </procedure>
  273.           </step>
  274.  
  275.           <step>
  276.             <para>
  277.               You may or may not want to remove those .deb files now:
  278.               <emphasis>$root</emphasis> <literal>rm
  279.               gnum*deb</literal>)
  280.             </para>
  281.            </step>
  282.        </procedure>
  283.      </sect4>
  284.  
  285.     <sect4 id="sect-extending-python-install-cvs">
  286.       <title>Getting and building the source from CVS-HEAD</title>
  287.       
  288.       <para>
  289.         Remember that this is the developing edge. Things may not
  290.         work. Generally don't do this unless you are subscribed to the
  291.         mail list and possibly also on the IRC channel.
  292.       </para>
  293.   
  294.       <para>
  295.         You will need a few things for this to work at all:
  296.       </para>
  297.  
  298.       <procedure>
  299.  
  300.            <step><para>gnome-common</para></step>
  301.  
  302.            <step><para>libgsf (see below)</para></step>
  303.  
  304.            <step>
  305.              <para>
  306.                pygtk2 (On Debian, make sure to get python-gtk2 and
  307.                python-gtk2-dev)
  308.              </para>
  309.            </step>
  310.  
  311.            <step><para>gnumeric (see below, obviously)</para></step>
  312.  
  313.       </procedure>
  314.  
  315.       <para>
  316.         And although the following will build in the main build space,
  317.         it's probably better to build in a temporary space. But I
  318.         can't be bothered to learn how to fiddle the build pathways.
  319.       </para>
  320.       
  321.       <procedure>
  322.           <step>
  323.             <para>
  324.               Change to a directory where you want to hang the source
  325.               directory for Gnumeric and a few other Gnome things.
  326.             </para>
  327.           </step>
  328.  
  329.           <step>
  330.             <para>
  331.               <literal>export
  332.               CVSROOT=:pserver:anonymous@anoncvs.gnome.org:/cvs/gnome</literal>
  333.             </para>
  334.           </step>
  335.  
  336.           <step>
  337.             <para>
  338.               <literal>cvs login</literal> (No password -- hit
  339.               RETURN.)
  340.             </para>
  341.           </step>
  342.  
  343.           <step>
  344.             <para>
  345.               Getting and building libgsf:
  346.             </para>
  347.  
  348.             <procedure>
  349.  
  350.               <step>
  351.                 <para>
  352.                   cvs co libgsf
  353.                 </para>
  354.               </step>
  355.  
  356.               <step>
  357.                 <para>
  358.                   cd libgsf
  359.                 </para>
  360.               </step>
  361.  
  362.               <step>
  363.                 <para>
  364.                   RedHat: <literal>./autogen.sh</literal>
  365.                 </para>
  366.               </step>
  367.  
  368.               <step>
  369.                 <para>
  370.                   Debian: <literal>./autogen.sh --prefix=/usr
  371.                 --with-gconf-schema-file-dir=/etc/gconf/schemas</literal>
  372.                 </para>
  373.               </step>
  374.  
  375.               <step>
  376.                 <para>
  377.                   <literal>make</literal>
  378.                 </para>
  379.               </step>
  380.  
  381.               <step>
  382.                 <para>
  383.                   <emphasis>$root</emphasis> <literal>make install</literal>
  384.                 </para>
  385.               </step>
  386.  
  387.               <step>
  388.                 <para>
  389.                   If you find that this didn't work, try <literal>make
  390.                   clean</literal> and then repeat from the autogen
  391.                   step.
  392.                 </para>
  393.               </step>
  394.             </procedure>
  395.  
  396.           </step>
  397.  
  398.           <step>
  399.              <para>
  400.                Getting and building libgal <emphasis
  401.                role="bold">No longer necessary! (13 June
  402.                2003)</emphasis>
  403.              </para>
  404.       </step>
  405.  
  406.       <step>
  407.             <para>
  408.               Getting and building gnumeric:
  409.             </para>
  410.           <procedure>
  411.             <step><para><literal>cvs -z3 checkout gnumeric -d
  412.                 gnumeric-head</literal></para></step>
  413.             <step><para><literal>cd gnumeric-head</literal></para></step>
  414.             <step><para>RedHat: <literal>./autogen.sh</literal> and wait while it
  415.               compiles</para></step>
  416.             <step><para>Debian: <literal>./autogen.sh --prefix=/usr --with-gconf-schema-file-dir=/etc/gconf/schemas</literal></para></step>
  417.             <step><para><literal>make</literal></para></step>
  418.             <step><para><emphasis role="bold">Optional:</emphasis> <emphasis>$root</emphasis> <literal>make install</literal></para></step>
  419.             <step><para>If you find that this didn't work, try <literal>make clean</literal> and
  420.               then repeat from the autogen step. For example,
  421.               sometimes I've had it not create the python-loader.</para></step>
  422.           </procedure>
  423.        </step>
  424.  
  425.       </procedure>
  426.  
  427.     <para>
  428.           OK, you should now have gnumeric! Test it! If you installed
  429.           the Debianized version via apt-get, or did "make install",
  430.           it should be installed to /usr/bin (or /usr/local/bin on
  431.           RedHat?)  and you can just type
  432.           <literal>gnumeric</literal>. Otherwise you will find it in
  433.           <literal>gnumeric-head/src/</literal> and you will have to
  434.           run it from there. 
  435.         </para>
  436.  
  437.     </sect4>
  438.  
  439.   </sect3>
  440.  
  441.   </sect2>
  442.  
  443.  
  444.   <sect2 id="sect-extending-python-console">
  445.     <title>The Python Console</title>
  446.  
  447.     <para>
  448.       There is an interactive Python console available from inside
  449.       Gnumeric. This is a good place to explore things, and if the
  450.       console is expanded, will be a nice place for scripting. In the
  451.       meantime, what I have called "Spellbooks" below are much more
  452.       useful, but are fixed plugins as of Gnumeric startup. So right
  453.       now I putter in the console as I develop plugin literal in the
  454.       form of spellbooks. After 1.2.0, Gnumeric will be working on its
  455.       scripting API, so the two approaches may merge. Or not.
  456.     </para>
  457.  
  458.     <sect3 id="sect-extending-python-console-enabling">
  459.       <title>Enabling the Python Console</title>
  460.  
  461.       <para>
  462.         You can run a Python interpreter from inside Gnumeric, but you
  463.         have to turn it on. To do this you simply uncomment a line in
  464.         python-loader/plugins.xml. Normally, that file lives in
  465.         /usr/lib/gnumeric/<emphasis>$version</emphasis>/plugins/python-loader/,
  466.         or perhaps <emphasis>/usr/local/lib...</emphasis> on RedHat.
  467.         I used to suggest making a local but you should probably make
  468.         a local copy, but that was pain for little gain. So:
  469.       </para>
  470.  
  471.         <procedure>
  472.  
  473.       <step>
  474.             <para>
  475.               <literal>gnumeric --version</literal> to make sure you
  476.               get the right version name for the following. (You'll
  477.               have to do this for every new version of Gnumeric!).
  478.             </para>
  479.           </step>
  480.  
  481.       <step>
  482.             <para>
  483.               <literal>cd ~/.gnumeric/</literal>
  484.                 <emphasis>$version</emphasis>
  485.                  <literal>/plugins/</literal>
  486.             </para>
  487.           </step>
  488.  
  489.       <step>
  490.             <para>
  491.               Edit <literal>python-loader/plugin.xml</literal>.
  492.             </para>
  493.           </step>
  494.  
  495.       <step>
  496.            <para>
  497.                Uncomment the five lines starting with
  498.                <literal>ui-console-menu service</literal> near the
  499.                bottom (remove the "<!--" and "-->" tags around
  500.                the <service...> and </service> tags.
  501.             </para>
  502.           </step>
  503.  
  504.       <step>
  505.             <para>
  506.               Save the file.
  507.             </para>
  508.           </step>
  509.  
  510.       <step>
  511.             <para>
  512.               Start gnumeric (same version).
  513.             </para>
  514.           </step>
  515.  
  516.       <step>
  517.             <para>
  518.               Select from the <guimenu>Tools</guimenu> the <guimenuitem>Python
  519.               console</guimenuitem>.
  520.             </para>
  521.           </step>
  522.  
  523.           <step>
  524.             <para>
  525.               <emphasis>Enjoy!</emphasis>
  526.             </para>
  527.           </step>
  528.  
  529.         </procedure>
  530.  
  531.     </sect3>
  532.  
  533.     <sect3 id="sect-extending-python-console-playing">
  534.       <title>Playing with the Python console</title>
  535.  
  536.       <para>
  537.         At the top there is a drop-down menu <guimenu>Execute
  538.         in</guimenu>. Right now your only choice will be
  539.         <guimenuitem>Default</guimenuitem>. After you evaluate functions
  540.         from other plugins, those environments will become available
  541.         too (JK says this is called lazy loading). But I'll assume you
  542.         are using Default. (The only real difference is that you have
  543.         to import Gnumeric first, and you can't see your plugin
  544.         functions.)
  545.       </para>
  546.   
  547.       <para>
  548.         (Note: older releases required you to type <literal>print
  549.         dir()</literal> instead of just
  550.         <literal>dir()</literal>. Fixed in cvs 16 June 2003, and
  551.         certainly in 1.1.20 and higher.
  552.       </para>
  553.   
  554.       <para>
  555.         Let's start by taking a look at the environment.
  556.       </para>
  557.  
  558.       <programlisting>
  559. >>> import <co id="gnumeric"></co>Gnumeric
  560. >>> dir()
  561. ['Gnumeric', '__builtins__', '__doc__', '__name__']
  562. >>> dir(Gnumeric)
  563. ['Boolean', 'CellPos', 'FALSE', 'GnumericError', 'GnumericErrorDIV0',
  564. 'GnumericErrorNA', 'GnumericErrorNAME', 'GnumericErrorNULL',
  565. 'GnumericErrorNUM', 'GnumericErrorRECALC', 'GnumericErrorREF',
  566. 'GnumericErrorVALUE', 'MStyle', 'Range', 'TRUE', '__doc__',
  567. '__name__', <co id="functions"></co>'functions', 'plugin_info', 'workbook_new', 'workbooks'] 
  568.  
  569.       </programlisting>
  570.  
  571.       <calloutlist>
  572.       <callout arearefs="gnumeric">
  573.         <para>
  574.           'Gnumeric' is a module that exists only within Gnumeric, and
  575.           which defines the Gnumeric Python API.
  576.         </para>
  577.       </callout>
  578.  
  579.       <callout arearefs="functions">
  580.         <para>
  581.           Gnumeric.functions is the list of all the Gnumeric functions
  582.           you would see in the function browser. You cannot yet do
  583.           <literal>dir(Gnumeric.functions)</literal> but maybe someone
  584.           will bind that soon.
  585.         </para>
  586.  
  587.         <para>
  588.           RangeRef is not listed. That seems to limit us, though later
  589.           in the tutorial we'll see how to use regular functions to
  590.           get inside RangeRefs.
  591.         </para>
  592.  
  593.       </callout>
  594.       </calloutlist>
  595.  
  596.       <para>
  597.         So do some exploring. First, let's poke around to figure out
  598.         how to use CellPos.
  599.       </para>
  600.  
  601.       <programlisting>
  602. # I wonder how to use CellPos (I've glanced at the source, but...)
  603.  
  604. >>> dir(Gnumeric.CellPos)                 # shows nothing useful
  605.  
  606. >>> Gnumeric.CellPos()                    
  607. TypeError: CellPos() takes exactly 2 arguments (0 given)  
  608.  
  609. >>> Gnumeric.CellPos("a1","a2") 
  610. TypeError: an integer is required.        # Right. 
  611.  
  612. >>> a=Gnumeric.CellPos(1,2)               # It worked!
  613. >>> a
  614. <CellPos object at 0x106b6eb8>      # Yeah, I know...
  615.  
  616. >>> dir(a)
  617. ['get_tuple']
  618.  
  619. >>> a.get_tuple()
  620. (1,2)                                     # Cool. That's (col,row)
  621.  
  622. >>> str(a)                                # Super cool.
  623. 'B3'                                      # JK hasn't implemented this for tuples yet
  624.        </programlisting>
  625.  
  626.  
  627.        <para>
  628.          Similarly, we can explore Gnumeric.Range:
  629.        </para>
  630.       
  631.        <programlisting>
  632. >>> r = Gnumeric.Range((1,2),(3,4))
  633. TypeError: Range() argument 1 must be CellPos, not tuple
  634.  
  635. >>> r = Gnumeric.Range(a,a)
  636. >>> r
  637. <Range object at 0x1071d888>
  638.  
  639. >>> dir(r)
  640. ['get_tuple']
  641.  
  642. >>> r.get_tuple()
  643. (3, 7, 3, 7)
  644.       </programlisting>
  645.  
  646.       <para>If you
  647.         evaluate in the context of a plugin (rather than in Default), then
  648.         <literal>dir(Gnumeric.plugin_info)</literal> will reveal some simple
  649.         informational functions you can call for the local plugin(s).</para>
  650.       
  651.       <para>Note: obviously I don't really know what I'm doing, or I wouldn't
  652.         be poking around like this.</para>
  653.  
  654.     </sect3>
  655.  
  656.     <sect3 id="sect-extending-python-console-morefun">
  657.       <title>More fun with the Python console</title>
  658.  
  659.      <para>Jon K. Hellan writes, "Here are some more things you can do
  660.       from the console:"</para>
  661.       <programlisting>
  662. # Get a workbook
  663. >>> wb=Gnumeric.workbooks()[0]
  664. >>> wb
  665. <Workbook object at 0x862a490>
  666. >>> dir(wb)
  667. >>> ['gui_add', 'sheet_add', 'sheets']
  668.  
  669. # Get a sheet
  670. >>> s=wb.sheets()[0]
  671. >>> s
  672. <Sheet object at 0x863e8d0>
  673. >>> dir(s)
  674. ['cell_fetch', 'get_extent', 'get_name_unquoted', 'rename',
  675. 'style_apply_range', 'style_get', 'style_set_pos', 'style_set_range']
  676.  
  677. # Get a cell. s.cell_fetch(0,0) and s[0,0] are synonyms. First
  678. # coordinate is columns, i.e. s[1,0] is B1.
  679. >>> c=s[0,0]
  680. >>> c
  681. <Cell object at 0x863d810>
  682. >>> dir(c)
  683. ['get_entered_text', 'get_mstyle', 'get_rendered_text', 'get_value',
  684. 'get_value_as_string', 'set_text']
  685.  
  686. # Change the cell - it changes in the grid
  687. >>> c.set_text('foo')
  688.  
  689. # Retrieve the cell - both ways.
  690. >>> c.get_value()
  691. foo
  692. >>> s.cell_fetch(0,0).get_value()
  693. foo
  694. </programlisting>
  695.  
  696.       <para>Very, very nice.  Note, after setting a value, it won't show up
  697.         until that cell is redrawn. That will happen automatically with plugin
  698.         functions, but in the console, you may have to click on the cell.
  699.       </para>
  700.  
  701.     </sect3>
  702.   </sect2>
  703.  
  704.   <sect2 id="sect-extending-python-builtins">
  705.     <title>Using the built-in Python functions</title>
  706.     
  707.     <para>
  708.       To enable the Python-loader and Python plugins:
  709.     </para>
  710.  
  711.     <procedure>
  712.  
  713.        <step>
  714.          <para>
  715.            Select the <guimenu>Tools</guimenu> menu and the
  716.            <guimenuitem>Plugins</guimenuitem> menuitem.
  717.          </para>
  718.        </step>
  719.  
  720.        <step>
  721.          <para>
  722.            Select "Python plugin loader" and "Python
  723.            functions". Restart Gnumeric.
  724.          </para>
  725.        </step>
  726.  
  727.      </procedure>
  728.  
  729.     <para>The quickest way to test whether you now have Python functions
  730.       is to type <literal>=py_capwords("fred flintstone")</literal> in the
  731.       first cell. After you hit <Enter>, you should see "Fred
  732.       Flintstone".</para>
  733.  
  734.     <para>You can also click on the functions button, and scroll down to
  735.       the "Python" category. Select that. You should see at
  736.       least two functions defined: PY_CAPWORDS and PY_PRINTF. They're
  737.       not very useful, but they prove you've got the plugins. Test
  738.       them either via the GUI or by typing into the cell.</para>
  739.  
  740.     <para>I'll presume they worked.</para>
  741.   </sect2>
  742.  
  743.   <sect2 id="sect-extending-python-writing">
  744.     <title>Writing your own Python functions</title>
  745.  
  746.     <para>To scribe new magic you must write your spells in places where
  747.       Gnumeric will find them. That place is in folders under:
  748.       <literal>~/.gnumeric/<version>/plugins/</literal>
  749.       Each folder under here is one "spellbook" of new plugin
  750.       functions. You may put all your spells in one spellbook, or group
  751.       them neatly depending on your tastes. Each spellbook must have two
  752.       files. We'll create a spellbook called "myfuncs". A pedestrian name
  753.       for pedestrian spells. When I have more skill, perhaps I'll make
  754.       some with better names. Several suggest themselves:
  755.       <itemizedlist>
  756.         <listitem><para>Transformations: of obvious value for a spreadsheet</para></listitem>
  757.         <listitem><para>Illusions: statistical functions, clearly</para></listitem>
  758.         <listitem><para>Charms: presentation graphics</para></listitem>
  759.         <listitem><para>Necromancy: file repair and missing values?</para></listitem>
  760.         <listitem><para>Divination: data mining!</para></listitem>
  761.       </itemizedlist>
  762.     </para>
  763.  
  764.     <sect3 id="sect-extending-python-writing-prepare">
  765.       <title>Prepare the spellbook</title>
  766.  
  767.     <para>In many ways it would be easier to start by copying the
  768.       py_func spellbook to your local .gnumeric folder, and just adding a
  769.       function to that. But in general it will be more useful to be
  770.       able to write your own separate spellbooks, so here we go.</para>
  771.  
  772.       <procedure>
  773.         <step>
  774.           <para>
  775.             <emphasis role="bold">Make the folder: </emphasis>
  776.             First we make the folders and get into the right one. As noted
  777.             above, we'll call our folder (spellbook) myfuncs.
  778.           </para>
  779.           <substeps>
  780.             <step><para>If they don't already exist:</para>
  781.               <substeps>
  782.                 <step><para><literal>mkdir ~/.gnumeric</literal></para></step>
  783.                 <step><para><literal>mkdir ~/.gnumeric/<version></literal></para></step>
  784.               </substeps>
  785.             </step>
  786.             <step><para><literal>mkdir ~/.gnumeric/<version>/myfuncs/</literal></para></step>
  787.             <step><para><literal>cd ~/.gnumeric/<version>/myfuncs/</literal></para></step>
  788.           </substeps>
  789.         </step>
  790.  
  791.         <step>
  792.           <para>
  793.             <emphasis role="bold">Make the files:</emphasis>
  794.             A spellbook has two files. The first is the python file
  795.             with the functions. The second is the XML file "plugin.xml". The
  796.             XML file holds that master spells that tell Gnumeric what
  797.             functions we've defined, and what the name of the python file
  798.             <emphasis>is</emphasis>, and one other important item. We'll create these as
  799.             blank files.
  800.           </para>
  801.           <substeps>
  802.             <step><para><literal>touch my-func.py</literal></para></step>
  803.             <step><para><literal>touch plugin.xml</literal></para></step>
  804.           </substeps>
  805.         </step>
  806.  
  807.         <step>
  808.           <para>
  809.             <emphasis role="bold">Write the master spells</emphasis>
  810.             The good news is that you only need to do this once per
  811.             spellbook. After that you just add spells to it.
  812.           </para>
  813.           <para>Your XML file must tell Gnumeric about your plugin. Here is a
  814.             simple template. (If you want to learn about internationalization,
  815.             see the example in the system's py-func spellbook.) Open up
  816.             plugin.xml and insert the following lines:
  817.           </para>
  818.  
  819.           <programlisting>
  820. <?xml version="1.0"?>
  821. <plugin id="Gnumeric_MyFuncPlugin">
  822.     <information>
  823.         <name>Other Python functions from HOWTO</name>
  824.         <description>A few extra python functions demonstrating the API.</description>
  825.     </information>
  826.     <loader type="Gnumeric_PythonLoader:python">
  827.         <attribute name="module_name" value="<emphasis
  828.               role="bold">my-func</emphasis>"/> <co id="my-func"></co>
  829.     </loader>
  830.     <services>
  831.         <service type="function_group" id="<emphasis
  832.                 role="bold">example</emphasis>"> <co id="example"></co>
  833.             <category>Local Python</category>
  834.             <functions>
  835.             </functions>
  836.         </service>
  837.     </services>
  838. </plugin>
  839.           </programlisting>
  840.  
  841.                   <calloutlist>
  842.           <callout arearefs="my-func">
  843.             <para>
  844.               The value of "name" determines the name of your python
  845.               script (file). In this case, it must be "my-func.py"
  846.             </para>
  847.           </callout>
  848.  
  849.           <callout arearefs="example">
  850.             <para>
  851.               The value of "id" here determines the name of the
  852.               function dictionary in your python script. In this case,
  853.               it must be "example_functions" because here the value is
  854.               "example".
  855.             </para>
  856.           </callout>
  857.                   </calloutlist>
  858.         </step>
  859.  
  860.         <step>
  861.           <para>
  862.             <emphasis role="bold">Prepare to write the
  863.             spells:</emphasis> Next we'll create a minimal python
  864.             file. As noted above, we must name the file
  865.             <emphasis
  866.             role="bold">my-func</emphasis>.py and it must have a dictionary
  867.             called <emphasis role="bold">example</emphasis>_functions.
  868.             So open up my-func.py and insert the following lines.
  869.           </para>
  870.           <programlisting>
  871. # my-func.py
  872. #
  873.  
  874. from Gnumeric import GnumericError GnumericErrorVALUE
  875. import Gnumeric
  876. import string
  877.     
  878. example_functions = {
  879. }
  880.           </programlisting>
  881.         </step>
  882.       </procedure>
  883.     </sect3>
  884.  
  885.     <sect3 id="sect-extending-python-writing-newspells">
  886.       <title>Writing new spells</title>
  887.  
  888.       <para>To add new functions to Python, you now must do five things
  889.         (three sir!):</para>
  890.  
  891.       <procedure>
  892.         <step><para>Write the python function in your copy of
  893.           <literal>my-func.py</literal>.</para></step>
  894.       
  895.         <step><para>Insert the function info into the <literal>example_functions</literal>
  896.           dictionary at the end of <literal>my_func.py</literal></para></step>
  897.       
  898.         <step><para>Insert the function name into the functions list at the end of
  899.           <literal>plugin.xml</literal>.</para></step>
  900.       </procedure>
  901.     
  902.     
  903.       <para>
  904.         <emphasis role="bold">Writing a simple script:</emphasis>
  905.         Let's do something very simple: add two numbers
  906.         together. First, edit my-func.py.</para>
  907.       <programlisting>
  908.     <emphasis># Add two numbers together</emphasis>
  909.     def func_add(num1, num2):
  910.         return num1 + num2
  911.  
  912.     <emphasis># Translate the func_add python function to a gnumeric function and register it</emphasis>
  913.     example_functions = {
  914.         'py_add': func_add
  915.     }
  916.       </programlisting>
  917.  
  918.     <para>Then let the plugin-loader(?) know about your function. Add the
  919.     following line near the end of plugin.xml (between
  920.       <functions> and </functions>).</para>
  921.     <programlisting>
  922.                  <function name="py_add"/>
  923.     </programlisting>
  924.  
  925.     <para>Now start Gnumeric and type <literal>py_add(2,3)</literal> into a
  926.       cell. You should get "5". You can also use cell references. If
  927.       that was in cell A1, go to cell A2 and type
  928.       <literal>py_add(A1,3)</literal> and you will get "8".  But your
  929.       function won't show up in the GUI list yet.</para>
  930.  
  931.     <para>
  932.         <emphasis role="bold">Tell the GUI:</emphasis>
  933.         To make your function show up in the GUI, you have to tell
  934.         Gnumeric some things about it via a standard header, like
  935.         this:</para>
  936.       <programlisting>
  937.     <emphasis># Add two numbers together</emphasis>
  938.     def func_add(num1, num2):
  939.         '@FUNCTION=PY_ADD\n'\
  940.         '@SYNTAX=py_add(num1, num2)\n'\
  941.         '@DESCRIPTION=Adds two numbers together.\n'\
  942.         'Look, the description can go onto other lines.\n\n'\
  943.         '@EXAMPLES=To add two constants, just type them in: py_add(2,3)\n'\
  944.         'To add two cells, use the cell addresses: py_add(A1,A2)\n\n'\
  945.         '@SEEALSO='
  946.  
  947.         return num1 + num2
  948.       </programlisting>
  949.  
  950.       <para>The text after '@DESCRIPTION=' is the description that shows up
  951.         in the function GUI. You can make it as simple or detailed as you
  952.         want. I'm not sure how many other fields get used right now, as I
  953.         haven't seen the EXAMPLES show up anywhere.</para>
  954.       
  955.       <para>But this still isn't quite right. Gnumeric doesn't know how
  956.         many arguments the function can handle, nor of what type. So the
  957.         function GUI will prompt for the two values it knows about (as
  958.         type "Any") and then keep prompting for more. But py_add cannot
  959.         accept all types, nor can it handle more than two arguments, so
  960.         unless you give it precisely 2 numbers, you will get an error when
  961.         you click "OK".</para>
  962.       
  963.       <para>
  964.         <emphasis role="bold">Know your limits...</emphasis>
  965.         We got away last time just because Gnumeric was forgiving. Now
  966.         we need to say that we can accept only 2 values, of type
  967.         floating-point (which will also handle ints).</para>
  968.       
  969.       <para>Where before we had: <literal>'py_add': func_add</literal>, 
  970.         we should now put: <literal>'py_add': ('ff','num1,num2',func_add)</literal>
  971.         This says that Gnumeric should expect two floating-point numbers
  972.         ('ff') with names 'num1' and 'num2', and pass them to func_add.</para>
  973.       
  974.       <para>
  975.         <emphasis role="bold">...and surpass them</emphasis>
  976.         Of course, there is no reason an add function shouldn't be able
  977.         to handle a range. The simplest way to do that is to accept a
  978.         range, and then call Gnumeric's own SUM function on it! All of
  979.         Gnumeric's functions are available to you in the dictionary
  980.         Gnumeric.functions, keyed by name. So here is how to write py_sum.
  981.       </para>
  982.  
  983.     <procedure>
  984.       <step>
  985.           <para>First, define func_sum (in my-func.py):</para>
  986.           
  987.           <programlisting>
  988. def func_sum(gRange):
  989.     '@FUNCTION=PY_SUM\n'\
  990.     '@SYNTAX=PY_SUM(range)\n'\
  991.     '@DESCRIPTION=Adds a range of numbers together.'\
  992.     'Just like built-in SUM.\n\n'\
  993.     '@EXAMPLES=To add values in A1 to A5, just type them in:\n'\
  994.     '    py_sum(a1:a5)\n'\
  995.     '@SEEALSO='
  996.     try:
  997.         sum = Gnumeric.functions['sum']
  998.         val = sum(gRange)
  999.         #  val = reduce(lambda a,b: a+b, vals)
  1000.     except TypeError:
  1001.         raise GnumericError, GnumericErrorVALUE
  1002.     else:
  1003.         return val
  1004.           </programlisting>
  1005.         </step>
  1006.  
  1007.         <step><para>Then insert it into your functions dictionary. That
  1008.         dictionary now looks like this (with 'r' denoting a range type):</para>
  1009.  
  1010.           <programlisting>
  1011. example_functions = {
  1012.     'py_add': ('ff','num1,num2',func_add),
  1013.     'py_sum': ('r', 'values', func_sum)
  1014. }
  1015.           </programlisting>
  1016.         </step>
  1017.  
  1018.         <step><para>Finally, make an entry in the XML list, so that it now looks
  1019.           like:</para>
  1020.           <programlisting>
  1021.             <functions>
  1022.                 <function name="py_add"/>
  1023.                 <function name="py_sum"/>
  1024.             </functions>
  1025.           </programlisting>
  1026.         </step>
  1027.       </procedure>
  1028.  
  1029.       <para>I told you this was the easy way to do it. Obviously it's not
  1030.         very useful to just duplicate Gnumeric functions. But that's as
  1031.         far as I've made it. From what can tell, range objects are
  1032.         packaged as opaque pointers of type RangeRefObject. There seems
  1033.         to be no way to work with them from within Python, so we must
  1034.         rely on the Gnumeric functions.</para>
  1035.  
  1036.     </sect3>
  1037.  
  1038.     <sect3 id="sect-extending-python-writing-newspells2">
  1039.       <title>Do it yourself (mostly)</title>
  1040.  
  1041.     <para>All is not lost, despite the opaque pointers. For in Gnumeric
  1042.     we can read about all the functions that have been defined. Some
  1043.     of those take references (including RangeRefs) and return useful
  1044.     information. For example, under "Lookup" we find "Column" and
  1045.     "Row" which return arrays of all the column (or row) indices in
  1046.     the range. So we can redo the sum function.</para>
  1047.  
  1048.     <para>Presume we can convert our RangeRef to a start tuple and and
  1049.     end tuple. Then we can write sum2:
  1050.         <programlisting>
  1051. def func_sum2(gRange):
  1052.     '@FUNCTION=PY_SUM2\n'\
  1053.     '@SYNTAX=PY_SUM2(range)\n'\
  1054.     '@DESCRIPTION=Adds a range of numbers together,'\
  1055.     'without calling built-in SUM.\n\n'\
  1056.     '@EXAMPLES=To add values in A1 to A5, just type them in:\n'\
  1057.     '    py_sum(a1:a5)\n'\
  1058.     '@SEEALSO='
  1059.     try:
  1060.         [r_begin, r_end] = range_ref_to_tuples(gRange)
  1061.         wb=Gnumeric.Workbooks()[0]   # Careful! This is WRONG! It doesn't
  1062.         s=wb.sheets()[0]             # use the ACTUAL workbook or sheet.
  1063.  
  1064.         val = 0
  1065.         for col in range(r_begin[0], r_end[0]):
  1066.             for row in range(r_begin[1], r_end[1]):
  1067.                 cell = s[col, row]
  1068.                 val = val + cell.get_value()
  1069.                 # Note: this doesn't skip blank cells etc.
  1070.  
  1071.     except TypeError:
  1072.         raise GnumericError,GnumericErrorVALUE
  1073.     else:
  1074.         return val
  1075.         </programlisting>
  1076.       </para>
  1077.  
  1078.     <para>That's fine as far as it goes, but we need to define the helper
  1079.     function "range_ref_to_tuples". Although I'm rather ashamed to
  1080.     show this ugly literal, here's how I did it (someone suggest a
  1081.     better way, please!):
  1082.         <programlisting>
  1083. def range_ref_to_tuples(range_ref):
  1084.     '''I need a function to find the bounds of a RangeRef. This one
  1085.     extracts them from the Gnumeric "column" and "row" commands, and
  1086.     returns them as a pair of tuples. Surely there is a better way?
  1087.     For example, return a list of cells??'''
  1088.  
  1089.     col  = Gnumeric.functions['column']   
  1090.     row  = Gnumeric.functions['row']
  1091.  
  1092.     # "column" and "row" take references and return an array of col or row
  1093.     # nums for each cell in the reference. For example, [[1, 1, 1], [2, 2, 2]]
  1094.     # for columns and [[2, 3, 4], [2, 3, 4]] for rows.
  1095.  
  1096.     try:
  1097.         columns = col(range_ref)
  1098.         rows    = row(range_ref)
  1099.  
  1100.         begin_col = columns[0][0] - 1  
  1101.         begin_row = rows[0][0] - 1     
  1102.  
  1103.         end_col = columns[-1][-1]
  1104.         end_row = rows[-1][-1]
  1105.  
  1106.         # We subtracted 1 from the begin values because in the API,
  1107.         # indexing begins at 0, while "column" and "row" begin at 1.
  1108.         # We did NOT subtract 1 from the end values, in order to make
  1109.         # them suitable for Python's range(begin, end) paradigm.
  1110.         
  1111.     except TypeError:
  1112.         raise GnumericError,GnumericErrorVALUE
  1113.     except NameError:                     # right name?
  1114.         raise GnumericError,Gnumeric.GnumericErrorNAME
  1115.     except RefError:                     # right name?
  1116.         raise GnumericError,Gnumeric.GnumericErrorREF
  1117.     except NumError:                     # right name?
  1118.         raise GnumericError,Gnumeric.GnumericErrorNUM
  1119.  
  1120.  
  1121.     return [ (begin_col, begin_row), (end_col, end_row) ]
  1122.         </programlisting>
  1123.       </para>
  1124.  
  1125.       <para>From there, insert the function into the dictionary, and insert
  1126.         its name into <literal>plugin.xml</literal>. I leave these as exercises
  1127.         to the reader (answers in the sample files -- no
  1128.         peeking!). Restart Gnumeric and you should be able to use
  1129.         py_sum2!</para>
  1130.       
  1131.       <para>There are a couple of glitches:</para>
  1132.       <itemizedlist>
  1133.         <listitem><para>It fails the first time with "could not import
  1134.           gobject". Just run again, I don't know what that's about.</para></listitem>
  1135.         
  1136.         <listitem><para>It will only work for Workbook 1 and Sheet 1. JK thinks that
  1137.           there may be no way to get the current Workbook/Sheet in the
  1138.           Python API. Hrm....</para></listitem>
  1139.         
  1140.         <listitem><para>As noted, it should do some simple trapping to skip blank or
  1141.       text-filled cells. That <emphasis>can</emphasis> be done! I just didn't. It's
  1142.       late.</para></listitem>
  1143.     </itemizedlist>
  1144.     </sect3>
  1145.  
  1146.     <sect3 id="extending-python-writing-help">
  1147.       <title>More help</title>
  1148.       
  1149.       <para>Relative to the source tree:</para>
  1150.       
  1151.       <itemizedlist>
  1152.         <listitem><para>The Python interface is defined in: <literal>plugins/python-loader/py-gnumeric.c</literal>
  1153.           That file also has good notes at the beginning.</para></listitem>
  1154.         
  1155.         <listitem><para>There are interesting things about the way it used to be in:
  1156.           <literal>doc/developer/python-gnumeric.txt</literal>.</para></listitem>
  1157.       </itemizedlist>
  1158.  
  1159.     </sect3>
  1160.     
  1161.     <sect3 id="extending-python-writing-programs">
  1162.       <title>Program Listings</title>
  1163.       
  1164.       <para>You can see my examples in full, with more comments:
  1165.         <itemizedlist>
  1166.           <listitem><para><ulink url="myfuncs/my-func.py"><literal>my-func.py</literal></ulink></para></listitem>
  1167.           <listitem><para><ulink url="myfuncs/plugin.xml"><literal>plugin.xml</literal></ulink></para></listitem>
  1168.         </itemizedlist>
  1169.       </para>
  1170.     </sect3>
  1171.   </sect2>
  1172.  
  1173.  
  1174.   <sect2 id="extending-python-upgrading">
  1175.     <title>Upgrading</title>
  1176.     
  1177.     <para>To upgrade, first choose any method from the installation
  1178.     section above. But note: when you upgrade your Gnumeric version,
  1179.     it will look for your Python scripts in the corresponding
  1180.     version-named subdirectories. For example, if your scripts are in
  1181.     "~/.gnumeric/1.1.17/plugins", but you just upgraded to 1.1.18, you
  1182.     may need to rename that to "~/.gnumeric/1.1.18/plugins". If you
  1183.     want to keep and run several versions of Gnumeric, you'll have to
  1184.     copy or symlink them.</para>
  1185.  
  1186.     <para>If you want the Python console, you'll also have to
  1187.     re-enable it, following the directions above. If you had made a
  1188.     local copy of the old one, make sure you
  1189.     <emphasis>don't</emphasis> copy or link that to the new
  1190.     directory. It won't work.</para>
  1191.  
  1192.     <para>Find the new version with <literal>gnumeric --version</literal>,
  1193.       making sure to invoke the proper gnumeric.</para>
  1194.  
  1195.   </sect2>
  1196.  
  1197.   <sect2 id="sect-extending-python-fancy">
  1198.     <title>Fancy tricks</title>
  1199.  
  1200.     <para>To be written....</para>
  1201.  
  1202.     <itemizedlist>
  1203.      <listitem><para>Swapping ranges (not a normal cell function, but I wrote
  1204.       one) that did this. But now I can rewrite it using the GUI,
  1205.         which will make a lot more sense.</para></listitem>
  1206.  
  1207.       <listitem><para>JK's python-only transpose function</para></listitem>
  1208.  
  1209.       <listitem><para>A Gnumeric interface to the Snob clustering
  1210.         algorithm. Coming soon to a spreadsheet near you!</para></listitem>
  1211.  
  1212.     </itemizedlist>
  1213.   </sect2>
  1214.  
  1215.   <sect2 id="sect-extending-python-questions">
  1216.     <title>Features wanted, and Questions</title>
  1217.  
  1218.     <itemizedlist>
  1219.       <listitem><para>Is it really impossible to determine the current
  1220.       workbook/sheet from Python? That's a bummer.  [JK writes: "Not
  1221.       yet fixed, but now fixable."]</para></listitem>
  1222.  
  1223.       <listitem><para>Several previous items are no longer on this list, due to
  1224.       the diligence of the Gnumeric hackers.</para></listitem>
  1225.     </itemizedlist>
  1226.   </sect2>
  1227.  
  1228. </sect1>
  1229.