- PVSM.RU - https://www.pvsm.ru -
Расширения это совместно используемая динамическая библиотека предназначения для загрузки в процессе исполнения основного приложения, которая обязательно должна реализовывать хотя бы один специальный интерфейс.
Расширения делятся на два типа:
Разберём как создать свою систему расширений и сами расширения для него.
Связь с расширением осуществляется с помощью интерфейса (сигналы, слоты и методы класса). Расширение загружается приложением при помощи класса QPluginLoader. Для загрузки расширения используется метод instance(), который создаёт объект расширения и возвращает указатель на него. Для выгрузки расширения используется метод unload().
В первом примере создадим расширение которое будет использовать функцию(алгоритм, формулу) из расширения.
Визуальная схема проекта будет выглядеть следующим образом.
Первым этапом создадим класс интерфейсов наследуемый от Qobject, в качестве интерфейса будет метод который принимает переменную типа QString и возвращает эту же строку в верхнем регистре. С помощью макроса Q_DECLARE_INTERFACE, задаём идентификотор интерфейсов, компилятор с генерирует метаинформацию для строки-идентификатор. Данный модуль является протоколом общения между плагином и основной программой и будет использоваться в проекте плагина и в основном проекте.
Класс будет выглядеть следующем образом.
//---------------------------------------------------
#ifndef INTERFACE_H
#define INTERFACE_H
//-------------------------------------------------------
#include <QObject>
//-------------------------------------------------------
class interface : public QObject
{
public:
/// brief виртуальный деструктор
virtual ~interface() = default;
/// brief Интерфейс расширения
virtual QString getUpString(QString str) = 0;
};
//----------------------------------------------------------------
Q_DECLARE_INTERFACE(interface, "com.mysoft.Application.interface")
//----------------------------------------------------------------
#endif // INTERFACE_H
//----------------------------------------------------------------
Создадим базовое приложение которое будет загружать расширение. По нажатию кнопку будет происходить поиск расширения и загрузка его в систему. Далее через интерфейс будем задействовать нашу функцию.
Базовое приложение:
mainproject.h
//---------------------------------------------------
#ifndef MAINPROJECT_H
#define MAINPROJECT_H
//-------------------------------------------------------
#include <QWidget>
#include <QPluginLoader>
#include <QDir>
#include "interface.h"
//-------------------------------------------------------
namespace Ui {
class mainProject;
}
//-------------------------------------------------------
class mainProject : public QWidget
{
Q_OBJECT
public:
/// brief конструктор
explicit mainProject(QWidget *parent = nullptr);
/// brief деструктор
~mainProject();
private slots:
/// brief Поиск плагина
void on_searchPlugin_clicked();
/// brief Использования интерфейса
void on_getUp_clicked();
private:
Ui::mainProject *ui;
interface *pluginObject; ///< Указатель на объект плагина
};
//-------------------------------------------------------
#endif // MAINPROJECT_H
//-------------------------------------------------------
mainproject.cpp
//---------------------------------------------------
#include "mainproject.h"
#include "ui_mainproject.h"
//-------------------------------------------------------
mainProject::mainProject(QWidget *parent) :
QWidget(parent),
ui(new Ui::mainProject)
{
ui->setupUi(this);
}
//-------------------------------------------------------
mainProject::~mainProject()
{
delete ui;
}
//-------------------------------------------------------
void mainProject::on_searchPlugin_clicked()
{
QStringList listFiles;
QDir dir(QApplication::applicationDirPath() + "/Plugins/");
// Поиск всех файлов в папке "Plugins"
if(dir.exists())
listFiles = dir.entryList(QStringList("*"), QDir::Files);
// Проход по всем файлам
for(QString str: listFiles)
{
QPluginLoader loader(dir.absolutePath() + "/" +str);
QObject *pobj = 0;
// Загрузка плагина
pobj = qobject_cast<QObject*>(loader.instance());
if(!pobj)
continue;
pluginObject = 0;
// Получения интерфейсов
pluginObject = qobject_cast<interface *>(pobj);
// Проверка тот ли загружен плагин
if(pluginObject)
{
ui->label->setText("Расширение найдено");
break;
}
}
}
//-------------------------------------------------------
void mainProject::on_getUp_clicked()
{
QString tmp;
tmp = ui->lineEdit->text();
// использование интерфейса getUpString()
tmp = pluginObject->getUpString(tmp);
ui->label_2->setText(tmp);
}
//-------------------------------------------------------
Создание расширения, первое что нужно сделать это в pro файле изменить типа собираемого проекта, для этого нужно добавить следующую строку TEMPLATE = lib, и задать конфигурацию проекта под расширения CONFIG += plugin.
upperstringplugin.pro
#-------------------------------------------------
#
# Project created by QtCreator 2019-04-03T11:35:18
#
#-------------------------------------------------
QT += core
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = upperStringPlugin
TEMPLATE = lib
CONFIG += plugin
DESTDIR = ../Plugins
DEFINES += QT_DEPRECATED_WARNINGS
CONFIG += c++11
SOURCES +=
upperstringplugin.cpp
HEADERS +=
upperstringplugin.h
interface.h
Далее создаём класс будущего расширения, класс должен быть унаследован от класса интерфейсов. Макрос Q_INTERFACES, нужен что бы компилятор с генерировал всю необходимую мета информацию для расширения. Макрос Q_PLUGIN_METADATA(), задаёт точку входа в расширение и доступ для библиотеки Qt. Также нужно создать файл inteface.json с метаинформацией(файл должен находиться в корне проекта), в нашем случае там нет информации поэтому просто запишем пустые кавычки {} в файл.
upperstringplugin.h
//---------------------------------------------------
#ifndef UPPERSTRINGPLUGIN_H
#define UPPERSTRINGPLUGIN_H
//---------------------------------------------------
#include "interface.h"
//---------------------------------------------------
class upperStringPlugin : public interface
{
Q_OBJECT
Q_INTERFACES(interface)
Q_PLUGIN_METADATA(IID "com.mysoft.Application.interface" FILE "interface.json")
public:
explicit upperStringPlugin();
~upperStringPlugin();
// interface interface
public:
QString getUpString(QString str);
};
//---------------------------------------------------
#endif // UPPERSTRINGPLUGIN_H
//---------------------------------------------------
upperstringplugin.cpp
//---------------------------------------------------
#include "upperstringplugin.h"
//---------------------------------------------------
upperStringPlugin::upperStringPlugin()
{}
//---------------------------------------------------
upperStringPlugin::~upperStringPlugin()
{}
//---------------------------------------------------
QString upperStringPlugin::getUpString(QString str)
{
return str.toUpper();
}
//---------------------------------------------------
На выходе компиляции проекта мы получим файл с расширением .so, данный файл перемещаем в папку «Plugins» главного проекта и запускаем его. В данном случае расширение загружается в основную программу и создаётся единственный объект расширения. При попытки заново использовать функцию instance(), функция вернёт указатель на уже созданный объект расширения.
Выполнение программы
Усложним нашу задачу, теперь нам нужно что бы расширение представляло собой виджет и что бы была возможность создавать несколько таких виджетов. Основная программа будет принимать сообщения от плагинов и отправлять обратно ответ. Создадим новые проекты, на первом этапе нам понадобиться два класса интерфейсов, один будет отвечать за загрузку расширения и создания виджета, другой за работу самого виджета.
Схема проекта будет выглядеть следующим образом:
Первый класс interface будет иметь две функции, получить названия плагина и получить виджет плагина. Название плагина будем хранить для индентификации его в системе. Виджет плагина мы будем добавлять в MDI окна основного приложения.
Второй класс является самим графическим виджетом, он унаследован от Qwidget, здесь мы прописали необходимые нам функции, виджет будет получать сообщение и отправлять в основную программу.
interface.h
//-------------------------------------------------------------------------
#ifndef INTERFACE_H
#define INTERFACE_H
//-------------------------------------------------------------------------
#include <QWidget>
class QString;
//-------------------------------------------------------------------------
class interface : public QObject
{
public:
/// brief Деструктор
virtual ~interface(){}
/// brief Получить название плагина
virtual QString getNamePlugin() = 0;
/// brief Получить новый виджет
virtual QObject *getPluginWidget() = 0;
};
//-------------------------------------------------------------------------
class interfaceWidget: public QWidget
{
public:
/// brief Деструктор
virtual ~interfaceWidget() = default;
signals:
/// brief Сигнал отправляет текст из расширения
virtual void signal_writeText(QString str) = 0;
public slots:
/// brief Слот получает текст в плагин
virtual void slot_getText(QString str) = 0;
};
//-------------------------------------------------------------------------
Q_DECLARE_INTERFACE(interface, "com.mysoft.Application.interface")
//-------------------------------------------------------------------------
#endif // INTERFACE_H
//-------------------------------------------------------------------------
Основная программа состоит из MDI окна, в котором есть основной виджет для принятия сообщений от плагинов и дополнительные окна, которые динамически появляются по мере вызова плагинов.
При создания виджета плагина мы соединяем сигнал от плагина со слотом и с помощью функции sender() мы получаем указатель на плагин который прислал сообщение. Созданный виджет мы помещаем в MDI окно, а сам объект плагина можно выгрузить из системы.
mainproject.h
//------------------------------------------------
#ifndef MAINPROJECT_H
#define MAINPROJECT_H
//------------------------------------------------
#include <QMainWindow>
#include <QDir>
#include <QPluginLoader>
#include "interface.h"
//------------------------------------------------
namespace Ui {
class mainProject;
}
//------------------------------------------------
typedef struct str_plugin
{
QString namePlugin; ///< Имя плагина
QString dirPlugin; ///< Расположение плагина
}TSTR_PLUGIN;
//------------------------------------------------
class mainWidget;
//------------------------------------------------
class mainProject : public QMainWindow
{
Q_OBJECT
public:
explicit mainProject(QWidget *parent = nullptr);
~mainProject();
private slots:
void on_action_triggered();
/// brief Слот Запуска плагина
void slot_showPlugin();
/// brief Функция получения текста от плагина и отправляет ему ответ
void slot_getTextFromPlugin(QString str);
private:
Ui::mainProject *ui;
mainWidget *widget; ///< Основное окно
QVector<TSTR_PLUGIN > vecPlugin; ///< Вектор плагинов
};
//------------------------------------------------
#endif // MAINPROJECT_H
//------------------------------------------------
mainproject.cpp
//------------------------------------------------
#include "mainproject.h"
#include "ui_mainproject.h"
#include "mainwidget.h"
#include <QMdiSubWindow>
//------------------------------------------------
mainProject::mainProject(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::mainProject)
{
ui->setupUi(this);
QMdiSubWindow *sWPS = new QMdiSubWindow;
widget = new mainWidget();
sWPS->setWidget(widget);
ui->mdiArea->addSubWindow(sWPS);
}
//------------------------------------------------
mainProject::~mainProject()
{
delete ui;
}
//------------------------------------------------
void mainProject::on_action_triggered()
{
ui->menu_2->clear();
QStringList listFiles;
QDir dir(QApplication::applicationDirPath() + "/Plugins/");
if(dir.exists())
{
listFiles = dir.entryList(QStringList("*"), QDir::Files);
}
for(QString str: listFiles)
{
QPluginLoader loader(dir.absolutePath() + "/" +str);
QObject *pobj = 0;
pobj = qobject_cast<QObject*>(loader.instance());
if(!pobj)
continue;
interface *plW = 0;
plW = qobject_cast<interface *>(pobj);
if(!plW)
continue;
QString namePlugin = plW->getNamePlugin();
QAction *action = new QAction(namePlugin);
ui->menu_2->addAction(action);
connect(action, SIGNAL(triggered()), this, SLOT(slot_showPlugin()));
TSTR_PLUGIN plug;
plug.namePlugin = namePlugin;
plug.dirPlugin = dir.absolutePath() + "/" +str;
vecPlugin.push_back(plug);
delete plW;
}
}
//------------------------------------------------
void mainProject::slot_showPlugin()
{
QObject *pobj = sender();
QAction *action = qobject_cast<QAction *>(pobj);
QString namePlugin = action->iconText();
for(int i = 0; i < vecPlugin.size(); i++)
{
if(namePlugin == vecPlugin[i].namePlugin)
{
QMdiSubWindow *sWPS = new QMdiSubWindow;
ui->mdiArea->addSubWindow(sWPS);
sWPS->setAttribute(Qt::WA_DeleteOnClose, true);
QPluginLoader loader(vecPlugin[i].dirPlugin);
QObject *pobj = qobject_cast<QObject*>(loader.instance());
if(!pobj)
continue;
interface *plW = qobject_cast<interface *>(pobj);
if(!plW)
continue;
QObject *ob = plW->getPluginWidget();
if(!ob)
continue;
interfaceWidget *interFaceW = dynamic_cast<interfaceWidget *>(ob);
if(!interFaceW)
continue;
sWPS->setWidget(interFaceW);
sWPS->show();
QSize size = interFaceW->minimumSize();
size.setHeight(size.height() + 20);
size.setWidth(size.width() + 20);
sWPS->resize(size);
loader.unload();
connect(interFaceW, SIGNAL(signal_writeText(QString)), this, SLOT(slot_getTextFromPlugin(QString)));
}
}
}
//------------------------------------------------
void mainProject::slot_getTextFromPlugin(QString str)
{
//Получение указателя на отправителя сообщения
QObject *pobj = sender();
interfaceWidget *pPlug = dynamic_cast<interfaceWidget *>(pobj);
widget->slot_getText("Получено сообщение от плагина");
widget→slot_getText(str);
widget->slot_getText("Отправлен ответ");
widget→slot_getText("------------------------------");
pPlug->slot_getText("Сообщение доставлено");
}
//------------------------------------------------
Основное окно, принимает сообщение и отображает его.
mainwidget.h
//----------------------------------------------------------
#ifndef MAINWIDGET_H
#define MAINWIDGET_H
//----------------------------------------------------------
#include <QWidget>
//----------------------------------------------------------
namespace Ui {
class mainWidget;
}
//----------------------------------------------------------
class mainWidget : public QWidget
{
Q_OBJECT
public:
explicit mainWidget(QWidget *parent = nullptr);
~mainWidget();
public slots:
/// brief Слот принимает сообщения от плагинов
void slot_getText(QString str);
private:
Ui::mainWidget *ui;
};
//----------------------------------------------------------
#endif // MAINWIDGET_H
//----------------------------------------------------------
mainwidget.cpp
//----------------------------------------------------------
#include "mainwidget.h"
#include "ui_mainwidget.h"
//----------------------------------------------------------
mainWidget::mainWidget(QWidget *parent) :
QWidget(parent),
ui(new Ui::mainWidget)
{
ui->setupUi(this);
}
//----------------------------------------------------------
mainWidget::~mainWidget()
{
delete ui;
}
//----------------------------------------------------------
void mainWidget::slot_getText(QString str)
{
ui->textEdit->append(str);
}
//----------------------------------------------------------
Создаём плагин, его идея состоит в том что он является фабрикой для создания виджета.
plugin.h
//-------------------------------------------------
#ifndef PLUGIN_H
#define PLUGIN_H
//-------------------------------------------------
#include "interface.h"
#include "texttranferwidget.h"
//-------------------------------------------------
class plugin : public interface
{
Q_OBJECT
Q_INTERFACES(interface)
Q_PLUGIN_METADATA(IID "com.mysoft.Application.interface" FILE "interface.json")
public:
explicit plugin();
~plugin();
// interface interface
public:
/// brief Получить название плагина
QString getNamePlugin();
/// brief Получить новый виджет
QObject *getPluginWidget();
};
//-------------------------------------------------
#endif // PLUGIN_H
//-------------------------------------------------
plugin.cpp
//-------------------------------------------------
#include "plugin.h"
//-------------------------------------------------
plugin::plugin()
{
}
//-------------------------------------------------
plugin::~plugin()
{
}
//-------------------------------------------------
QString plugin::getNamePlugin()
{
return "Тестовый плагин1";
}
//-------------------------------------------------
QObject *plugin::getPluginWidget()
{
textTranferWidget *widget = new textTranferWidget();
return qobject_cast<QObject *>(widget);
}
//-------------------------------------------------
Виджет создаваемый плагином.
texttranferwidget.h
//-------------------------------------------------------------------
#ifndef TEXTTRANFERWIDGET_H
#define TEXTTRANFERWIDGET_H
//-------------------------------------------------------------------
#include "interface.h"
//-------------------------------------------------------------------
namespace Ui {
class textTranferWidget;
}
//-------------------------------------------------------------------
class textTranferWidget : public interfaceWidget
{
Q_OBJECT
public:
/// brief Конструктор
explicit textTranferWidget();
/// brief Деструктор
~textTranferWidget();
private:
Ui::textTranferWidget *ui;
// interfaceWidget interface
signals:
/// brief Сигнал отправляет текст из расширения
void signal_writeText(QString str);
public slots:
/// brief Слот получает текст в плагин
void slot_getText(QString str);
private slots:
void on_pushButton_clicked();
};
//-------------------------------------------------------------------
#endif // TEXTTRANFERWIDGET_H
//-------------------------------------------------------------------
texttranferwidget.cpp
//-------------------------------------------------------------------
#include "texttranferwidget.h"
#include "ui_texttranferwidget.h"
//-------------------------------------------------------------------
textTranferWidget::textTranferWidget() :
ui(new Ui::textTranferWidget)
{
ui->setupUi(this);
}
//-------------------------------------------------------------------
textTranferWidget::~textTranferWidget()
{
delete ui;
}
//-------------------------------------------------------------------
void textTranferWidget::slot_getText(QString str)
{
ui->textEdit->append(str);
}
//-------------------------------------------------------------------
void textTranferWidget::on_pushButton_clicked()
{
emit signal_writeText(ui->lineEdit->text());
}
//-------------------------------------------------------------------
Вывод основной программы:
Автор: CIUK
Источник [1]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/qt-2/315231
Ссылки в тексте:
[1] Источник: https://habr.com/ru/post/448806/?utm_campaign=448806
Нажмите здесь для печати.