Python Plugin How To for gedit 2

This document describes how to create plugins for gedit 2 using Python, you can find the tutorial for the new Python plugins of gedit 3 here.

This document describes how to create plugins for Gedit using Python. Below follows a list of the different topics you need to know about. Note also that knowledge of GTK (and more importantly, PyGTK) is useful in creating an actual plugin. An extensive tutorial can be found here.

Files needed

Every Python plugin needs at least two files. One file (pluginname.gedit-plugin) is to tell Gedit where the plugin can be found, what it's called, a short description, who's the author, etc. This file is in the .desktop format (see example). The second file is the actual Python code. Both of these files need to be placed in either the system-wide plugins directory /usr/lib/gedit-2/plugins/, or in the user plugins directory, which may need to be created, ~/.gnome2/gedit/plugins/.

Gedit plugin infrastructure

Most plugin writers find the way Gedit plugins work counterintuitive at first glance. It's important to read this section carefully. A Python plugin is derived from the gedit.Plugin base class which defines the following functions (which you can override):

  • activate(self, gedit.Window)
  • deactivate(self, gedit.Window)
  • update_ui(self, gedit.Window)

Now, Gedit is an application which usually only runs one instance, even if you launch it multiple times. When Gedit starts it looks for an existing Gedit instance, hands over control to that instance and exits. Plugins are therefore only instantiated once (during load) of the master Gedit application. However, plugins usually apply to windows. This is what the activate and deactivate are for. Whenever a new window is created the activate function on your plugin will be called with one argument (the window). On the other hand, when a window is destroyed the deactivate function is called.

The approach I find easy to implement (also implemented in the example) is to create a new class. Whenever a new Gedit window is created and activate is run, I instantiate a new object of this class and attach it to the window. This object will now handle that window and do whatever the plugin needs to do. This way every window has a separate object controlling it.

The update_ui function is called when the plugin is requested to see if it needs to update its UI.

Writing the plugin

Now we are ready to actually write a plugin. As stated in Files needed we first need to create a .gedit-plugin file which will give Gedit information about our new plugin.

examplepy.gedit-plugin

[Gedit Plugin]
Loader=python
Module=examplepy
IAge=2
Name=Example py
Description=A Python plugin example
Authors=Jesse van den Kieboom <jesse@icecrew.nl>
Copyright=Copyright © 2006 Jesse van den Kieboom <jesse@icecrew.nl>
Website=http://www.gedit.org

To explain the specified fields:

  • Loader: which loader Gedit needs to use; for Python plugins this is python

  • Module: the file in which your plugin is located (leaving out the extension .py). You can also specify a directory here. As with normal Python applications you need to put a __init__.py in that directory)

  • IAge: the plugin version, this is always 2

  • Name: the name of the plugin. This will show up in the plugins list in the preference dialog
  • Description: a short description of the plugin. This will show up in the plugins list in the preference dialog
  • Authors: a list of people who wrote the plugin. This will show up in the about dialog of the plugin (plugins list in the preference dialog)
  • Copyright: the plugins copyright. This will show up in the about dialog of the plugin (plugins list in the preference dialog)
  • Website: optionally, the plugins website

Now we need to create the plugin itself. The plugin is called examplepy, so we create examplepy.py. At the most minimal level, it just defines one class derived from gedit.Plugin defining activate, deactivate and update_ui:

Minimal plugin

   1 import gedit
   2 
   3 class ExamplePyPlugin(gedit.Plugin):
   4     def activate(self, window):
   5         pass
   6 
   7     def deactivate(self, window):
   8         pass
   9 
  10     def update_ui(self, window):
  11         pass

Our first plugin! When we copy these two files to ~/.gnome2/gedit/plugins and restart Gedit the plugin will be detected. When you open the plugins list in the preferences dialog you should see something like this:

Implementing the window controller

Of course, this plugin doesn't do anything. We can activate it, but it's not of much use, yet. Before implementing useful stuff, let's first get the structure right. We're now going to implement the idea I introduced above, creating a separate class (ExamplePyWindowHelper) which will control a window.

   1 import gedit
   2 
   3 class ExamplePyWindowHelper:
   4     def __init__(self, plugin, window):
   5         print "Plugin created for", window
   6         self._window = window
   7         self._plugin = plugin
   8 
   9     def deactivate(self):
  10         print "Plugin stopped for", self._window
  11         self._window = None
  12         self._plugin = None
  13 
  14     def update_ui(self):
  15         # Called whenever the window has been updated (active tab
  16         # changed, etc.)
  17         print "Plugin update for", self._window
  18 
  19 class ExamplePyPlugin(gedit.Plugin):
  20     def __init__(self):
  21         gedit.Plugin.__init__(self)
  22         self._instances = {}
  23 
  24     def activate(self, window):
  25         self._instances[window] = ExamplePyWindowHelper(self, window)
  26 
  27     def deactivate(self, window):
  28         self._instances[window].deactivate()
  29         del self._instances[window]
  30 
  31     def update_ui(self, window):
  32         self._instances[window].update_ui()

What you see here is that when activate is called we instantiate a new ExamplePyWindowHelper. The instance is stored per window object. When deactivate or update_ui is called the plugin instance is retrieved and either stopped (deactivate) or updated (update_ui).

The plugin still doesn't do anything except printing some messages when it's created, stopped and updated. But we do have a good starting point now to really implement something.

Adding a menu item

So what we are going to do now is add a menu item in the menu bar. What we'll implement is that when the menu item is activated the currently active buffer is cleared.

   1 from gettext import gettext as _
   2 
   3 import gtk
   4 import gedit
   5 
   6 # Menu item example, insert a new item in the Tools menu
   7 ui_str = """<ui>
   8   <menubar name="MenuBar">
   9     <menu name="ToolsMenu" action="Tools">
  10       <placeholder name="ToolsOps_2">
  11         <menuitem name="ExamplePy" action="ExamplePy"/>
  12       </placeholder>
  13     </menu>
  14   </menubar>
  15 </ui>
  16 """
  17 class ExamplePyWindowHelper:
  18     def __init__(self, plugin, window):
  19         self._window = window
  20         self._plugin = plugin
  21 
  22         # Insert menu items
  23         self._insert_menu()
  24 
  25     def deactivate(self):
  26         # Remove any installed menu items
  27         self._remove_menu()
  28 
  29         self._window = None
  30         self._plugin = None
  31         self._action_group = None
  32 
  33     def _insert_menu(self):
  34         # Get the GtkUIManager
  35         manager = self._window.get_ui_manager()
  36 
  37         # Create a new action group
  38         self._action_group = gtk.ActionGroup("ExamplePyPluginActions")
  39         self._action_group.add_actions([("ExamplePy", None, _("Clear document"),
  40                                          None, _("Clear the document"),
  41                                          self.on_clear_document_activate)])
  42 
  43         # Insert the action group
  44         manager.insert_action_group(self._action_group, -1)
  45 
  46         # Merge the UI
  47         self._ui_id = manager.add_ui_from_string(ui_str)
  48 
  49     def _remove_menu(self):
  50         # Get the GtkUIManager
  51         manager = self._window.get_ui_manager()
  52 
  53         # Remove the ui
  54         manager.remove_ui(self._ui_id)
  55 
  56         # Remove the action group
  57         manager.remove_action_group(self._action_group)
  58 
  59         # Make sure the manager updates
  60         manager.ensure_update()
  61 
  62     def update_ui(self):
  63         self._action_group.set_sensitive(self._window.get_active_document() != None)
  64 
  65     # Menu activate handlers
  66     def on_clear_document_activate(self, action):
  67         doc = self._window.get_active_document()
  68         if not doc:
  69             return
  70 
  71         doc.set_text('')

We left out the ExamplePyPlugin because nothing has changed there. So what happens here. When the plugin instance is created it inserts a menu item. Gedit uses GtkUIManager and defines placeholders for plugins to insert menu items in. This makes it easy for us to insert a menu item. We add a Clear document item to the tools menu and register the on_clear_document_activate callback to be called when the item is activated. In the callback we simply retrieve the currently active document and clear the text.

Additionally we change the sensitivity of the action group in update_ui. The action group will be insensitive when there is no currently active document (because we can't clear a document that doesn't exist). That's everything there is to it. We now have our first functional Python plugin!

Making use of side/bottom panels

In the context of the above examples, the code

self._window.get_side_panel()

would return a gedit.Panel object corresponding to the side panel, and

self._window.get_bottom_panel()

would return the bottom panel. Methods of the gedit.Panel object can be found in the API section.

Events

This is a very, very incomplete description of signals. A proper discussion of signals can be found in the pyGTK tutorial. You will need to consult this.

Your plugin can react to certain events (such as the opening of a tab or the saving of a file). This is accomplished by listening to signals from various Gedit objects (gedit.Window and so on). A full specification of the signals emitted by each object can be found in the C API.

To listen for a certain signal, use the connect() function of the object. The syntax is as follows:

object.connect("signal", handler, other_arg1, ...)

The signal that you are listening for is the first argument, in quotes. The second argument is the name of a function that will handle the signal event. The arguments to this function will vary by signal; consult the reference for your particular signal to find out all the information. connect() function returns an integer that server as an identified. An example in actual use would be:

l_id = doc.connect("loaded", self.add, view).

where doc is a gedit.Document and the add function is defined in the plugin class. You will also want to tell doc that l_id is a handler, and so you'll need a function that looks something like

doc.set_data("ExamplePyPluginHandlerId", l_id)

When cleaning up, remember to kill all your signal handlers. Given the above example, cleanup code would look something like this:

l_id = doc.get_data("ExamplePyPluginHandlerId")
doc.disconnect(l_id)
doc.set_data("ExamplePyPluginHandlerId", None)

Adding a configure dialog for your plugin

If you want to be able to display a configuration dialog for your plugin, you have to add two additional methods to your plugin class.

   1 def is_configurable(self):
   2     return True
   3 
   4 def create_configure_dialog(self):
   5     dialog = gtk.Dialog("Example Plugin Configuration")
   6     # ...code to add widgets to your dialog and connect signal handlers
   7     return dialog

Gedit will handle calling the run() method on the dialog for you.

Writing plugins without wrappers

If you want to write a plugin using Python, but the appropriate wrappers for a specific library are unavailable, you can use the "dl" module to access the library's functions. For example, if you want to access the Enchant spell checking library you could use the following code:

   1 import dl
   2 class Enchant(object):
   3     def __init__(self, lang='en_US'):
   4         try:
   5             self.library = dl.open('libenchant.so')
   6 
   7             broker = self.library.call('enchant_broker_init')
   8             self.dict = self.library.call(
   9                'enchant_broker_request_dict', broker, lang)
  10 
  11         except dl.error:
  12             self.incorrect_spelling = lambda x: False
  13 
  14     def incorrect_spelling(self, word):
  15         return bool(self.library.call('enchant_dict_check',
  16                     self.dict, word, len(word)))

Note: The "dl" module has been deprecated in Python 2.5, you should use the "ctypes" module instead.

Drag and Drop

The MDI document area is a drag and drop target. You can use the following data types from your source:

Data Type

Purpose

text/uri-list

Opens a tab for each filename passed.

This section is incomplete

Template generator

A Python plugin template can be easily generated using the little script attached to this page: gedit-py-plugin.py (mind that this is just a very simple little script, there is a much better one in the tools/ directory in Gedit CVS)

Most important API

Below follows a list of the most important Gedit specific API functions you need to know about. Note that this list is incomplete. A full documentation of the Gedit C API is contained in the docs/ directory of the gedit-2.15.3 release, and can be found online here. This is useful because the C API and the Python API are very similar — generally, the C method foo_bar(foo_instance) is replaced by foo_instance.bar(). Wherever the API is incomplete, this can be used as a reference.

gedit

The gedit module defines two functions to get the default gedit.App and to get a gedit.Tab from a gedit.Document.

app_get_default()

gets the default gedit.App instance

tab_get_from_document(gedit.Document)

gets the gedit.Tab belonging to a gedit.Document

gedit.App

The gedit application provides functions to get all the gedit.Windows, create new windows, get all the documents, etc.

create_window([gdk.Screen])

creates a new gedit.Window on a gdk.Screen. If gdk.Screen is omitted then the default screen will be used

get_windows()

get a list of gedit.Window

get_active_window()

get the currently active window

get_documents()

get all the gedit.Document in all the windows

get_views()

get all the gedit.View in all the windows


gedit.Window

The gedit window based on gtk.Window.

Methods

  • Method

    Description

    create_tab(jump_to)

    create a new empty document. jump_to (boolean) specifies whether to select the new document when it's created

    create_tab_from_uri(uri, encoding, line_pos, create, jump_to)

    open a file. uri (string) specifies the file to open. encoding (gedit.Encoding) specifies the encoding to use (None for default), line_pos (int) specifies to which line to jump when the file is opened. create (boolean) specifies whether to create a new file if it doesn't exist yet. jump_to (boolean) specifies whether to select the new document when it's created

    close_tab(tab)

    close a tab (gedit.Tab)

    close_all_tabs()

    closes all the tabs in the window

    get_active_tab()

    gets the currently active tab (gedit.Tab)

    set_active_tab(tab)

    sets the currently active tab (gedit.Tab)

    get_active_view()

    gets the currently active view (gedit.View)

    get_active_document()

    gets the currently active document (gedit.Document)

    get_documents()

    gets a list of the documents (gedit.Document) in the window

    get_unsaved_documents()

    gets a list of unsaved documents (gedit.Document)

    get_views()

    get a list of the views (gedit.View) in the window

    get_group()

    gets the gtk.WindowGroup the window belongs to

    get_side_panel()

    gets the sidepanel (gedit.Panel)

    get_bottom_panel()

    gets the bottompanel (gedit.Panel)

    get_statusbar()

    gets the statusbar (gtk.Statusbar)

    get_ui_manager()

    gets the ui manager (gtk.UIManager)

    get_state()

    gets the state of the window (cumulative state of the different documents)

Signals

  • Signal Name

    Description

    Callback

    tab-added

    Tab added to window

    def callback (tab, ...)

    tab-removed

    Tab removed from window

    def callback (tab, ...)

    tabs-reordered

    Tabs reordered

    def callback (...)

    active-tab-changed

    Active tab has been changed

    def callback (tab, ...)

    active-tab-state-changed

    Active tab has changed state

    def callback (...)


gedit.Panel

add_item(item, "name", image)

Adds an item (a gtkWidget) to the panel. image is the icon (usually a gtk.Image).

remove_item(item)

Removes an item from the panel. item is the gtkWidget that was specified in add_item.

activate_item(item)

Switches to an existing panel item. item is the gtkWidget that was specified in add_item.

set_property("visible", state)

Shows or hides the entire panel. state can be True to display the panel, or False to hide it.

gedit.Tab

The tab is based on a gtk.VBox and is the controller of gedit.View and gedit.Document.

get_view()

get the view (gedit.View)

get_document()

get the document (gedit.Document)

get_state()

get the state

set_auto_save_enabled(enabled)

sets whether autosave is enabled

get_auto_save_enabled()

gets whether autosave is enabled

set_auto_save_interval(interval)

sets the interval on which to perform autosave

get_auto_save_interval()

gets the interval on which to perform autosave

gedit.View

The view is based on a gtksourceview and is the view for gedit.Document.

cut_clipboard()

cut selection to clipboard

copy_clipboard()

copy selection to clipboard

paste_clipboard()

paste clipboard at cursor position

delete_selection()

deletes the selected text

select_all()

select all text

scroll_to_cursor()

scroll to the cursor position

set_colors(def, background, text, selection, sel_text)

sets the different colors to use. def (boolean) specifies whether to use the default colors. background, text, selection and sel_text are gdk.Color objects which specify the corresponding colors

set_font(def, font_name)

sets the font to use. def (boolean) specifies whether to use the default font. font_name (string) specifies the font to use

gedit.Document

The document is based on a gtksourcebuffer and is the document for gedit.View.

get_uri()

gets the documents uri (or None if the document is unsaved)

get_uri_for_display()

gets the documents uri for display (or None if the document is unsaved)

get_short_name_for_display()

gets the documents short name for display

get_mime_type()

gets the documents mime type

get_readonly()

gets whether the document is read only

load(uri, encoding, line_pos, create)

loads a file. uri (string) specifies the file. encoding (gedit.Encoding) specifies the encoding (None for the default encoding). line_pos (int) specifies the line to scroll to after loading. create (boolean) specifies whether to create the file if it doesn't exist yet.

insert_file(iter, uri, encoding)

inserts the contents of a file. iter (gtk.TextIter) specifies where to insert the file. uri specifies the file to insert. encoding (gedit.Encoding) specifies the encoding (None for the default encoding)

load_cancel()

cancels the loading of a file

is_untouched()

gets whether the document has been changed since the last time it was saved

is_untitled()

gets whether the document has a title

get_deleted()

gets whether the document has been deleted

goto_line(line)

scroll to a line number. line (int) specifies the line number to scroll to

set_search_text(text, flags)

set text to search for

get_search_text()

gets the text to search for

get_can_search_again()

gets whether the end of the document has not yet been reached

search_forward

replace_all

search_backward

set_language(lang)

sets the source language of the document to lang (gtk.SourceLanguage)

get_language()

gets the source language of the document (gtk.SourceLanguage)

get_encoding()

gets the encoding of the document (gedit.Encoding)

gedit.Encoding

copy()

free()

get_charset()

gets the actual character set like 'UTF-8'

get_name()

gets the name of the encoding like 'Unicode'

to_string()

gets its string representation like 'Unicode (UTF-8)'

Apps/Gedit/PythonPluginHowToOld (last edited 2013-10-31 06:11:48 by JefferyTo)