Erstellen eines Erweiterungssystems in der Qt-Bibliothek

Plugins (Erweiterungen)


Extensions ist eine gemeinsam genutzte dynamische Bibliothek, die während der Ausführung der Hauptanwendung geladen werden soll und die mindestens eine spezielle Schnittstelle implementieren muss.

Erweiterungen werden in zwei Typen unterteilt:

  • Für Qt
  • Für native Anwendungen

Lassen Sie uns herausfinden, wie Sie Ihr eigenes Erweiterungssystem und die Erweiterungen selbst dafür erstellen.

Die Kommunikation mit der Nebenstelle erfolgt über die Schnittstelle (Signale, Slots und Klassenmethoden). Die Erweiterung wird von der Anwendung mithilfe der QPluginLoader- Klasse geladen. Zum Laden der Erweiterung wird die Methode instance () verwendet, die ein Erweiterungsobjekt erstellt und einen Zeiger darauf zurückgibt. Die unload () -Methode wird zum Entladen der Erweiterung verwendet.

Teil 1


Erstellen Sie im ersten Beispiel eine Erweiterung, die eine Funktion (Algorithmus, Formel) aus der Erweiterung verwendet.

Das visuelle Schema des Projekts sieht wie folgt aus.



Stufe 1:


Der erste Schritt besteht darin, eine von QObject geerbte Schnittstellenklasse zu erstellen. Als Schnittstelle gibt es eine Methode, die eine Variable vom Typ QString verwendet und dieselbe Zeichenfolge in Großbuchstaben zurückgibt. Mit dem Makro Q_DECLARE_INTERFACE setzen wir den Bezeichner der Schnittstellen, der Compiler c generiert Metainformationen für den Bezeichnerstring. Dieses Modul ist das Kommunikationsprotokoll zwischen dem Plug-In und dem Hauptprogramm und wird im Plug-In-Projekt und im Hauptprojekt verwendet.

Die Klasse sieht wie folgt aus.

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


Stufe 2:


Lassen Sie uns eine Basisanwendung erstellen, die die Erweiterung herunterlädt. Durch Drücken der Taste wird die Nebenstelle durchsucht und in das System geladen. Weiter über die Schnittstelle werden wir unsere Funktion nutzen.

Basisanwendung:

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


Stufe 3:


Wenn Sie eine Erweiterung erstellen, müssen Sie zunächst den Typ des Projekts ändern, das in der Pro-Datei erstellt wird. Dazu müssen Sie die folgende Zeile TEMPLATE = lib hinzufügen und die Projektkonfiguration für die Plugin-Erweiterung CONFIG + = festlegen.

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 

Als nächstes erstellen wir eine Klasse für die zukünftige Erweiterung. Die Klasse sollte von der Klasse der Schnittstellen geerbt werden. Makro Q_INTERFACES , Sie benötigen den Compiler, um alle erforderlichen Metainformationen für die Erweiterung zu generieren. Das Makro Q_PLUGIN_METADATA () legt den Einstiegspunkt für die Erweiterung und den Zugriff für die Qt-Bibliothek fest. Sie müssen auch eine inteface.json-Datei mit Metainformationen erstellen (die Datei muss sich im Stammverzeichnis des Projekts befinden). In unserem Fall gibt es dort keine Informationen. Schreiben Sie also einfach leere Anführungszeichen {} in die Datei.

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

Bei der Ausgabe des Kompilierens des Projekts erhalten wir eine Datei mit der Erweiterung .so, verschieben diese Datei in den Plugins-Ordner des Hauptprojekts und starten sie. In diesem Fall wird die Erweiterung in das Hauptprogramm geladen und ein einzelnes Erweiterungsobjekt erstellt. Wenn Sie versuchen, die instance () - Funktion wiederzuverwenden, gibt die Funktion einen Zeiger auf das bereits erstellte Erweiterungsobjekt zurück.

Programmausführung



Teil 2


Um unsere Aufgabe zu verkomplizieren, benötigen wir jetzt die Erweiterung als Widget und die Möglichkeit, mehrere solcher Widgets zu erstellen. Das Hauptprogramm empfängt Nachrichten von Plugins und sendet eine Antwort zurück. Wir werden neue Projekte erstellen. In der ersten Phase benötigen wir zwei Klassen von Schnittstellen. Eine ist für das Laden der Erweiterung und das Erstellen des Widgets verantwortlich, die andere für den Betrieb des Widgets selbst.

Das Projektschema sieht wie folgt aus:



Stufe 1:


Die erste Schnittstellenklasse hat zwei Funktionen: Rufen Sie den Namen des Plugins und das Plugin-Widget ab. Der Name des Plugins wird zur Identifizierung im System gespeichert. Wir werden das Plugin-Widget zu den MDI-Fenstern der Hauptanwendung hinzufügen.

Die zweite Klasse ist das grafische Widget selbst. Es wird von QWidget geerbt. Hier haben wir die Funktionen angegeben, die wir benötigen. Das Widget empfängt eine Nachricht und sendet sie an das Hauptprogramm.

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

Stufe 2:


Das Hauptprogramm besteht aus einem MDI-Fenster, in dem es ein Haupt-Widget zum Empfangen von Nachrichten von Plugins und zusätzliche Fenster gibt, die dynamisch angezeigt werden, wenn die Plugins aufgerufen werden.

Beim Erstellen eines Plugin-Widgets verbinden wir das Signal vom Plugin mit dem Steckplatz und erhalten mit der Funktion sender () einen Zeiger auf das Plugin, das die Nachricht gesendet hat. Wir platzieren das erstellte Widget im MDI-Fenster, und das Plug-In-Objekt selbst kann vom System entladen werden.

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

Das Hauptfenster akzeptiert die Nachricht und zeigt sie an.

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

Stufe 2:


Wir erstellen ein Plugin. Die Idee ist, dass es eine Fabrik zum Erstellen eines Widgets ist.

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

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

Die Ausgabe des Hauptprogramms:

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


All Articles