Difference between revisions of "Gramplets"

From Gramps
Jump to: navigation, search
m (Hello World)
(Gramplet Interface: No longer exisit "or go to Help -> Plugin Status -> Reload")
(46 intermediate revisions by 7 users not shown)
Line 1: Line 1:
This page describes the technical details of Gramplets. If you are interested in using Gramplets, please see [[Gramps 3.2 Wiki Manual - Gramplets]].
+
{{man warn|Warning:|Technical details about Gramplets for Developers. If you are interested in using Gramplets, please see [[Gramps 3.4 Wiki Manual - Gramplets]].}}
 +
 
 +
A '''Gramplet''' is a type of GRAMPS plugin. Gramplets are mini-view that is designed to be composed with other Gramplets or Views to create a way to see your Family Tree that is just right for you. In fact, Gramplets can be made
 +
to do just about anything that you want.
 +
 
 +
[[Image:Gramplets.png|400px|right|Gramplet]]
  
[[Category:Developers/General]][[Category:Developers/Tutorials]][[Category: Plugins]]
 
  
 
= Gramplet Interface =
 
= Gramplet Interface =
  
[[Image:Gramplets.png|thumb|right|Gramplet]]
 
  
A Gramplet is a type of GRAMPS plugin. A Gramplet is a mini-view that is designed to be composed with other Gramplets or Views to create a way to see your Family Tree that is just right for you. In fact, Gramplets can be made
+
 
to do just about anything that you want. There are 6 main kinds of plugins:
+
There are 6 main kinds of plugins:
  
 
# '''Reports''': output for printing or display
 
# '''Reports''': output for printing or display
Line 17: Line 20:
 
# '''Gramplets''': interactive views for moving, analysing, displaying, etc.
 
# '''Gramplets''': interactive views for moving, analysing, displaying, etc.
  
There are two plugin directories: a global/system one, and a
+
There are two plugin directories: a global/system one, and a private/personal one.  You can easily create a plugin by simply putting a file in your personal plugin directory (usually in ''.gramps/grampsxx/plugins/gramplet/'' ).
private/personal one.  You can easily create a plugin by simply
 
putting a file in your personal plugin directory (usually in
 
.gramps/plugins).
 
  
 
== Hello World ==
 
== Hello World ==
  
In teaching programming, a common "first program" is to write a program that
+
In teaching programming, a common "first program" is to write a program that says "Hello World".
says "Hello World".  
 
Let's jump right in and take a look at such a gramplet named ''HelloWorld.py'':
 
  
 +
Let us jump right in and take a look at such a gramplet named ''HelloWorld.py'':
 +
 +
Create a python file named ''HelloWorld.py'' and add the following content:
 
<pre>
 
<pre>
 +
# File: HelloWorld.py
 
def init(gui):
 
def init(gui):
 
   gui.set_text("Hello world!")
 
   gui.set_text("Hello world!")
 
</pre>
 
</pre>
  
Then an other python file named ''HelloWorld.gpr.py''  
+
And create another python file named ''HelloWorld.gpr.py'' with the following content:
  
 
<pre>
 
<pre>
 +
# File: HelloWorld.gpr.py
 
register(GRAMPLET,
 
register(GRAMPLET,
 
         id="Hello World Gramplet",  
 
         id="Hello World Gramplet",  
Line 44: Line 47:
 
         fname="HelloWorld.py",
 
         fname="HelloWorld.py",
 
         height = 20,  
 
         height = 20,  
         gramplet = 'HelloWorldGramplet',
+
         gramplet = 'Sample Gramplet',
         gramps_target_version="3.2",
+
        gramplet_title=_("Sample Gramplet"),
         gramplet_title=_("Sample Gramplet")
+
         gramps_target_version="3.3",
 +
         help_url="Sample Gramplet"
 
         )
 
         )
 
</pre>
 
</pre>
  
If you copy this files into a file in your plugins directory, and then
+
You can read more about the gpr.py file in [[Addons development]] and the [http://gramps.svn.sourceforge.net/viewvc/gramps/trunk/src/gen/plug/_pluginreg.py?view=markup source code implementation].
either restart GRAMPS or go to Help -> Plugin Status -> Reload, the
+
 
you'll be able to create this Gramplet. On the Gramplet View,
+
If you place these files your plugins directory (usually in ''.gramps/grampsxx/plugins/gramplet/'' ), and then restart GRAMPS, you will be able to create this Gramplet. On the Gramplet View, right-click in an open area and select the "Hello World Gramplet". You should then see:
right-click in an open area and select the "Hello World Gramplet". You
 
should then see:
 
  
 
[[Image:HelloWorldGramplet.png]]
 
[[Image:HelloWorldGramplet.png]]
Line 77: Line 79:
  
 
<pre>
 
<pre>
 +
# File: HelloWorld2.py
 +
from gen.plug import Gramplet
 +
 
class HelloWorldGramplet(Gramplet):
 
class HelloWorldGramplet(Gramplet):
 
     def init(self):
 
     def init(self):
Line 83: Line 88:
  
 
<pre>
 
<pre>
register(
+
# File: HelloWorld2.gpr.py
 +
register(GRAMPLET,
 
         id="Hello World2 Gramplet",  
 
         id="Hello World2 Gramplet",  
 
         name=_("Hello World2 Gramplet"),
 
         name=_("Hello World2 Gramplet"),
 
         description = _("a program that says 'Hello World'"),
 
         description = _("a program that says 'Hello World'"),
 
         version="0.0.1",
 
         version="0.0.1",
         gramps_target_version="3.2",
+
         gramps_target_version="3.3",
 
         status = STABLE,
 
         status = STABLE,
 
         fname="HelloWorld2.py",
 
         fname="HelloWorld2.py",
 
         height = 20,  
 
         height = 20,  
         gramplet = 'HelloWorld2Gramplet',
+
         gramplet = 'HelloWorldGramplet',
         gramplet_title=_("Sample Gramplet")  
+
         gramplet_title=_("Sample Gramplet"),
 +
        help_url="Sample Gramplet"
 
         )
 
         )
 
</pre>
 
</pre>
Line 102: Line 109:
 
== Register Options ==
 
== Register Options ==
  
* '''id''': the case-insensitive keyword "gramplet"
+
* '''GRAMPLET''': the first argument is the keyword GRAMPLET
* '''name''': the gramplet's name (can be translated), unique among gramplets
+
* '''id''': the identifying name of the gramplet, unique among all plugins
* '''height''': the minimum or maximum height of the gramplet in normal mode
+
* '''name''': the translated gramplet's name
* '''fname''': the name of your file
+
* '''height''': the minimum (or maximum) height of the gramplet in normal mode
* '''title''': the default gramplet title; user changeable
+
* '''fname''': the name of your gramplet file
 +
* '''gramplet''': the name of the function or class in fname that creates the gramplet
 +
* '''gramplet_title''': the default gramplet title; user changeable in ''Configure View''
 
* '''status''': STABLE or UNSTABLE
 
* '''status''': STABLE or UNSTABLE
 
* '''version''': a string with 2 dots (such as "1.23.14") representing the version number
 
* '''version''': a string with 2 dots (such as "1.23.14") representing the version number
 
* '''gramps_target_version''': a string with 2 dots representing the version of GRAMPS that this gramplet was written for
 
* '''gramps_target_version''': a string with 2 dots representing the version of GRAMPS that this gramplet was written for
 +
* '''help_url''': the title of the wiki page that describes the gramplet
  
At the bare minimum, you need to have the above 8 options when registering your Gramplets.
+
At the bare minimum, you need to have the above 10 options when registering your Gramplets.
  
 
In addition, you can use the following as well:
 
In addition, you can use the following as well:
Line 118: Line 128:
 
* '''detached_height''': the size in pixels of the minimum and default detached height
 
* '''detached_height''': the size in pixels of the minimum and default detached height
 
* '''expand''': whether or not the Gramplet should expand to fill the column, if it can
 
* '''expand''': whether or not the Gramplet should expand to fill the column, if it can
 +
* '''description''': a description of the gramplet
  
 
== Core Methods ==
 
== Core Methods ==
 +
 +
The class-based gramplet utilizes the following methods:
  
 
* '''init()''': run once, on construction
 
* '''init()''': run once, on construction
Line 227: Line 240:
  
 
<pre>
 
<pre>
 +
# File: Widget.py
 
from gettext import gettext as _
 
from gettext import gettext as _
from DataViews import Gramplet, register
+
from gen.plug import Gramplet
 
import gtk
 
import gtk
  
 
class WidgetGramplet(Gramplet):
 
class WidgetGramplet(Gramplet):
 
     def init(self):
 
     def init(self):
         self.gui.WIDGET = gtk.WIDGET()
+
         self.gui.WIDGET = ### Some Widget Constructor ###
 
         self.gui.get_container_widget().remove(self.gui.textview)
 
         self.gui.get_container_widget().remove(self.gui.textview)
 
         self.gui.get_container_widget().add_with_viewport(self.gui.WIDGET)
 
         self.gui.get_container_widget().add_with_viewport(self.gui.WIDGET)
 
         self.gui.WIDGET.show()
 
         self.gui.WIDGET.show()
 +
</pre>
  
register(type="gramplet",  
+
<pre>
         name= "Widget Gramplet",  
+
# File: Widget.gpr.py
         tname=_("Widget Gramplet"),  
+
register(GRAMPLET,  
 +
         id= "Widget Gramplet",  
 +
         name=_("Widget Gramplet"),  
 
         height=100,
 
         height=100,
 
         expand=False,
 
         expand=False,
         content = WidgetGramplet,
+
         fname="Widget.py",
         title=_("Widget"),
+
        gramplet = "WidgetGramplet",
 +
        gramps_target_version = "3.4",
 +
         gramplet_title=_("Widget"),
 
         )
 
         )
 
</pre>
 
</pre>
  
In fact, with Python, gtk, and cairo, you can make your own widgets that do pretty much anything and look very nice. Here is an example adding a Cairo Clock (which really keeps time) to GRAMPS: [https://gramps-addons.svn.sourceforge.net/svnroot/gramps-addons/branches/gramps32/download/ClockGramplet.addon.tgz ClockGramplet.addon.tgz]
+
In fact, with Python, gtk, and cairo, you can make your own widgets that do pretty much anything and look very nice. Here is an example adding a Cairo Clock (which really keeps time) to GRAMPS 3.4 : [https://gramps-addons.svn.sourceforge.net/svnroot/gramps-addons/branches/gramps34/download/ClockGramplet.addon.tgz ClockGramplet.addon.tgz]
  
 
Here it is on the Gramplet view:
 
Here it is on the Gramplet view:
Line 307: Line 326:
 
To learn more about writing a Gramplet, it is suggested to look at the existing Gramplets. You can see a complete list of the Gramplet source code here:
 
To learn more about writing a Gramplet, it is suggested to look at the existing Gramplets. You can see a complete list of the Gramplet source code here:
  
* [http://gramps.svn.sourceforge.net/viewvc/gramps/branches/maintenance/gramps32/src/plugins/gramplet/ GRAMPS 3.2 Gramplets]
+
* [http://gramps.svn.sourceforge.net/viewvc/gramps/branches/maintenance/gramps34/src/plugins/gramplet/ GRAMPS 3.4 Gramplets]
 
* [http://gramps.svn.sourceforge.net/viewvc/gramps/trunk/src/plugins/gramplet/ GRAMPS Trunk Gramplets]
 
* [http://gramps.svn.sourceforge.net/viewvc/gramps/trunk/src/plugins/gramplet/ GRAMPS Trunk Gramplets]
  
 
Click on a filename, and then "(view)" to see the source code of that Gramplet.
 
Click on a filename, and then "(view)" to see the source code of that Gramplet.
 +
 +
[[Category:Developers/General]]
 +
[[Category:Developers/Tutorials]]
 +
[[Category: Plugins]]
 +
[[Category:Gramplets]]

Revision as of 09:25, 30 August 2012

Gnome-important.png
Warning:

Technical details about Gramplets for Developers. If you are interested in using Gramplets, please see Gramps 3.4 Wiki Manual - Gramplets.

A Gramplet is a type of GRAMPS plugin. Gramplets are mini-view that is designed to be composed with other Gramplets or Views to create a way to see your Family Tree that is just right for you. In fact, Gramplets can be made to do just about anything that you want.

Gramplet


Gramplet Interface

There are 6 main kinds of plugins:

  1. Reports: output for printing or display
  2. Tools: a method for processing data
  3. Quick View: a list of details based on the current object
  4. Importer: reads a file into your current tree
  5. Exporter: writes a file from your current tree
  6. Gramplets: interactive views for moving, analysing, displaying, etc.

There are two plugin directories: a global/system one, and a private/personal one. You can easily create a plugin by simply putting a file in your personal plugin directory (usually in .gramps/grampsxx/plugins/gramplet/ ).

Hello World

In teaching programming, a common "first program" is to write a program that says "Hello World".

Let us jump right in and take a look at such a gramplet named HelloWorld.py:

Create a python file named HelloWorld.py and add the following content:

# File: HelloWorld.py
def init(gui):
   gui.set_text("Hello world!")

And create another python file named HelloWorld.gpr.py with the following content:

# File: HelloWorld.gpr.py
register(GRAMPLET,
         id="Hello World Gramplet", 
         name=_("Hello World Gramplet"),
         description = _("a program that says 'Hello World'"),
         status = STABLE,
         version="0.0.1",
         fname="HelloWorld.py",
         height = 20, 
         gramplet = 'Sample Gramplet',
         gramplet_title=_("Sample Gramplet"),
         gramps_target_version="3.3",
         help_url="Sample Gramplet"
         )

You can read more about the gpr.py file in Addons development and the source code implementation.

If you place these files your plugins directory (usually in .gramps/grampsxx/plugins/gramplet/ ), and then restart GRAMPS, you will be able to create this Gramplet. On the Gramplet View, right-click in an open area and select the "Hello World Gramplet". You should then see:

File:HelloWorldGramplet.png

Explanation

The main work of a Gramplet is performed in a function, or a class. In this very simple example, a function init is defined that takes a single argument, gui. The function simply sets the gui's text area to be "Hello World!", and that's it. It does this just once, and never changes.

Before a plugin can be used, it needs to be "registered". You call the register function with a number of named-arguments. There are a number of named-arguments that you can provide, including: name, height, content, title, expand, state, and data. We will explore those in detail, below.

Hello World, with Class

Here is the same functionality again, but this time as a class:

# File: HelloWorld2.py
from gen.plug import Gramplet

class HelloWorldGramplet(Gramplet):
    def init(self):
        self.set_text("Hello world!")
# File: HelloWorld2.gpr.py
register(GRAMPLET,
         id="Hello World2 Gramplet", 
         name=_("Hello World2 Gramplet"),
         description = _("a program that says 'Hello World'"),
         version="0.0.1",
         gramps_target_version="3.3",
         status = STABLE,
         fname="HelloWorld2.py",
         height = 20, 
         gramplet = 'HelloWorldGramplet',
         gramplet_title=_("Sample Gramplet"),
         help_url="Sample Gramplet"
         )

This is the recommended method of creating a Gramplet. The following details describe the properties and methods of this class.

Register Options

  • GRAMPLET: the first argument is the keyword GRAMPLET
  • id: the identifying name of the gramplet, unique among all plugins
  • name: the translated gramplet's name
  • height: the minimum (or maximum) height of the gramplet in normal mode
  • fname: the name of your gramplet file
  • gramplet: the name of the function or class in fname that creates the gramplet
  • gramplet_title: the default gramplet title; user changeable in Configure View
  • status: STABLE or UNSTABLE
  • version: a string with 2 dots (such as "1.23.14") representing the version number
  • gramps_target_version: a string with 2 dots representing the version of GRAMPS that this gramplet was written for
  • help_url: the title of the wiki page that describes the gramplet

At the bare minimum, you need to have the above 10 options when registering your Gramplets.

In addition, you can use the following as well:

  • detached_width: the size in pixels of the minimum and default detached height
  • detached_height: the size in pixels of the minimum and default detached height
  • expand: whether or not the Gramplet should expand to fill the column, if it can
  • description: a description of the gramplet

Core Methods

The class-based gramplet utilizes the following methods:

  • init(): run once, on construction
  • main(): run once per update
  • update(): don't change this, it calls main
  • active_changed(): run when active-changed is triggered
  • db_changed(): run when db-changed is triggered
  • set_tooltip(TEXT) - tooltip for gramplet

Don't call main directly; use the update method.

In the db_changed method, you should connect all of the signals that will trigger an update. That typically looks like:

    def db_changed(self):
        self.dbstate.db.connect('person-add', self.update)
        self.dbstate.db.connect('person-delete', self.update)
        self.dbstate.db.connect('person-update', self.update)
        self.dbstate.db.connect('family-add', self.update)
        self.dbstate.db.connect('family-delete', self.update)
        self.dbstate.db.connect('family-update', self.update)

The method main() can be written as a normal Python method, or it can be written to run nicely in parallel with other GRAMPS code. To make it run nicely in parallel, you should issue a yield True every once in a while. For example:

    def main(self):
        for i in range(5000):
            if i % 500 == 0:
                yield True
        yield False

The True means that there is more to do; False means that there is nothing left to do.

Textual Output Methods

The most common kinds of Gramplets are text-based. There are a number of methods to assist with handling this text.

  • set_text(TEXT) - clear and set text to TEXT
  • append_text(TEXT, scroll_to=POSITION)
    • POSITION is 'begin' (top), 'end' (bottom) or 'start' (start of append)
  • clear_text() - clears all text
  • set_use_markup(BOOLEAN-VALUE)
  • render_text(TEXT) - for use with A, B, I, U, and TT tags
    • A for creating links; use tag HREF="url" for URLs, and WIKI="name" for pages on the wiki
    • B for bold
    • I for italics
    • U for underlined
    • TT for a fixed-width, typewriter font
  • link(TEXT, LINK-TYPE, DATA) -
    • TEXT can be any text
    • LINK-TYPE is:
      • 'Person' - and DATA is a person handle
      • 'PersonList' - and DATA is a list of handles
      • 'Family' - and DATA is a family handle
      • 'Surname' - and DATA is a person
      • 'Given' -
      • 'Filter' - and DATA is either:
        • 'all people' -
        • 'males' - all males
        • 'females' - all females
        • 'people with unknown gender' - people marked as unknown
        • 'people with incomplete names' - people who have a missing surname or given name
        • 'people with missing birth dates' - people who have missing birth dates
        • 'disconnected people' - people with no parents and no children
        • 'all families' - all families
        • 'unique surnames' - list of all unique surnames
        • 'people with media' - people who have media
        • 'media references' - all of the media
        • 'unique media' -
        • 'missing media' - media for which the file does not exist
        • 'media by size' -
        • 'list of people'-
      • 'URL' - and DATA is a URL
      • 'WIKI' - and DATA is a wiki page
      • 'Attribute' - and DATA is an attribute (eg, 'Nickname')
  • no_wrap() - turn word wrap off DEPRECATED
  • set_wrap(BOOL) - change word wrap

Using Tags

Tags are the manner in which you format the text.

   tag = self.gui.buffer.create_tag("fixed")
   tag.set_property("font", "Courier 8")
   ...
   start, end = self.gui.buffer.get_bounds()
   self.gui.buffer.apply_tag_by_name("fixed", start, end)
   self.append_text("", scroll_to="begin")

GUI Interface

Occasionally, you might have to dive down to the graphical objects that compose a Gramplet.

  • gui
    • gui.buffer
    • gui.textview
    • gui.get_container_widget()

If you wanted to put an arbitrary gtk object into the main area of a Gramplet, then you need to replace the standard textview object with your own. Here is the basic structure:

# File: Widget.py
from gettext import gettext as _
from gen.plug import Gramplet
import gtk

class WidgetGramplet(Gramplet):
    def init(self):
        self.gui.WIDGET = ### Some Widget Constructor ###
        self.gui.get_container_widget().remove(self.gui.textview)
        self.gui.get_container_widget().add_with_viewport(self.gui.WIDGET)
        self.gui.WIDGET.show()
# File: Widget.gpr.py
register(GRAMPLET, 
         id= "Widget Gramplet", 
         name=_("Widget Gramplet"), 
         height=100,
         expand=False,
         fname="Widget.py",
         gramplet = "WidgetGramplet",
         gramps_target_version = "3.4",
         gramplet_title=_("Widget"),
         )

In fact, with Python, gtk, and cairo, you can make your own widgets that do pretty much anything and look very nice. Here is an example adding a Cairo Clock (which really keeps time) to GRAMPS 3.4 : ClockGramplet.addon.tgz

Here it is on the Gramplet view:

Clock on Gramplet View

and detached:

Clock Gramplet detached

GUI Options

  • add_option(OPTION)
    • OPTION is one of the menu options:
      • NumberOption
      • EnumeratedListOption
      • StringOption
      • ColorOption
      • TextOption
      • BooleanOption
      • FilterOption
      • PersonOption
      • FamilyOption
      • NoteOption
      • MediaOption
      • see src/gen/plug/menu/ for others
  • build_options()
  • save_options()
  • get_option_widget(TEXT)
  • get_option(TEXT)

Predifined Properties

There are a number of preset properties:

  • dbstate
    • dbstate.db
      • dbstate.db.connect(SIGNAL, METHOD)
      • dbstate.db.get_person_from_handle(HANDLE)
      • dbstate.get_active_person()
  • uistate

Persistance

  • gui
    • gui.data
  • on_load()
  • on_save()
  • save_text_to_data()
  • load_data_to_text()

Advanced Settings

  • force_update: set True to have the gramplet update, even when minimized

Learning More

To learn more about writing a Gramplet, it is suggested to look at the existing Gramplets. You can see a complete list of the Gramplet source code here:

Click on a filename, and then "(view)" to see the source code of that Gramplet.