Thursday, 29 December 2011

My first Gedit plugin

Hi :)
Today I am posting my first plugin for Gedit.
It is a word mixer that takes each line of the document and changes the order of the words. It is not very useful, but I learned a lot while developing it:
I've learned how to use the Gtk+ API in Python, specially how GtkTextView, GtkTextBuffer and GtkTextIter work. I've learned how to add a new menu item in the menu bar too, and how a plugin works.

Also, I've realized how to manage a git repository -I have some experience using Subversion and Mercurial, but I have never used git before!-, since I had to use the basic commands to create a repository, commit my changes and push them.

Basically, to create a plugin in Python you need two files: a .plugin and a .py.

Here is my wordMixer.plugin file:

[Plugin]
Loader=python
Module=wordMixer
IAge=3
Name=Word Mixer
Description= A plugin that changes the order of the words of a document.
Authors=Laura Lazzati


An here, my wordMixer.py file:

from gi.repository import GObject, Gtk, Gedit
import re

UI_XML = """<ui>
<menubar name="MenuBar">
<menu name="ToolsMenu" action="Tools">
<placeholder name="ToolsOps_3">
<menu name="Submenu" action="SubmenuAction">
<menuitem name="WordMixer" action='WordMixerAction'/>
</menu>
</placeholder>
</menu>
</menubar>
</ui>"""

class WordMixerPlugin(GObject.Object, Gedit.WindowActivatable):

__gtype_name__ = "WordMixer"
window = GObject.property(type=Gedit.Window)


def __init__(self):
GObject.Object.__init__(self)

# Adds a submenu with the WordMixer option to the Tools menu
def _add_ui(self):
manager = self.window.get_ui_manager()
self._actions = Gtk.ActionGroup("PerformAction")
self._actions.add_actions([ ('SubmenuAction', Gtk.STOCK_INFO, "Submenu",None, "This is a submenu", None),
('WordMixerAction', Gtk.STOCK_INFO, "WordMixer",
None, "Changes the order of the words of the document",
self.on_action_activate),
])
manager.insert_action_group(self._actions)
self._ui_merge_id = manager.add_ui_from_string(UI_XML)
manager.ensure_update()


def do_activate(self):
self._add_ui()


def do_deactivate(self):
self._remove_ui()


# Activates the submenu in the Tools menu only if there is a view. Otherwise it remains insensitive.
def do_update_state(self):
view = self.window.get_active_view()
sensitive = False
if view:
sensitive = True
submenuAction = self._actions.get_action('SubmenuAction')
submenuAction.set_sensitive(sensitive)

# Takes each line of the buffer, changes it order, and replaces the line in the buffer with the mixed one.
def _mixer(self, viewBuffer):
firstIter = viewBuffer.get_start_iter()
nextIter = viewBuffer.get_start_iter()
nextIter.forward_line()
while firstIter.equal(viewBuffer.get_end_iter())<> True:
textBuffer = viewBuffer.get_text(firstIter,nextIter,1)
textList = re.split('\s',textBuffer)
textList.reverse();
newText = " ".join(textList)
newText += "\n"
viewBuffer.delete(firstIter, nextIter)
viewBuffer.insert(firstIter, newText)
nextIter = firstIter.copy()
nextIter.forward_line()

# Mixes the lines of the view's buffer.
def on_action_activate(self, action, data=None):
view = self.window.get_active_view()
if view:
viewBuffer = view.get_buffer()
self._mixer(viewBuffer)

# Removes the submenu from the Tools menu.
def _remove_ui(self):
manager = self.window.get_ui_manager()
manager.remove_ui(self._ui_merge_id)
manager.remove_action_group(self._actions)
manager.ensure_update()



In addition, here is the link to my repository in Github.

Thursday, 15 December 2011

My first Gtk+ application

Hi everyone :)
Today, after struggling, I finished my first Gtk+ application in C.
It is a simple application that has  an entry widget and a button. When it is pressed, it counts the number of characters introduced by the user in the entry widget and shows it in a statusbar. Also, it has a menubar with two options: Clear -that clears the entry and statusbar widgets- and Quit, which opens a dialog box asking the user if they are sure about leaving.

This is how it looks:
I wanted to thank my mentor and the guys in #gtk+ because they helped me a lot :).


Here is the code:


#include
#include
#include


struct clear_window_data
{
GtkWidget * entry;
GtkWidget * statusbar;
gint context_id;
};


/******** callback functions **********/
void quit_dialog(GtkWidget *widget, gpointer window)
{
GtkWidget *dialog;
GtkResponseType result;

dialog = gtk_message_dialog_new(GTK_WINDOW(window),
GTK_DIALOG_DESTROY_WITH_PARENT,
GTK_MESSAGE_QUESTION,
GTK_BUTTONS_YES_NO,
"Are you sure to quit?");
gtk_window_set_title(GTK_WINDOW(dialog), "Question");
result= gtk_dialog_run(GTK_DIALOG(dialog));

if (result == GTK_RESPONSE_YES)
{
gtk_widget_destroy (window);

}
gtk_widget_destroy(dialog);

}


void clear_window(GtkWidget *widget, gpointer data)
{
struct clear_window_data * structData;
GtkWidget * entry;
GtkWidget * statusbar;
gint context_id;

structData = data;
entry = structData ->entry;
statusbar =structData ->statusbar;
context_id =structData -> context_id;

gtk_entry_set_text(GTK_ENTRY(entry), "");
gtk_statusbar_push(GTK_STATUSBAR(statusbar),
context_id," ");

}

void button_count(GtkWidget *widget, gpointer data)
{
struct clear_window_data * structData;
GtkWidget * entry;
GtkWidget * statusbar;
gint context_id;
const gchar *str;
char message[100];
int length;

structData = data;
entry = structData ->entry;
statusbar =structData ->statusbar;
context_id =structData -> context_id;

str = gtk_entry_get_text(GTK_ENTRY(entry));
length = strlen(str);
sprintf( message, "Number of chars: %d",length);
gtk_statusbar_push(GTK_STATUSBAR(statusbar),
context_id,message);

}


/********* main program **************/

int main( int argc, char *argv[])
{

struct clear_window_data data;

GtkWidget *window;
GtkWidget *table;
GtkWidget *button;
GtkWidget *entry;
GtkWidget *halign;
GtkWidget *halign2;
GtkWidget *statusbar;
GtkWidget *vbox;
GtkWidget *menubar;
GtkWidget *filemenu;
GtkWidget *file;
GtkWidget *quit;
GtkWidget *clear;

gtk_init(&argc, &argv);

window = gtk_window_new(GTK_WINDOW_TOPLEVEL);
gtk_window_set_position(GTK_WINDOW(window), GTK_WIN_POS_CENTER);
gtk_widget_set_size_request (window, 300, 250);
gtk_window_set_resizable(GTK_WINDOW(window), FALSE);
gtk_window_set_title(GTK_WINDOW(window), "Character counter");


menubar = gtk_menu_bar_new();
filemenu = gtk_menu_new();

file = gtk_menu_item_new_with_label("Options");
clear = gtk_menu_item_new_with_label("Clear");
quit = gtk_menu_item_new_with_label("Quit");

gtk_menu_item_set_submenu(GTK_MENU_ITEM(file), filemenu);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), clear);
gtk_menu_shell_append(GTK_MENU_SHELL(filemenu), quit);
gtk_menu_shell_append(GTK_MENU_SHELL(menubar), file);


button = gtk_button_new_with_label("Count");
gtk_widget_set_size_request(button, 80, 35);

table = gtk_table_new(1, 3, FALSE);
gtk_table_set_col_spacings(GTK_TABLE(table), 3);

entry = gtk_entry_new();

halign = gtk_alignment_new(125, 0, 0, 0);
gtk_container_add(GTK_CONTAINER(halign), entry);

gtk_table_attach(GTK_TABLE(table), halign, 0, 2, 2, 3,
GTK_FILL, GTK_FILL, 0, 0);
halign2 = gtk_alignment_new(0, 100, 0, 0);
gtk_container_add(GTK_CONTAINER(halign2), button);

gtk_table_attach(GTK_TABLE(table), halign2, 2, 3,2, 3,
GTK_FILL, GTK_FILL, 0, 0);

statusbar = gtk_statusbar_new();
data.context_id = gtk_statusbar_get_context_id(GTK_STATUSBAR(statusbar),
"My statusbar");
gtk_statusbar_push(GTK_STATUSBAR(statusbar),data.context_id
, "My statusbar");

vbox = gtk_vbox_new(FALSE, 2);

gtk_container_add(GTK_CONTAINER(window), vbox);

gtk_box_pack_start(GTK_BOX(vbox), menubar, FALSE, FALSE, 3);
gtk_box_pack_start(GTK_BOX(vbox), table, TRUE, TRUE, 1);
gtk_box_pack_start(GTK_BOX(vbox), statusbar, TRUE, TRUE, 1);


g_signal_connect(G_OBJECT(quit), "activate",
G_CALLBACK(quit_dialog), (gpointer) window);

data.entry = entry;
data.statusbar = statusbar;

g_signal_connect(G_OBJECT(clear),"activate",
G_CALLBACK(clear_window), (gpointer) &data);

g_signal_connect(button, "clicked", G_CALLBACK(button_count),
(gpointer) &data);

g_signal_connect_swapped(G_OBJECT(window), "destroy",
G_CALLBACK(gtk_main_quit), NULL);
gtk_widget_show_all(window);

gtk_main();

return 0;
}

Wednesday, 2 November 2011

How to create a patch for a bug for Gedit

Hi everybody!

Here I am going to list a few steps that should be followed for fixing a bug for Gedit. They are not a big deal, but they might be helpful as a memory aid :)

1. Choose a bug from Bugzilla.

2. Open a terminal, and remember to do:

$ jhbuild shell



3. Edit the source code with your favourite tool so that you can fix the bug

4. Once you are ready, buildone the code:

$ jhbuild buildone gedit


5. Very important: TEST YOUR CODE! ;)

6. If everything's ok, then commit your changes:

$ git commit filename/s




7.Create a patch:

$ git format-patch HEAD^


8.Do an attachment to Bugzilla's web page:



Sunday, 30 October 2011

Building Gedit in Fedora

Yesterday, after struggling, I was finally able to build Gedit on my computer :)

First of all, I installed some devel stuff - some of them may not be strictly necessary, but they won't do you any harm either.


# devel stuff
yum install -y @development-tools @development-libs @x-software-development @gnome-software-development iso-codes-devel cmake libffi-devel perl-XML-Simple libtool-ltdl-devel libudev-devel fuse-devel libusb-devel libexif-devel libatasmart-devel libsmbclient-devel webkitgtk-devel cups-devel pulseaudio-libs-devel xcb-util-devel libxslt-devel gnome-doc-utils udisks-devel avahi-devel avahi-ui-devel libstdc++-static
yum install -y man-pages
yum install -y devhelp gitg git-svn valgrind meld ghex gperf cppcheck
yum install -y kernel-devel
yum install -y rpmdevtools
yum install -y @java @java-development @eclipse
yum install -y js


I followed the steps here to install Jhbuild.
My .jhbuildrc files goes here:


# -*- mode: python -*-

#repos['git.gnome.org'] =

#use_local_modulesets = True

moduleset = 'gnome-world-3.2'
modules = [
'gedit',
]

build_policy = 'all'

skip = [
'libxml2',
'libxslt',
'libgpg-error',
'libgcrypt',
'rarian',
'expat',
'dbus',
'iso-codes',
'hal',
'avahi',
'NetworkManager',
'mozilla',
'mono',
'ekiga',
'sqlite3',
'WebKit',
'gstreamer','liboil','gst-plugins-base',
'gjs','gnome-js-common','seed',
]

# the prefix to install things to (user must have write access)
prefix = '/opt/gnome'

checkoutroot = os.path.join(os.environ['HOME'], 'git', 'gnome')

# arguments to pass to autogen script
autogenargs='--enable-maintainer-mode --disable-static --disable-gtk-doc'

os.environ['INSTALL'] = os.path.join(os.environ['HOME'],
'bin', 'install-check')

# parallel build
os.environ['MAKEFLAGS'] = '-j2'

# flags useful for sysprof, gdb etc
os.environ['CFLAGS'] = '-fno-omit-frame-pointer -g -O0 -fno-inline'

addpath('ACLOCAL_FLAGS', '/usr/share/aclocal')
addpath('PKG_CONFIG_PATH', '/usr/lib/pkgconfig')
addpath('PKG_CONFIG_PATH', '/usr/local/lib/pkgconfig')
addpath('PKG_CONFIG_PATH', '/usr/share/pkgconfig')
addpath('PKG_CONFIG_PATH', '/usr/local/share/pkgconfig')
addpath('GI_TYPELIB_PATH', '/opt/gnome/lib64/girepository-1.0')
addpath('PYTHONPATH', '/opt/gnome/lib64/python2.7/site-packages/gtk-2.0')

module_autogenargs['glib'] = '--enable-debug=yes --enable-dtrace=yes --enable-systemtap=yes --disable-visibility'
module_autogenargs['ORBit2'] = '--enable-purify'
module_autogenargs['gimp'] = '--disable-print --enable-python'
module_autogenargs['totem'] = '--disable-mozilla'
module_autogenargs['epiphany'] = autogenargs + ' --with-engine=webkit'
module_autogenargs['libunique'] = '--disable-introspection'
module_autogenargs['poppler'] = '--disable-poppler-qt --disable-poppler-qt4'
module_autogenargs['evince'] = '--enable-nautilus=no'
module_autogenargs['vte-3'] = '--enable-introspection'
module_autogenargs['dconf'] = '--disable-editor'
module_autogenargs["libpeas"] = autogenargs + " --disable-vala"

branches['glib'] = 'master'
branches['gtk+'] = 'master'

After that, I checked everything was ok with:

jhbuild update gedit

Then, I ran:

jhbuild build gedit

Lastly,I ran:

jhbuild build dconf

And that was it :)

Wednesday, 26 October 2011

Escribiendo plugins para Gedit 3 con Python


As part of my contribution to gedit project, I am translating some documents.
Here you can find a Tutorial on how to write plugins for Gedit 3 in Python in my mother tongue language, which is Spanish. I hope you find it as useful as I did :)

Escribiendo plugins para Gedit 3 con Python

La versión en su idioma original se encuentra  acá.
Esta es una guía para programar plugins para Gedit 3, el editor de textos por defecto de GNOME 3. 
Gedit usa el sistema de plugins Libpeas Gobject y los plugins se pueden escribir en C o Python. Esta guía cubrirá la escritura de plugins usando Python.
Parte de la información que proporciono también se encuentra en Python Plugin How To for Gedit en live.gnome.org. Sin embargo, espero proporcionar un enfoque diferente sobre la presentación de la información y agregar algunos principios adicionales.

Antes de empezar

Asegúrese de que tiene gedit 3.xo superior. Los plugins para gedit 2.x no son compatibles con 3.x.

Los Plugins en Python de gedit 3 utilizan los enlaces para PyGObject GObject GLib, Gio y GTK. Este es el reemplazo de PyGTK. Si usted todavía no está familiarizado con PyGObject, lea Introspection Porting Guide(no se preocupe, es fácil pasar de PyGTK a PyGOBject).

Ss usted no tiene experiencia con Python y GTK entonces esta guía, puede no ser adecuada para usted (aún). Dedique algo de tiempo a aprender a crear pequeñas aplicaciones GTK con Python primero.

El archivo .plugin

Todo plugin empieza con un archivo .plugin usado para describir el plugin. Gedit utilizará la información proporcionada en este archivo para cargar el plug-in e incluirlo en la lista de plugins disponibles.
Para que gedit pueda encontrar este archivo . plugin, se debe colocar en el directorio ~ / .local / share / gedit / plugins /, que puede ser necesario que lo cree si no ha instalado ningún plugin de gedit a nivel de usuario .

Nota: Existe un directorio de todo el sistema para plugins, que es donde puede encontrar plugins que fueron instalados con gedit por el administrador de paquetes de software de su distribución.
example01.plugin

[Plugin]

Loader=python

Module=example01

IAge=3

Name=Example #1

Description=A minimal plugin

Authors=Micah Carrick

Copyright=Copyright © 2011 Micah Carrick

Website=http://www.micahcarrick.com

Version=3.0.0


Loader: Para plugins en Python, siempre va Python
Module: El módulo de python del plugin que utiliza las convenciones típicas de nomenclatura a importar. En el archivo .plugin de arriba, Gedit espera encontrar un archivo llamado example01.py en el mismo directorio que el archivo .plugin o un directorio llamado example01que contiene un archivo __init__.py. Vea Python Modules Documentation si no entiende los módulos o paquetes de Python.
iAge:  Los plugins para gedit 3.x siempre tendrá n un "3" para iAge.

Las restantes líneas del archivo .plugin se explican bastante por sí mismas y se muestran en las capturas de pantalla siguientes:







Ejemplo 1: Un plugin simple




from gi.repository import GObject, Gedit



class ExamplePlugin01(GObject.Object, Gedit.WindowActivatable):

    __gtype_name__ = "ExamplePlugin01"

    window = GObject.property(type=Gedit.Window)



    def __init__(self):

        GObject.Object.__init__(self)



    def do_activate(self):

        print "Window %s activated." % self.window



    def do_deactivate(self):

        print "Window %s deactivated." % self.window



    def do_update_state(self):

        print "Window %s state updated." % self.window


Este ejemplo muestra un plugin muy simple. Este plugin implementa la interfaz Gedit.WindowActivatable para el punto de extensión Gedit.Window (lo cual no debería tener ningún sentido por ahora). El concepto de puntos de extensión se examinarán con más detalle en un ejemplo diferente. Por ahora, diremos que este ejemplo se colocará en cada ventana de gedit (por lo general sólo una).

Para ver cómo funciona este plugin:
1. Asegúrese de que example01.plugin y example01.py se copiaron al directorio ~/.local/share/gedit/plugins/
2.Ejecute gedit desde una terminal para que pueda ver la salida de las sentencias print.
3. Abra el cuadro de diálogos de Preferencias (Edición > Preferencias). Elija la pestaña de Plugins y active el plugin Example #1.
4.Cierre el cuadro de diálogos de Preferencias y abra un nuevo documento con Gedit.
5.Vuelva a Plugins y desactive el plugin Example #1.
En la ventana de la terminal debería ver los siguientes mensajes:
Window <Window object at 0x1104e10 (GeditWindow at 0x1180020)> activated.
Window <Window object at 0x1104e10 (GeditWindow at 0x1180020)> state updated.
Window <Window object at 0x1104e10 (GeditWindow at 0x1180020)> deactivated.

Dado que este es el primer ejemplo, hay algunas cosas a destacar. En primer lugar, la declaración de importación es un poco diferente de lo a que puede estar acostumbrado:

Esta declaración de importación utiliza PyGObject para importar GObject y las APIs de gedit.
La clase ExamplePlugin01 hereda del objeto Gobject.Object y de la interfaz Gedit.WindowActivatable. Los plugins de Gedit siempre extenderán el Gobject.Object e implementan una de las tres interfaces del punto de extensión (nuevamente, los puntos de extensión se explicarán en un ejemplo posterior).
Al implementar la interfaz Gedit.WindowActivatable, este plugin debe definir tres métodos: do_activate(), do_deactivate(), and do_update_state(). La activación ocurre cuando el plugin se activa desde el cuadro de diálogo de Preferencias o cuando gedit se inicia. La desactivación ocurre cuando se desactiva el plugin desde el cuadro de diálogo de Preferencias o cuando se cierra gedit. El estado de actualización ocurre cuando algo cambia en la ventana UI del gedit, como por ejemplo agregar, guardar o eliminar una pestaña.
En consecuencia, este plugin no hace nada en sí mismo. Es solo un ejemplo con el mínimo código necesario para implementar un plugin de gedit.


Ejemplo 2: Puntos de extensión




from gi.repository import GObject, Gedit



class ExampleAppActivatable(GObject.Object, Gedit.AppActivatable):

    __gtype_name__ = "ExampleAppActivatable"

    app = GObject.property(type=Gedit.App)



    # ...



class ExampleWindowActivatable(GObject.Object, Gedit.WindowActivatable):

    __gtype_name__ = "ExampleWindowActivatable"

    window = GObject.property(type=Gedit.Window)



    # ...



class ExampleViewActivatable(GObject.Object, Gedit.ViewActivatable):

    __gtype_name__ = "ExampleViewActivatable"

    view = GObject.property(type=Gedit.View)



    # ...


Este plugin es similar al Ejemplo #1 excepto porque implementa cada una de las tres interfaces de extensión. La mayoría de los plugins implementan sólo una interfaz de extensión basándose en los objetivos del plugin.

Gedit.AppActivatable

Incluso cuando tiene múltiples ventanas de Gedit abiertas, hay sólo una instancia de la aplicación que administra las ventanas abiertas. Un plugin que implementa la interfaz Gedit.AppActivatable se activa cuando la primera ventana de gedi se abre y se desactiva cuando la última ventana de gedit se cierra (o desde el administrador de plugins en el cuadro de diálogo Preferencias).
El plugin interactuaría entonces con gedit a través de la API Gedit.App.

Gedit.WindowActivatable

Un plugin que implementa la interfaz Gedit.WindowActivatable se activa cuando cada ventana se abre y se desactiva cuando la ventana se cierra. Los plugins WindowActivatable también implementan el método do_update_state() para cuando la UI de esa ventana cambia.
El plugin interactuaría entonces con la API Gedit.Window.

Gedit.ViewActivatable

Un plugin que implementa la interfaz Gedit.ViewActivatable se activa cuando se crea una vista y se desactiva cuando esa vista se cierra. Una vista es el widget que muestra el texto del documento.
Los plugins ViewActivatable también implementan el método do_update_state() para cuando la UI de esa ventana cambia.
El plugin interactuaría con gedit por medio de la API Gedit.View (que hereda de Gedit.View, que a su vez hereda de Gtk.TextVie).


Eligiendo la extensión a implementar
La mayoría de los plugins implementan la interfaz Gedit.WindowActivatable o Gedit.ViewActivatable.
Un plugin implementará la interfaz Gedit.WindowActivatable si necesita administrar o interactuar con múltiples documentos o si necesita agregar elementos a la UI (como menúes y barras de herramientas, paneles laterales, paneles inferiores, etc). Ejemplos de plugins de este tipo son la terminal embebida y el selector de color.
Un plugin implementará la interfaz Gedit.ViewActivatable si necesita administrar cómo es manejado el texto. Ejemplos de plugins de este tipo son los plugins de espacios inteligentes y de completitud de paréntesis.
Los ejemplos en esta guía implementan la interfaz Gedit.WindowActivatable.

Ejemplo 3: Conexión a señales.
Download Example #3


from gi.repository import GObject, Gtk, Gedit



class ExamplePlugin03(GObject.Object, Gedit.WindowActivatable):

    __gtype_name__ = "ExamplePlugin03"

    window = GObject.property(type=Gedit.Window)



    def __init__(self):

        GObject.Object.__init__(self)



    def do_activate(self):

        print "Activating plugin..."

        handlers = []

        handler_id = self.window.connect("tab-added", self.on_tab_added)

        handlers.append(handler_id)

        print "Connected handler %s" % handler_id

   

        handler_id = self.window.connect("tab-removed", self.on_tab_removed)

        handlers.append(handler_id)

        print "Connected handler %s" % handler_id

   

        self.window.set_data("ExamplePlugin03Handlers", handlers)



    def do_deactivate(self):

        print "Deactivating plugin..."

        handlers = self.window.get_data("ExamplePlugin03Handlers")

        for handler_id in handlers:

            self.window.disconnect(handler_id)

            print "Disconnected handler %s" % handler_id



    def do_update_state(self):

        pass



    def on_tab_added(self, window, tab, data=None):

        document = tab.get_document()

        print "'%s' has been added." % document.get_short_name_for_display()



    def on_tab_removed(self, window, tab, data=None):

        document = tab.get_document()

        print "'%s' has been removed." % document.get_short_name_for_display()


Como el resto de GTK, los distintos objetos de gedit emiten señales para los diversos eventos que ocurren. Un plugin puede conectar métodos manejadores (callbacks) a cualquiera de estas señales.
Como un plugin puede ser activado o desactivado por el usuario final, es importante utilizar los métodos do_activate() y do_deactivate() para asegurarse de que las señales son conectadas y desconectadas según el plugin es activado o desactivado.
Este ejemplo se conecta a las señales "tab-added" y "tab-removed"de GeditWindow. Cada handler_id de estas conexiones, tal como son devueltos por el método connect(), se almacenan en una lista y se adjuntan al objeto ventana usando set_data(). Cuando se desactiva el plugin, la lista de handler_ids se devuelve usando get_data() y es iterada para desconectar cada manejador de señal .
La salida de este plugin debería ser similar a la siguiente:

Activating plugin...
Connected handler 2271
Connected handler 2272
''Unsaved Document 1'' has been added.
''Unsaved Document 1'' has been removed.
''Unsaved Document 2'' has been added.
''Unsaved Document 2'' has been removed.
Deactivating plugin...
Disconnected handler 2271
Disconnected handler 2272


Conectarse a señales para manejar eventos es la base para escribir plugins de gedit que verdaderamente hagan algo. Navegue Gedit 3 API para ver los diversos objetos y sus señales. Y no se olvide que también tiene disponibles a las señales de los objetos padres. Por ejemplo, GeditWindow hereda de GtkWindow, entonces se podría conectar con la señal "set-focus" signal emitida por GeditWindow.
Nota: las señales son un concepto fundamental de la programación GTK y están más allá del alcance de este texto. Se asume que está familiarizado con eventos y señales en GTK

Ejemplo 4: Insertando items de menú.
Download Example #4

from gi.repository import GObject, Gtk, Gedit



UI_XML = """<ui>

<menubar name="MenuBar">

    <menu name="ToolsMenu" action="Tools">

      <placeholder name="ToolsOps_3">

        <menuitem name="ExampleAction" action="ExampleAction"/>

      </placeholder>

    </menu>

</menubar>

</ui>"""



class ExamplePlugin04(GObject.Object, Gedit.WindowActivatable):

    __gtype_name__ = "ExamplePlugin04"

    window = GObject.property(type=Gedit.Window)

  

    def __init__(self):

        GObject.Object.__init__(self)



    def _add_ui(self):

        manager = self.window.get_ui_manager()

        self._actions = Gtk.ActionGroup("Example04Actions")

        self._actions.add_actions([

            ('ExampleAction', Gtk.STOCK_INFO, "Say _Hello",

                None, "Say hello to the current document",

                self.on_example_action_activate),

        ])

        manager.insert_action_group(self._actions)

        self._ui_merge_id = manager.add_ui_from_string(UI_XML)

        manager.ensure_update()

    

    def do_activate(self):

        self._add_ui()



    def do_deactivate(self):

        self._remove_ui()



    def do_update_state(self):

        pass



    def on_example_action_activate(self, action, data=None):

        view = self.window.get_active_view()

        if view:

            view.get_buffer().insert_at_cursor("Hello World!")

    

    def _remove_ui(self):

        manager = self.window.get_ui_manager()

        manager.remove_ui(self._ui_merge_id)

        manager.remove_action_group(self._actions)

        manager.ensure_update()


Los plugins que implementan la interfaz Gedit.WindowActivatable frecuentemente necesitan agregar items al menú de gedit. Como Gedit.Window expone su GtkUIManager a través del método get_ui_manager(), agregar un nuevo ítem al menú es tan simple como mezclar una definición UI XML.
Gedit provee varios elementos <placeholder> que un plugin puede elegir para agregarle items de menú. Este plugin ejemplo inserta un ítem de menú al placeholder ToolsOps_3". Puede ver la lista de los placeholder disponibles en gedit-ui.xml.
Este ejemplo agrega un item al menú “Tools” llamado “Say Hello”. Cuando se lo selecciona, la frase “Hello world” es insertada en el documento actual.

Nuevamente, es importante para el plugin insertar ítems de menú en el método do_activate() y eliminar dichos ítems en el método do_deactivate(). Sólo para mantener el código cuidadosamente organizado, este ejemplo pone el código para agregar y eliminar el UI XML del plugin en métodos llamados add_ui() y remove_ui() respectivamente.

Ejemplo 5: agregando paneles laterales e inferiores
Download Example #5


from gi.repository import GObject, Gtk, Gedit



class ExamplePlugin05(GObject.Object, Gedit.WindowActivatable):

    __gtype_name__ = "ExamplePlugin05"

    window = GObject.property(type=Gedit.Window)

  

    def __init__(self):

        GObject.Object.__init__(self)

     

    def do_activate(self):

        icon = Gtk.Image.new_from_stock(Gtk.STOCK_YES, Gtk.IconSize.MENU)

        self._side_widget = Gtk.Label("This is the side panel.")

        panel = self.window.get_side_panel()

        panel.add_item(self._side_widget, "ExampleSidePanel", "Example #5", icon)

        panel.activate_item(self._side_widget)



    def do_deactivate(self):

        panel = self.window.get_side_panel()

        panel.remove_item(self._side_widget)



    def do_update_state(self):

        pass



</ui>"""



class ExamplePlugin04(GObject.Object, Gedit.WindowActivatable):

    __gtype_name__ = "ExamplePlugin04"

    window = GObject.property(type=Gedit.Window)

  

    def __init__(self):

        GObject.Object.__init__(self)

 

    def _add_ui(self):

        manager = self.window.get_ui_manager()

        self._actions = Gtk.ActionGroup("Example04Actions")

        self._actions.add_actions([

            ('ExampleAction', Gtk.STOCK_INFO, "Say _Hello",

                None, "Say hello to the current document",

                self.on_example_action_activate),

        ])

        manager.insert_action_group(self._actions)

        self._ui_merge_id = manager.add_ui_from_string(UI_XML)

        manager.ensure_update()

     

    def do_activate(self):

        self._add_ui()



    def do_deactivate(self):

        self._remove_ui()



    def do_update_state(self):

        pass

 

    def on_example_action_activate(self, action, data=None):

        view = self.window.get_active_view()

        if view:

            view.get_buffer().insert_at_cursor("Hello World!")

     

    def _remove_ui(self):

        manager = self.window.get_ui_manager()

        manager.remove_ui(self._ui_merge_id)

        manager.remove_action_group(self._actions)

        manager.ensure_update()



Los plugins que implementan la interfaz Gedit.WindowActivatable también frecuentemente agregan items a los paneles laterales o inferior. Gedit.Window proporciona los métodos get_side_panel() y get_bottom_panel() para obtener un objeto Gedit.Panel. El ítem del panel luego se agrega con add_item().
Este plugin ejemplo agrega un simple Gtk.Label al panel lateral cuando es activado y elimina dicho ítem del panel usando remove_item() cuando el plugin es desactivado.
 
Ejemplo 6: El cuadro de diálogo de configuración
Download Example #6

from gi.repository import GObject, Gtk, Gedit, PeasGtk



class ExamplePlugin06(GObject.Object, Gedit.AppActivatable, PeasGtk.Configurable):

    __gtype_name__ = "ExamplePlugin06"

    window = GObject.property(type=Gedit.Window)

  

    def __init__(self):

        GObject.Object.__init__(self)

       

    def do_activate(self):

        pass

   

    def do_create_configure_widget(self):

        widget = Gtk.CheckButton("A configuration setting.")

        widget.set_border_width(6)

        return widget

       

    def do_deactivate(self):

        pass



    def do_update_state(self):

        pass


Este ejemplo provee las opciones de configuración al usuario al implementar PeasGtk.Configurable y proveyendo el método do_create_configure_widget() para construir el widget que gedit mostrará en un cuadro de diálogo de configuración. A diferencia de versiones anteriores de Gedit, este método devuelve el widget hijo en un cuadro de diálogo y no el cuadro de diálogo completo en sí mismo. Gedit manejará la inserción de este widget en un cuadro de diálogo y su presentación (mire la captura de pantalla de abajo).


Un plugin de gedit debería idealmente guardar las opciones de configuración de usuario usando Gsettings, la API de alto nivel para opciones de configuración de la aplicación (en GNOME 3 es la API frontend de dconf). Los plugins que vienen con gedit usan el esquema org.gnome.gedit.plugins para opciones de configuración. Using GSettings with Python/PyGObject proporciona un tutorial corto.
Hay un problema con la implementación actual de Gsettings ya que requiere compilar y copiar los esquemas en un directorio del sistema, de esta forma negando la simplicidad y conveniencia de guardar los plugins de python en un único directorio. Bug #649717 está siguiendo dicho problema.


Usando la documentación de la API
Al trabajar en los plugins de gedit probablemente necesitará referirse a la documentación de la API.
Hasta ahora no hay mucha documentación de Python. La razón es, en parte, porque PyGObject mapea la API de C muy de cerca. El proyecto PyGObject es también relativamente nuevo. Por suerte para nosotros, la API de C es suficiente.
Mucho de esto está cubierto en Introspection Porting Guide. Sin embargo, a continuación hay algunos ejemplos para ilustrar cómo la API de C de estas bibliotecas se traducen fácilmente a Python.


C API Python (PyGObject)
gedit_window_close_tab() Gedit.Window.close_tab()
gedit_document_get_location() Gedit.Document.get_location()
gtk_button_new() Gtk.Button()
gdk_pixbuf_new_from_file() GdkPixbuf.Pixbuf.new_from_file()



A continuación hay algunos links a bibliotecas de C usadas frecuentemente para plugins de gedit y sus correspondientes módulos PYGObject.





 





 















 

 

Monday, 24 October 2011

First steps in Fedora

After two years using Ubuntu, yesterday I installed Fedora on my computer.
I have always wanted to experiment with other distros, and now seems to be the opportunity to do so, as I wanted to have a new clean distro to install there everything I need for the project.
I had some normal technical problems, such as not being able to boot my Ubuntu partition from Fedora's grub and not being able to run the script to install my wireless usb device - hope to solve them soon!-, but, in spite of them, I'm happy with my new distro :)
Now I have to set it up and learn more about it!

Sunday, 23 October 2011

First Entry

Hi everybody :)
My name is Laura, better known as Laurita.
I am a 23- years-old software engineering student that lives in Buenos Aires, Argentina, with my lovely dog  - yes, apart from computers, I love animals, specially dogs.

The aim of this blog is to share my experience with free software, as I have always been a great fan of it  and now I'm having the chance to participate in a project.
I will be taking part in my favourite text editor's project, which is gedit . So, I will be writing here what I learn and consider relevant so that I can share it with others :)
Welcome to my blog!