Qt Plugin plug-in development

1. Qt plug-in mechanism

.1 Introduction to Qt Plugins

A plug-in is a program written in accordance with a certain standard application program interface, and is positioned to develop a program that implements functions that the application software platform does not have. The plug-in and the host program are connected through an interface, just like a hardware plug-in card, which can be deleted, inserted and modified at any time, so the structure is very flexible, easy to modify, and convenient for software upgrades and maintenance. Qt provides two APIs for creating plug-ins: one is high-level API, which is used to extend the functions of Qt itself, such as custom database driver, image format, text encoding, custom style, etc.; one is low-level API, Used to extend Qt applications. This article mainly creates Qt plug-ins through low-level APIs, and calls the plug-ins in static and dynamic ways. (The following are all Qt5 plug-in development methods)

1.2 The process of Qt plug-in development

  1. Define a set of interfaces (classes with only pure virtual functions).
  2. Use the macro Q_DECLARE_INTERFACE() to tell the Qt meta-object system about the interface
  3. Declare the plug-in class, which inherits from QObject and the interface implemented by the plug-in
  4. The plugin interface is told to the Qt meta-object system (in the header file) with the macro Q_INTERFACES().
  5. Build the plugin with the appropriate .pro file.

1.3 The process of Qt plug-in call

  1. Include interface headers (classes with only pure virtual functions).
  2. The application uses QPluginLoader to load plugins.
  3. Use the macro qobject_cast() to determine whether a plug-in implements the interface.

2. Example of plug-in development

2.1 Create directory project

Create a directory project to place the GUI application project and plug-in project, select "Other Project" -> "Subdirs Project", fill in the project name as PluginApp, and select the save directory.
insert image description here

2.2 Create GUI application project

Right-click on the PluginApp project and select the "New Subproject" menu item, and select to create a GUI application:
insert image description here
fill in the project application name as MainWindow:
insert image description here
fill in the name of the main interface class:
insert image description here

2.3 Create a plug-in subproject

Right-click on the PluginApp project and select the “New Subproject” menu item, and choose to create an empty Qt project named EchoPlugin.
insert image description here

2.4 Implementation of plug-ins

1. Define an interface set (a class with only pure virtual functions)
and add an interface Echonterface.h in the MainWindow application.

#ifndef ECHOINTERFACE_H
#define ECHOINTERFACE_H

#include <QString>

// 1.定义一个接口集(只有纯虚函数的类)
class EchoInterface
{
    
    
public:
    virtual ~EchoInterface() {
    
    }
    virtual QString echo(const QString &message) = 0;
};

// 2.用宏Q_DECLARE_INTERFACE()将该接口告诉Qt元对象系统
QT_BEGIN_NAMESPACE
#define EchoInterface_iid "org.qt-project.Qt.Examples.EchoInterface"
Q_DECLARE_INTERFACE(EchoInterface, EchoInterface_iid)
QT_END_NAMESPACE
#endif

2. Declare the plug-in class, which inherits from QObject and the interface implemented by the plug-in.
The EchoPlugin.pro project file content is as follows:

TEMPLATE        = lib
CONFIG         += plugin
QT             += widgets
INCLUDEPATH    += ../MainWindow
HEADERS         = EchoPlugin.h
SOURCES         = EchoPlugin.cpp
TARGET          = $$qtLibraryTarget(echoplugin)
DESTDIR         = ../EchoPlugin

# install
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/echoplugin/plugins
INSTALLS += target

CONFIG += install_ok  # Do not cargo-cult this!

Add a plugin class EchoPlugin in the plugin subproject, as follows:

EchoPlugin.h file:

#ifndef ECHOPLUGIN_H
#define ECHOPLUGIN_H

#include <QObject>
#include <QtPlugin>
#include "EchoInterface.h"

// 3.声明插件类,插件类继承自QObject和插件实现的接口
class EchoPlugin : public QObject, EchoInterface
{
    
    
    // 3.用宏Q_INTERFACES()将插件接口告诉Qt元对象系统(在头文件中)
    Q_OBJECT
    Q_PLUGIN_METADATA(IID "org.qt-project.Qt.Examples.EchoInterface") // 宏需要声明通过对象实现的接口的IID,并引用一个包含插件元数据的文件
    Q_INTERFACES(EchoInterface)

public:
    QString echo(const QString &message) override; // 实现的接口:返回字符串消息
};

#endif

EchoPlugin.cpp file:

#include "EchoPlugin.h"

// 实现的接口:返回字符串消息
QString EchoPlugin::echo(const QString &message)
{
    
    
    return message;
}

2.5 Realization of GUI application

Realize the main interface of MainWindow.

MainWindow.pro file:

QT += widgets

HEADERS    = Widget.h \
             EchoInterface.h
SOURCES    = Widget.cpp \
             main.cpp

TARGET     = echoplugin
QMAKE_PROJECT_NAME = MainWindow
win32 {
    
    
    CONFIG(debug, release|debug):DESTDIR = ../debug/
    CONFIG(release, release|debug):DESTDIR = ../release/
} else {
    
    
    DESTDIR    = ../
}

# install
target.path = $$[QT_INSTALL_EXAMPLES]/widgets/tools/echoplugin
INSTALLS += target

CONFIG += install_ok  # Do not cargo-cult this!

Widget.h file:

#ifndef ECHODIALOG_H
#define ECHODIALOG_H

#include <QApplication>
#include <QWidget>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QGridLayout>
#include <QMessageBox>
#include <QDir>
#include <QPluginLoader>
#include "EchoInterface.h"

class Widget : public QWidget
{
    
    
    Q_OBJECT

public:
    Widget();

private slots:
    void sendEcho();

private:
    void initUI(); // 初始化UI
    bool loadPlugin(); // 加载插件

    EchoInterface *m_pEchoInterface;
    QLineEdit *m_pLineEdit;
    QLabel *m_pLabel;
    QPushButton *m_pBtn;
};

#endif

Widget.cpp file:

#include "Widget.h"

Widget::Widget()
{
    
    
    // 初始化UI
    initUI();

    // 加载插件
    if (!loadPlugin()) {
    
    
        QMessageBox::information(this, "Error", "Could not load the plugin");
        m_pLineEdit->setEnabled(false);
        m_pBtn->setEnabled(false);
    }
}

void Widget::sendEcho()
{
    
    
    // 调用插件接口 - EchoPlugin::echo
    QString text = m_pEchoInterface->echo(m_pLineEdit->text());
    m_pLabel->setText(text);
}

// 初始化UI
void Widget::initUI()
{
    
    
    m_pLineEdit = new QLineEdit;
    m_pLabel = new QLabel;
    m_pLabel->setFrameStyle(QFrame::Box | QFrame::Plain);
    m_pBtn = new QPushButton(tr("Send Message"));

    connect(m_pLineEdit, &QLineEdit::editingFinished,
            this, &Widget::sendEcho);
    connect(m_pBtn, &QPushButton::clicked,
            this, &Widget::sendEcho);

    QGridLayout *m_pLayoutMain = new QGridLayout(this);
    m_pLayoutMain->addWidget(new QLabel(tr("Message:")), 0, 0);
    m_pLayoutMain->addWidget(m_pLineEdit, 0, 1);
    m_pLayoutMain->addWidget(new QLabel(tr("Answer:")), 1, 0);
    m_pLayoutMain->addWidget(m_pLabel, 1, 1);
    m_pLayoutMain->addWidget(m_pBtn, 2, 1, Qt::AlignRight);
    m_pLayoutMain->setSizeConstraint(QLayout::SetFixedSize);
}

// 加载插件
bool Widget::loadPlugin()
{
    
    
    bool ret = true;

    // 获取当前应用程序所在路径
    QDir pluginsDir(qApp->applicationDirPath());
    if (pluginsDir.dirName().toLower() == "debug" || pluginsDir.dirName().toLower() == "release")
        pluginsDir.cdUp();

    // 切换到插件目录
    pluginsDir.cd("plugins");
    // 遍历plugins目录下所有文件
    foreach (QString fileName, pluginsDir.entryList(QDir::Files))
    {
    
    
        QPluginLoader pluginLoader(pluginsDir.absoluteFilePath(fileName));
        QObject *plugin = pluginLoader.instance();
        if (plugin)
        {
    
    
            // 获取插件名称
            QString pluginName = plugin->metaObject()->className();
            if(pluginName == "EchoPlugin")
            {
    
    
                // 对插件初始化
                m_pEchoInterface = qobject_cast<EchoInterface *>(plugin);
                if (m_pEchoInterface)
                    ret =  true;
                break;
            }
            else
            {
    
    
                ret = false;
            }
        }
    }
    return ret;
}

Main.cpp file:

#include <QtWidgets>
#include "Widget.h"
#include "EchoInterface.h"

int main(int argv, char *args[])
{
    
    
    QApplication app(argv, args);

    Widget window;
    window.show();

    return app.exec();
}

2.6 Program running results

insert image description here

3. Positioning plug-in

Qt applications will automatically be aware of available plugins, since plugins are stored in standard subdirectories. So the application doesn't need any code to find or load plugins.
During the development process, the directory of the plug-in is QTDIR/plugins (QTDIR is the installation directory of Qt), and each type of plug-in is placed under the corresponding type of directory. If you want the application to use plug-ins, but do not want to use the standard plug-in storage path, you can specify the path of the plug-in to be used during the installation process of the application. You can use QSettings to save the plug-in path and read the configuration file when the application is running. . The application can load the specified plugin path into the application through the QCoreApplication::addLibraryPath() function.
One way to make plugins loadable is to create a subdirectory in the application directory where the plugins will reside. If you want to distribute any plugins in the plugins (stored in the plugins directory) distributed with Qt, you must copy the plugins subdirectory in the plugins directory to the root directory of the application.

Guess you like

Origin blog.csdn.net/houxian1103/article/details/130536848