RkBlog

Hardware, programming and astronomy tutorials and reviews.

Rozszerzenia gedit

Opis rozszerzeń-wtyczek gedit napisanych w PyGTK/Python.

Domyślny edytor tekstowy GNOME - Gedit posiada możliwość rozszerzania jego funkcjonalności za pomocą wtyczek napisanych w Pythonie (PyGTK), czy też opcjonalnie w C.

Każda wtyczka potrzebuje minimum dwa pliki - nazwa_wtyczki.gedit-plugin, oraz plik Pythona z kodem. Plik "gedit-plugin" zawiera opis wtyczki potrzebny do jej uruchamiania i konfiguracji. Wtyczki umieszczane są w katalogach /usr/lib/gedit-2/plugins/ lub ~/.gnome2/gedit/plugins/ - dla danego użytkownika.

Wtyczka gedit to klasa Pythona dziedzicząca klasę gedit.Plugin, która zawiera trzy metody, jakie wtyczka może nadpisać:

Metoda activate zostanie wykonana gdy stworzone zostanie nowe okno gedit, a deactivate, gdy zostanie zniszczone. Metoda update_ui wykonywana jest, gdy wtyczka zostanie poproszona o sprawdzanie, czy potrzebuje zaktualizowania swojego interfejsu.

Prosta wtyczka

Zaczynamy od stworzenia pliku "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
Teraz wystarczy stworzyć plik examplepy.pu o minimalnym kodzie:
import gedit

class ExamplePyPlugin(gedit.Plugin):
    def activate(self, window):
        pass

    def deactivate(self, window):
        pass

    def update_ui(self, window):
        pass
Po umieszczeniu obu plików w katalogu wtyczek i uruchomieniu Gedit powinniśmy zobaczyć naszą wtyczkę na liście wtyczek (warto odpalić gedit z konsoli, co ułatwi debugowanie wtyczki - będziemy widzieć np. wyjątki). Powyższy kod wtyczki nie robi nic, więc pora go nieco rozbudować. Zaleca się stosowanie oddzielnej klasy, którą wykorzystuje się następnie we właściwej klasie wtyczki:
import gedit

class ExamplePyWindowHelper:
    def __init__(self, plugin, window):
        print "Plugin created for", window
        self._window = window
        self._plugin = plugin

    def deactivate(self):
        print "Plugin stopped for", self._window
        self._window = None
        self._plugin = None

    def update_ui(self):
        # Called whenever the window has been updated (active tab
        # changed, etc.)
        print "Plugin update for", self._window

class ExamplePyPlugin(gedit.Plugin):
    def __init__(self):
        gedit.Plugin.__init__(self)
        self._instances = {}

    def activate(self, window):
        self._instances[window] = ExamplePyWindowHelper(self, window)

    def deactivate(self, window):
        self._instances[window].deactivate()
        del self._instances[window]

    def update_ui(self, window):
        self._instances[window].update_ui()

Dla każdego okna gedit tworzymy nową instancję klasy "ExamplePyWindowHelper". Przy wywoływaniu "deactivate", czy "update_ui" instancje jest odszukana i wykonywana jest na niej odpowiednia metoda. Poza wypisywaniem paru tekstów wtyczka nadal robi niewiele.

Rozbudujmy naszą wtyczkę dodając nowe menu, które po użyciu wyczyści dokument:

from gettext import gettext as _

import gtk
import gedit

# Menu item example, insert a new item in the Tools menu
ui_str = """<ui>
  <menubar name="MenuBar">
    <menu name="ToolsMenu" action="Tools">
      <placeholder name="ToolsOps_2">
        <menuitem name="ExamplePy" action="ExamplePy"/>
      </placeholder>
    </menu>
  </menubar>
</ui>
"""
class ExamplePyWindowHelper:
    def __init__(self, plugin, window):
        self._window = window
        self._plugin = plugin

        # Insert menu items
        self._insert_menu()

    def deactivate(self):
        # Remove any installed menu items
        self._remove_menu()

        self._window = None
        self._plugin = None
        self._action_group = None

    def _insert_menu(self):
        # Get the GtkUIManager
        manager = self._window.get_ui_manager()

        # Create a new action group
        self._action_group = gtk.ActionGroup("ExamplePyPluginActions")
        self._action_group.add_actions([("ExamplePy", None, _("Clear document"),
                                         None, _("Clear the document"),
                                         self.on_clear_document_activate)])

        # Insert the action group
        manager.insert_action_group(self._action_group, -1)

        # Merge the UI
        self._ui_id = manager.add_ui_from_string(ui_str)

    def _remove_menu(self):
        # Get the GtkUIManager
        manager = self._window.get_ui_manager()

        # Remove the ui
        manager.remove_ui(self._ui_id)

        # Remove the action group
        manager.remove_action_group(self._action_group)

        # Make sure the manager updates
        manager.ensure_update()

    def update_ui(self):
        self._action_group.set_sensitive(self._window.get_active_document() != None)

    # Menu activate handlers
    def on_clear_document_activate(self, action):
        doc = self._window.get_active_document()
        if not doc:
            return

        doc.set_text('')

Gdy tworzymy instancję wtyczki tworzona jest także nowa pozycja menu. Gedit używa GtkUIManager do obsługi nawigacji. Do menu "narzędzi" ("tools") dodaliśmy opcję wyczyszczenia dokumentu - "Clear document" z przypisaną metodą "on_clear_document_activate" do wykonania. Więcej szczegółów znajdziemy na live.gnome.org. Znajdziemy tam również listę wtyczek.
RkBlog

PyGTK, 8 December 2008,

Comment article