Development/Tutorials/Services/Plugins (es)

From KDE TechBase
 
Proposed for Deletion
This page has been proposed for deletion for the following reason:

Page was created before the current translation system.

Creando y cargando Complementos usando KService
Tutorial Series   Services
Previous   Traders: Querying the System
What's Next   n/a
Further Reading   n/a

Resumen

Antes de empezar con el código, vamos a ver qué ventajas podemos obtener desarrollando una aplicación estructurada en complementos (plugins). Primero de todo, una aplicación capaz de manejar complementos puede extender su funcionalidad sin límites aparentes. No obstante, en lugar de desarrollar un aplicación estructurada en complementos porque "es muy guay!!", el desarrollador debería pensar en las ventajas reales de este tipo de desarrollos. Por ejemplo podemos pensar en una aplicación que maneja archivos: el programa principal delega la creación/extracción/edición de cada tipo de archivo al complemento específico. De esta manera conseguimos modularidad en la aplicación, de forma que soportar nuevos tipos de archivos en el futuro consistirá simplemente en escribir el complemento específico y no en reescribir la aplicación. Bien, dejemos de hablar... y empecemos a programar =).

Ejemplo del Editor de Textos

Aunque una aplicación de manejo de archivos sería resultaría más bonita, aquí examinaremos un editor de textos simple capaz de cargar complementos. Cada complemento proveerá simplemente una característica nueva para la edición de texto.

Creando una clase como Interfaz de Complementos

El paso principal para crear una estructura de complementos es definir su clase base. Esta clase será heredada por cada complemento y debería dar acceso a los recursos de la aplicación principal. En este ejemplo la clase principal de un complemento heredará un KXMLGUIClient, ya que cada complemento dará acceso a sus añadidos a través de un elemento gráfico (un KAction en este caso).

#include <kdemacros.h>
#include <kxmlguiclient.h>
#include <QObject>

class KTextEdit;

class KDE_EXPORT Plugin : public QObject , public KXMLGUIClient
{
    Q_OBJECT

    public:
        Plugin(QObject *parent);
        virtual ~Plugin();

        /**
         * @return KTextEdit apunta al control de edición de la aplicación principal
         */
        KTextEdit* editorInterface();

        /**
         * @internal Usado por la aplicación principal para establecer correctamente el editor.
         * No llamar desde la reimplementación.
         */
        void setEditorInterface(KTextEdit *);

    private:
        class PluginPrivate;
        PluginPrivate *d;

};

Esta clase base dará los punteros necesarios para cada complemento que le permitan manipular los datos de la aplicación principal. En este caso, sólo damos acceso a la interfaz del editor principal a través de editorInterface().

Aquí tenemos la implementación:

#include "plugin.h"
#include <KTextEdit>

class Plugin::PluginPrivate {

public:
    PluginPrivate(Plugin *q):
                  q(q),
                  m_editor(0){}

    Plugin *q;
    KTextEdit *m_editor;
};

Plugin::Plugin(QObject *parent) : QObject(parent),
                                  d(new PluginPrivate(this))
{}

Plugin::~Plugin()
{}

KTextEdit* Plugin::editorInterface()
{
    return d->m_editor;
}

void Plugin::setEditorInterface(KTextEdit *editor)
{
    d->m_editor = editor;
}

Donde plugin.h es el archivo cabecera visto anteriormente. El método setEditorInterface permite a la aplicación principal establecer el puntero al KTextEditor. De esta forma, cada programador será capaz de acceder a la interfaz del editor de textos principal con él.

La Macro de la fábrica de complementos

Cada complemento estará "visible y disponible" a través del uso de una macro. Por tanto, mejor poner esta macro en nuestro propio myplugin_macros.h como sigue.

#include <kdemacros.h>
#include <KPluginFactory>
#include <KPluginLoader>

#define TEXTEDITOR_PLUGIN_EXPORT( c ) \
  K_PLUGIN_FACTORY( TextEditorFactory, registerPlugin< c >(); ) \
  K_EXPORT_PLUGIN( TextEditorFactory("c") )

Esta macro simplemente unifica dos macros de KDE que nos permiten la exportación del complemento.

Material para la carga de complementos

Ahora preparémonos para hacer a nuestra aplicación encontrar y cargar todos los complementos compatibles. Haremos uso de un ayudante que usa los métodos estándar de KDE para localizar y cargar complementos. Aquí tenemos la cabecera e implementación.

PLUGINLOADER.H

#include <QObject>

#include "plugintest_macros.h"

class Plugin;

class PluginLoader : public QObject
{
    Q_OBJECT
    public:
        PluginLoader(QObject * parent);
        virtual ~PluginLoader();

        void loadAllPlugins();

    signals:
        void pluginLoaded(Plugin * plugin);
};

PLUGINLOADER.CPP

#include "pluginloader.h"

#include "plugin.h"

#include <KServiceTypeTrader>
#include <KDebug>

PluginLoader::PluginLoader(QObject * parent)
  : QObject(parent)
{
}

PluginLoader::~PluginLoader()
{
}

void PluginLoader::loadAllPlugins()
{
    kDebug() << "Cargar todos los complementos";
    KService::List offers = KServiceTypeTrader::self()->query("TextEditor/Plugin");

    KService::List::const_iterator iter;
    for(iter = offers.begin(); iter < offers.end(); ++iter)
    {
       QString error;
       KService::Ptr service = *iter;

        KPluginFactory *factory = KPluginLoader(service->library()).factory();

        if (!factory)
        {
            //KMessageBox::error(0, i18n("<html><p>KPluginFactory no pudo cargar el complemento:<br/><i>%1</i></p></html>",
              //                         service->library()));
            kError(5001) << "KPluginFactory no pudo cargar el complemento:" << service->library();
            continue;
        }

       Plugin *plugin = factory->create<Plugin>(this);

       if (plugin) {
           kDebug() << "Cargar complemento:" << service->name();
           emit pluginLoaded(plugin);
       } else {
           kDebug() << error;
       }
    }
}

El código mostrado simplemente pregunta a SyCoCa por un complemento para el TextEditor. Cuando creamos un complemento, se provee un archivo .desktop junto con el Tipo de Service para el cual el complemento está diseñado. Veremos más tarde cómo establecer un Tipo de Servicio adecuado a nuestro propio complemento. Emitimos la señal pluginLoaded cuando el complemento está cargado correctamente de forma que la aplicación lo integre en la interfaz gráfica.

Manejo de los complementos por la aplicación principal

Mostraré trozos de código que manejan la carga de complementos en la ventana principal. Vamos a asumir que tenemos un puntero a nuestro KTextEdit llamado simplemente editor.

void MyMainWindow::loadPlugins()
{
    PluginLoader *loader = new PluginLoader(this);
    connect(loader, SIGNAL(pluginLoaded(Plugin*)), this, SLOT(addPlugin(Plugin*)));
    pluginLoader->loadAllPlugins();
}

Si con este trozo de código hemos preguntado a PluginLoader que cargue todos los complementos y llamado al slot pluginLoaded, ahora implementaremos los complementos en la interfaz. El método loadPlugins, se supone, es llamado al principio de la aplicación para cargar todo antes que el usuario pueda comenzar a usarlo. Por supuesto puedes decidir qué hacer con tus complementos e ignorar esta sugerencia :).

void MyMainWindow::addPlugin(Plugin *plugin)
{
    plugin->setEditorInterface(editor);
    guiFactory()->addClient(plugin);
}

Como nuestro complemento hereda de KXMLGuiClient, podemos usar cualquier tipo de interfaz gráfica cliente y KDE lo cargará automáticamente en nuestra aplicación. Por tanto, este pequeño código simplemente integra nuestro complemento con la ventana principal.

Ejemplo de un complemento simple