IT Notes

Создание Qt-плагинов

Плагины в Qt - динамически подключаемые библиотеки. Мы уже немного затрагивали преимущества от использования плагинов, когда говорили о создании гибкого кода.

А сейчас займемся практикой и напишем свой собственный Qt-плагин.

Создание интерфейса плагина

Опишем интерфейс плагина в файле myplugininterface.h:

#ifndef MYPLUGININTERFACE_H
#define MYPLUGININTERFACE_H

#include <QString>
#include <QVariant>

#include <QtPlugin>

class MyPluginInterface {
public:
    virtual ~MyPluginInterface() { }

    virtual QString getString() const = 0;
    virtual QVariant getVar() const = 0;
};

Q_DECLARE_INTERFACE( MyPluginInterface, "ru.itnotesblog.MyApp.MyPluginInterface/1.0" )

#endif // MYPLUGININTERFACE_H

MyPluginInterface предоставляет две операции, возвращающих значения. Особый интерес представляет вторая (getVar()), которая работает с обобщенным типом QVariant. С его помощью вы можете создавать сверх-универсальные интерфейсы.

Чтобы интерфейс можно было использовать для создания плагинов, необходимо применение макроса Q_DECLARE_INTERFACE. Первым параметром он принимает класс интерфейса, а вторым - идентификационную строку.

Идентификационная строка состоит из нескольких частей (по структуре похоже на название пакетов в Java):

  1. Обратный адрес домена;
  2. Название приложения, в котором определен интерфейс;
  3. Имя интерфейса;
  4. Номер версии интерфейса (через слэш).

На самом деле, такой формат не является абсолютно обязательным. Достаточно, чтобы строка была уникальной. Но я рекомендую вам придерживаться принятых стандартов.

Реализация плагина

Создадим файл проекта (MyPlugin.pro):

QT       += core
QT       -= gui

TARGET = MyPlugin
TEMPLATE = lib
CONFIG += plugin
DESTDIR = ../../bin/plugins/

SOURCES += myplugin.cpp
HEADERS += myplugin.h

INCLUDEPATH += ../include/

Конфигурация проекта напоминает обычную динамическую библиотеку. Ключевое отличие заключается в строке CONFIG += plugin. Она указывает на то, что создаем мы именно плагин, а не что-то другое.

INCLUDEPATH определен таким образом, чтобы мы смогли использовать myplugininterface.h. Скомпонованный плагин будет помещен в DESTDIR.

Заголовочный файл myplugin.h:

#ifndef MYPLUGIN_H
#define MYPLUGIN_H

#include <myplugininterface.h>

class MyPlugin : public QObject, public MyPluginInterface {
    Q_OBJECT
    Q_INTERFACES( MyPluginInterface )

public:
    ~MyPlugin();

    QString getString() const;
    QVariant getVar() const;
};

#endif // MYPLUGIN_H

Три важных момента:

  1. Наследование QObject обязательно;
  2. Без Q_OBJECT плагин работать не будет;
  3. С помощью макроса Q_INTERFACES явно указываются реализуемые интерфейсы.

Соответствующая реализация (myplugin.cpp):

#include "myplugin.h"

#include <QRect>

MyPlugin::~MyPlugin() {
}

QString MyPlugin::getString() const {
    return "Hello, Plugin!";
}

QVariant MyPlugin::getVar() const {
    return QRect( 10, 10, 500, 500 );
}

Q_EXPORT_PLUGIN2( MyPluginInterface, MyPlugin )

Главное: не забудьте экспортировать плагин с помощью макроса Q_EXPORT_PLUGIN2.

Использование плагина в приложении

Для подключения плагинов к Qt-приложению не нужно никаких особых настроек проекта. Создадим простое консольное Qt-приложение с таким main.cpp:

#include <QDir>
#include <QPluginLoader>
#include <QDebug>

#include "myplugininterface.h"

int main() {
    QDir pluginsDir( "./plugins" );
    foreach( const QString& pluginName, pluginsDir.entryList( QDir::Files ) ) {
        qDebug() << "===============================================================================";
        qDebug() << "Found:" << pluginName;

        QPluginLoader loader( pluginsDir.absoluteFilePath( pluginName ) );
        if( loader.load() ) {
            if( MyPluginInterface* myPlugin = qobject_cast< MyPluginInterface* >( loader.instance() ) ) {
                qDebug() << "Testing: \n" <<
                            "(1)" << myPlugin->getString() << "\n" <<
                            "(2)" << myPlugin->getVar();
            }
            loader.unload();
        } else {
            qDebug() << "Failed to load :(";
            qDebug() << loader.errorString();
        }

        qDebug() << "";
    }

    return 0;
}

Поиск плагинов осуществляется в цикле по содержимому каталога ./plugins. Загрузить Qt-плагин можно с помощью QPluginLoader.

Явно вызывать load() не обязательно. Загрузка в любом случае произойдет при получении экземпляра экспортированного класса из плагина (функция-член instance()).

Функция instance() возвращает указатель на QObject. Чтобы начать работу с плагином, требуется сделать приведение типа. Для этого используйте qobject_cast.

Важно: не освобождайте память для созданного объекта. Если объект вам больше не нужен, то вызовите функцию unload(). В этом случае плагин выгрузится из памяти со всеми созданными экземплярами.

Результат работы программы:

===============================================================================
Found: "libMyPlugin.so"
Testing:
 (1) "Hello, Plugin!"
 (2) QVariant(QRect, QRect(10,10 500x500) )

Похожие публикации

Комментарии

Начинаю писать свой сайт. Очень полезная инфа.

Рад слышать. Удачи! :)

ну чо ко так

Очень хотелось бы почитать про Qt Scripting, собственно, тоже как еще одно средство расширения функционала основного приложения…

overlapped:

Очень хотелось бы почитать про Qt Scripting, собственно, тоже как еще одно средство расширения функционала основного приложения…

К следующей неделе постараюсь подготовить вводную статью.

Спасибо, очень полезная информация.

Привет , такая ситуация , под архитектурой (32x) работает на ура , при переводе на (64x) ругается на строку Q_INTERFACES("имя интерфейса") с ошибкой Undefined interface . Не могу разобраться в чем дело . Подскажешь ?