📜 ⬆️ ⬇️

Creating an extension system on the Qt library

Plugins (Extensions)


Extensions are a shared dynamic library intended for loading during the execution of the main application, which must necessarily implement at least one special interface.

Extensions are divided into two types:


Let us analyze how to create your own system of extensions and the extensions themselves for it.

Communication with the extension is carried out using the interface (signals, slots, and class methods). The extension is loaded by the application using the QPluginLoader class. To load an extension, use the instance () method, which creates an extension object and returns a pointer to it. To unload an extension, use the unload () method.
')

Part 1


In the first example, we will create an extension that will use the function (algorithm, formula) from the extension.

The visual scheme of the project will look as follows.



Stage 1:


The first step is to create an interface class inherited from QObject, the interface will be a method that accepts a variable of type QString and returns the same string in uppercase. Using the Q_DECLARE_INTERFACE macro, set the interface identifier, the compiler generates meta-information for the identifier string. This module is a communication protocol between the plug-in and the main program and will be used in the plug-in project and in the main project.

The class will look like this.

//--------------------------------------------------- #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 //---------------------------------------------------------------- 


Stage 2:


Create a basic application that will load the extension. By pressing the button, the extension will be searched and loaded into the system. Further, through the interface, we will use our function.

Basic application:

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); } //------------------------------------------------------- 


Stage 3:


Creating an extension, the first thing you need to do in the pro file is to change the type of project you are building, for this you need to add the following line TEMPLATE = lib, and set the project configuration for the CONFIG + = plugin extensions.

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 

Next, create a class of the future extension, the class should be inherited from the class of interfaces. Macro Q_INTERFACES , you need a compiler to generate all the necessary meta information for the extension. The macro Q_PLUGIN_METADATA () , sets the entry point to the extension and access for the Qt library. You also need to create an inteface.json file with meta-information (the file must be in the root of the project), in our case there is no information there, so just write down the empty quotes {} in the file.

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(); } //--------------------------------------------------- 

At the output of the project compilation, we will get a file with the .so extension, move this file to the “Plugins” folder of the main project and launch it. In this case, the extension is loaded into the main program and a single extension object is created. When trying to reuse the instance () function, the function will return a pointer to the already created extension object.

Execution of the program



Part 2


Let's complicate our task, now we need the extension to be a widget and the possibility to create several such widgets. The main program will receive messages from the plug-ins and send back a response. We will create new projects, at the first stage we will need two classes of interfaces, one will be responsible for loading the extension and creating the widget, the other for the work of the widget itself.

The project outline will be as follows:



Stage 1:


The first class interface will have two functions, get the names of the plug-in and get the plug-in widget. The name of the plugin will be stored for identification in the system. The plugin widget will be added to the main application window in MDI.

The second class is the graphical widget itself, it is inherited from QWidget, here we have prescribed the functions we need, the widget will receive the message and send it to the main program.

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 //------------------------------------------------------------------------- 

Stage 2:


The main program consists of an MDI window, in which there is a main widget for accepting messages from plug-ins and additional windows that dynamically appear as the plug-ins are called.

When creating a plug-in widget, we connect the signal from the plug-in to the slot and, using the sender () function, we get a pointer to the plug-in that sent the message. We place the created widget in the MDI window, and the plug-in object itself can be unloaded from the system.

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(" "); } //------------------------------------------------ 

The main window accepts the message and displays it.

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); } //---------------------------------------------------------- 

Stage 2:


We create a plugin, its idea is that it is a factory for creating a widget.

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); } //------------------------------------------------- 

Widget created by the plugin.

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()); } //------------------------------------------------------------------- 

The output of the main program:

Source: https://habr.com/ru/post/448806/


All Articles