1, an overview of the

After the first two articles, we are now ready to write a QT plugin from zero and have our own simple plugin framework. However, the example only gives the loading and invocation of A single plug-in. The purpose of plug-in implementation is for extensibility. There will be plug-in A, plug-in B and plug-in C in the actual project. How do these plug-ins communicate with each other? Or how to build a good communication structure, which is not only an essential work, but also an important milestone in improving our plug-in framework.

As we implement communication mechanisms, consider the following points (not just here, but at all times) :

1. Low coupling. 2. Code less. 3. Strong expansibility. 4. Encapsulation, inheritance, polymorphism.

We’re trying to be object-oriented, and in object-oriented, we’re programming to interfaces, not implementations.

Communication includes the communication between the plug-in and the main program, as well as between plug-ins. First of all, the contents of the main program cannot be obtained in the plug-in (think how can the keyboard know the things of the Windows kernel? They are the master and slave devices, that is, the plug-in manager and the plug-in are also the master and slave devices). Secondly, plug-in A is not aware of the existence of plug-in B (the keyboard is not aware of the existence of the mouse), that is, zero coupling between plug-ins without dependencies.

The following figure shows the communication mechanism of our entire plug-in:

The PluginManager class is a simple example of the PluginManager class. The PluginManager class is a simple example of the PluginManager class. The PluginManager class is a simple example of the PluginManager class.

2, the instance,

Based on the example of the plug-in manager, we added A simulation scenario. There is A button on the main interface, click to open the interface of plug-in A, and there is A button on plug-in A that can be clicked to call the animation method of plug-in B.

# pluginMetaData.h #pragma once #include <QObject> #include <QVariant> #include <QMap> #include <QString> /** * **/ struct PluginMetaData {QString; QString dest; int type; QMap<QString,QVariant> map; QObject *object = nullptr; }; Q_DECLARE_METATYPE(PluginMetaData);Copy the code

First, we need to encapsulate a standard communication packet to unify the method parameters.

#pragma once #include <QString> #include <QtPlugin> #include <QObject> #include "pluginmetadata. h InterfacePlugin { public: virtual ~InterfacePlugin() {} virtual void recMsgfromManager(PluginMetaData) = 0; virtual void sendMsg2Manager(PluginMetaData) = 0; }; #define InterfacePlugin_iid "Test.Plugin.InterfacePlugin" Q_DECLARE_INTERFACE(InterfacePlugin, InterfacePlugin_iid)Copy the code

The interface has been improved with the addition of two virtual functions, one to receive messages from the Manager and one to send messages to the Manager for forwarding.

# qtpluginmanager.cpp void QtPluginsManager::initSignalAndSlot() { auto plugins = allPlugins(); Foreach (auto Loader, plugins) {// Each plugin sends a message to the Manager, It is then forwarded by the Manager based on the Dest field connect(loader->instance(),SIGNAL(sendMsg2Manager(PluginMetaData)),this,SLOT(recMsgfromPlugin(PluginMetaData))); } } void QtPluginsManager::recMsgfromPlugin(PluginMetaData metadata) { auto loader = getPlugin(metadata.dest); if(loader) { auto interface = qobject_cast<InterfacePlugin*>(loader->instance());; if(interface) { interface->recMsgfromManager(metadata); }}}Copy the code

The PluginManager class also adds two methods. One is to initialize a signal slot to communicate with plug-ins after all plug-ins have been loaded, and the other is to forward messages received from plug-ins to the corresponding plug-ins based on the Dest field.

# widget.cpp #include "widget.h" #include "ui_widget.h" #include "qtpluginmanager.h" #include "interfaceplugin.h" Widget::Widget(QWidget *parent) : QWidget(parent), ui(new Ui::Widget) { ui->setupUi(this); connect(ui->pushButtonA,SIGNAL(clicked()),this,SLOT(clickA())); } Widget::~Widget() { delete ui; } void Widget::clickA() { QPluginLoader *loader = QtPluginsManager::getInstance().getPlugin("pluginA"); if(loader) { auto obj = loader->instance(); if(obj->isWidgetType()) { QWidget *widget = qobject_cast<QWidget*>(obj); widget->show(); }} else {qDebug() << "Failed to find plug-in A"; }}Copy the code

CPP file on the home screen.

# pluginA.h #pragma once #include "ui_pluginA.h" #include <QtPlugin> #include ".. /TTTT/interfaceplugin.h" namespace Ui { class PluginA; } class PluginA :public QWidget, Public InterfacePlugin {// Programmer. Json Q_OBJECT Q_PLUGIN_METADATA(IID InterfacePlugin_iid FILE "programmer.json") Q_INTERFACES(InterfacePlugin) public: explicit PluginA(QWidget *parent = nullptr); virtual void recMsgfromManager(PluginMetaData) Q_DECL_OVERRIDE; public slots: void clickOpenImg(); void click2Animation(); Void sendMsg2Manager(PluginMetaData) Q_DECL_OVERRIDE; void sendMsg2Manager(PluginMetaData) Q_DECL_OVERRIDE; private: Ui::PluginA *ui; };Copy the code

Plug-in A implements two virtual functions for interface, one as A slot function and one as A signal (every plug-in needs to do this, so it is defined as virtual).

# pluginA.cpp #include "pluginA.h" #include <QtDebug> #include <QFileDialog> #include <QPixmap> PluginA::PluginA(QWidget  *parent) : QWidget(parent), ui(new Ui::PluginA) { ui->setupUi(this); connect(ui->pushButton,SIGNAL(clicked()),this,SLOT(clickOpenImg())); connect(ui->pushButton2,SIGNAL(clicked()),this,SLOT(click2Animation())); {} void PluginA: : clickOpenImg () QString path = QFileDialog: : getOpenFileName (this, tr (" select image "), ". ", tr("Image Files(*.jpg *.png)")); ui->label->setPixmap(QPixmap(path)); } void PluginA::click2Animation() { PluginMetaData metadata; metadata.from = "pluginA"; metadata.dest = "pluginB"; metadata.type = 1; metadata.object = ui->label; emit sendMsg2Manager(metadata); } void PluginA::recMsgfromManager(PluginMetaData metedata) { qDebug() << "recMsgfromManager : " << metedata.from; }Copy the code

PluginB is relatively simple, just executing the animation after receiving the message and parameters.

# pluginB.cpp

#include "pluginB.h"
#include <QtDebug>
#include <QPropertyAnimation>

PluginB::PluginB(QObject *parent) :
    QObject(parent)
{
}

void PluginB::recMsgfromManager(PluginMetaData metedata)
{
    qDebug() << "PluginB recMsgfromManager : ";
    qDebug() << "metedata.from:" << metedata.from;
    qDebug() << "metedata.dest:" << metedata.dest;
    qDebug() << "metedata.type:" << metedata.type;
    if(metedata.object)
    {
        animation((QWidget*)metedata.object);
        qDebug() << "metedata.object:" << metedata.object->objectName();
    }
}

void PluginB::animation(QWidget *widget)
{
    QPropertyAnimation *pScaleAnimation = new QPropertyAnimation(widget, "geometry");
    pScaleAnimation->setDuration(1000);
    pScaleAnimation->setStartValue(QRect(0, 0, 0, 0));
    pScaleAnimation->setEndValue(QRect(100, 100, 200, 200));
    pScaleAnimation->start();
}

Copy the code

The Demo address: download.csdn.net/download/u0… Github.com/qht10030778…