Плагины в Qt — динамически подключаемые библиотеки. Мы уже немного затрагивали преимущества от использования плагинов, когда говорили о создании гибкого кода.
А сейчас займемся практикой и напишем свой собственный Qt-плагин.
Создание интерфейса плагина
Опишем интерфейс плагина в файле myplugininterface.h:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
#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):
- Обратный адрес домена;
- Название приложения, в котором определен интерфейс;
- Имя интерфейса;
- Номер версии интерфейса (через слэш).
На самом деле, такой формат не является абсолютно обязательным. Достаточно, чтобы строка была уникальной. Но я рекомендую вам придерживаться принятых стандартов.
Реализация плагина
Создадим файл проекта ( MyPlugin.pro):
1 2 3 4 5 6 7 8 9 10 11 12 |
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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 |
#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 |
Три важных момента:
- Наследование QObject обязательно;
- Без Q_OBJECT плагин работать не будет;
- С помощью макроса Q_INTERFACES явно указываются реализуемые интерфейсы.
Соответствующая реализация ( myplugin.cpp):
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
#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:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
#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(). В этом случае плагин выгрузится из памяти со всеми созданными экземплярами.
Результат работы программы:
1 2 3 4 5 |
=============================================================================== Found: "libMyPlugin.so" Testing: (1) "Hello, Plugin!" (2) QVariant(QRect, QRect(10,10 500x500) ) |
Начинаю писать свой сайт. Очень полезная инфа.
Рад слышать. Удачи!
Очень хотелось бы почитать про Qt Scripting, собственно, тоже как еще одно средство расширения функционала основного приложения…
К следующей неделе постараюсь подготовить вводную статью.