12/18/10

QT Aplikacja z pluginami

Ostatnio rozpocząłem swoją przygodę z pisaniem aplikacji w QT i zacząłem się zastanawiać, czy trudnym jest napisanie aplikacji w QT do której można pisać pluginy.

Okazało się to prostsze niż sądziłem.

Jak stworzyć aplikację obsługującą pluginy? Oto kilka kroków.
1. Stworzenie interface'u umożliwiającego komunikację pomiędzy pluginem a programem głównym.
2. Stworzenie klasy implementującej interface.
3. Załadowanie pluginu w programie głównym i jego użycie.

Przykład:
1. Interface (interface.h)

#ifndef INTERFACES_H
#define INTERFACES_H
#include <QtPlugin>
class DBConnection
{
public:
    virtual ~DBConnection() {}
    virtual QString getName() = 0;
};
Q_DECLARE_INTERFACE(DBConnection,"myDBConnection");
#endif // INTERFACES_H





Najważniejszy element interface'u zaznaczyłem czcionką pogrubioną. 
Należy pamiętać o tym aby zadeklarować interface za pomocą macro Q_DECLARE_INTERFACE

Skoro mamy już interface, stwórzmy plugin w postaci biblioteki implementującej interface. 


2. Klasa implementująca interface.


#ifndef TESTPLUGIN_H
#define TESTPLUGIN_H
//załączenie interface'u zadeklarowanego w programie głównym
#include "../../interfaces.h" 
#include "TestPlugin_global.h"
class TestPlugin : public QObject,
                   public DBConnection //implementacja interface'u
{
    Q_OBJECT
// informacja dla MOC (meta-object compiler),
// że klasa bazowa jest pluginem
    Q_INTERFACES(DBConnection); 
public:
    TestPlugin();
    QString getName();
};
#endif // TESTPLUGIN_H
Ważnym jest użycie Q_INTERFACES ,dzieki niemu możliwe będzie użycie qobject_cast podczas ładowania pluginu.

3. Załadowanie pluginu.

W tym miejscu należy zaznaczyć, że pluginy znajdują sie w folderze PLUGINS w miejscu gdzie znajduje się wykonywalny plik aplikacji.


#include "mainwindow.h"
#include "ui_mainwindow.h"
#include <interfaces.h>
#include <QPluginLoader>
#include <QDir>
MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    ui->setupUi(this);
    QDir pluginsDir;

    pluginsDir = QDir(qApp->applicationDirPath());
    pluginsDir.cd("PLUGINS");
    foreach (QString fileName, pluginsDir.entryList(QDir::Files)) 
    {
        QPluginLoader loader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = loader.instance();
        QString name;
        if (plugin) 
        {
            DBConnection *iDBConnection = 
                          qobject_cast<DBConnection *>(plugin);
            if (iDBConnection)
                name = iDBConnection->getName();
        }
    }
}
MainWindow::~MainWindow()
{
    delete ui;
}


QPluginLoader umożliwia załadowanie pluginu o nazwie podanej jako parametr.
QPluginLoader::instance() zwróci wskaźnik do pluginu. Jeśli ładowany plugin nie jest QTPlugin to metoda zwróci null.
qobject_cast umożliwia zrzutowanie ładowanego pluginu na konkretny interface. Jeśli jest nie null, możliwe jest wywołanie metody z interface'u. Jeśli chcemy obsłużyć więcej niż jeden iterface musimy sprawdzić z jakim typem mamy do czynienia po załadowaniu.

Opis ten powstał na podstawie:
http://doc.trolltech.com/latest/tools-plugandpaint.html#the-plugin-interfaces
http://doc.trolltech.com/latest/tools-plugandpaintplugins-basictools.html