Qt插件机制介绍及实现

$ vim ImageView.pro

修改后项目文件内容如下:

#项目模板

TEMPLATE = app

#生成目标

TARGET = ImageView

INCLUDEPATH += .

#向qmake声明应用程序依赖widgets模块

greaterThan(QT_MAJOR_VERSION,4): QT += widgets

#引入头文件

HEADERS += mainwindow.h

SOURCES += main.cpp mainwindow.cpp

mainwindow.cpp

#ifndef MAINWINDOW_H

#define MAINWINDOW_H

#include

#include

#include

#include

#include

#include

#include

//创建新的窗口类

class MainWindow : public QMainWindow

{

Q_OBJECT

public:

explicit MainWindow(QWidget *parent=nullptr);

~MainWindow();

private:

void initUI();

void createActions();

void showImage(QString);

private slots:

void openImage();

private:

QMenu *fileMenu;

QMenu *editMenu;

QToolBar *fileToolBar;

QToolBar *editToolBar;

QGraphicsScene *imageScene;

QGraphicsView *imageView;

QAction *openAction;

QString currentImagePath;

QGraphicsPixmapItem *currentImage;

};

#endif // MAINWINDOW_H

main.cpp

#include

#include “mainwindow.h”

int main(int argc, char *argv[])

{

QApplication app(argc, argv);

MainWindow window;

window.setWindowTitle(“ImageView”);

window.show();

return app.exec();

}

mainwindow.cpp

#include

#include

#include

#include

#include

#include

#include “mainwindow.h”

MainWindow::MainWindow(QWidget *parent) :

QMainWindow(parent)

, fileMenu(nullptr)

, editMenu(nullptr)

, currentImage(nullptr)

{

initUI();

}

MainWindow::~MainWindow()

{

}

void MainWindow::initUI()

{

this->resize(800, 600);

// 创建菜单

fileMenu = menuBar()->addMenu(“&File”);

editMenu = menuBar()->addMenu(“&Edit”);

// 创建工具栏

fileToolBar = addToolBar(“File”);

editToolBar = addToolBar(“Edit”);

// 图片显示区域

imageScene = new QGraphicsScene(this);

imageView = new QGraphicsView(imageScene);

setCentralWidget(imageView);

createActions();

}

void MainWindow::createActions()

{

// 创建动作,用于打开图片

openAction = new QAction(“&Open”, this);

fileMenu->addAction(openAction);

// 将动作添加至工具栏

fileToolBar->addAction(openAction);

// 连接信号和槽

connect(openAction, SIGNAL(triggered(bool)), this, SLOT(openImage()));

}

void MainWindow::openImage()

{

qDebug() << “slot openImage is called.”;

QFileDialog dialog(this);

dialog.setWindowTitle(“Open Image”);

dialog.setFileMode(QFileDialog::ExistingFile);

dialog.setNameFilter(tr(“Images (*.png *.bmp *.jpg)”));

QStringList filePaths;

if (dialog.exec()) {

filePaths = dialog.selectedFiles();

showImage(filePaths.at(0));

}

}

void MainWindow::showImage(QString path)

{

imageScene->clear();

imageView->resetTransform();

QPixmap image(path);

currentImage = imageScene->addPixmap(image);

imageScene->update();

imageView->setSceneRect(image.rect());

currentImagePath = path;

}

编译运行

主窗口程序编写完成后,编译测试

$ qmake -makefile

$ make

$ ./ImageView

主窗口成功运行由于上述代码并非本文主要说明对象,因此不做过多解释。

接下来,使用Qt插件机制,实现图片模糊操作。

插件接口


首先是一些必须安装的依赖项:

Qt插件机制是一种强大的方法,它使Qt应用程序更具可扩展性。使用此机制抽象出一种可以轻松添加新功能的方法。

完成后,只需要注意功能的名称和操作,就可以添加一个新功能。

第一步是找出一个接口,以便在应用程序和插件之间提供一个公共协议,这样就可以加载和调用插件,而不管它们是如何实现的。在C++中,接口是一个带有纯虚成员函数的类。对于插件,处理动作名和操作,因此,创建editor_plugin_interface.h文件,在editor_plugin_interface.h中声明接口:

#ifndef EDITOR_PLUGIN_INTERFACE_H

#define EDITOR_PLUGIN_INTERFACE_H

#include

#include

//引入opencv库方便编辑操作

#include “opencv2/opencv.hpp”

class EditorPluginInterface

{

public:

virtual ~EditorPluginInterface(){};

virtual QString name()=0;

virtual void edit(const cv::Mat &input,cv::Mat &output)=0;

};

#define EDIT_PLUGIN_INTERFACE_IID “com.kdr2.editorplugininterface”

Q_DECLARE_INTERFACE(EditorPluginInterface,EDIT_PLUGIN_INTERFACE_IID);

#endif // EDITOR_PLUGIN_INTERFACE_H

声明一个名为EditorPluginInterface的类,它是接口类。在这个类中,除了一个虚的空析构函数,可以看到两个纯虚成员函数:name和edit函数。name函数返回一个QString,它将是编辑操作的名称。edit函数以Mat的两个引用作为其输入和输出函数,用于编辑操作。每个插件都是这个接口的一个子类,这两个函数的实现将决定操作名和编辑操作。

在类声明之后,定义一个唯一的标识字符串com.kdr2.editorplugininterface作为接口的ID。这个ID在应用程序范围内必须是唯一的,也就是说,如果编写其他接口,则必须为它们使用不同的ID。然后,使用Q_DECLARE_INTERFACE宏将接口的类名与定义的唯一标识符相关联,这样Qt的插件系统就可以在加载插件之前识别该接口的插件。至此,编辑功能的界面已经确定。编写一个插件来实现这个接口。

实现插件


编写Qt插件,应该从头开始一个新的Qt项目。考虑到主要目的是引入Qt库的插件机制,将使用OpenCV库中的一个简单函数进行简单的编辑以保持代码清晰。这里,调用OpenCV库中的模糊函数来模糊图像。

将插件命名BlurPlugin,然后从头开始创建项目,插件目录和主程序目录在同一级:

$ cd …

$ ls

ImageView

$ mkdir BlurPlugin

$ ls

ImageView BlurPlugin

$ cd blurPlugin

$ touch blur_plugin.h blur_plugin.cpp

$ qmake -project

首先,将目录更改为ImageView项目的父目录,创建一个名为blurPlugin的新目录,然后进入该目录。创建两个空的源文件,blur_plugin.hblur_plugin.cpp。运行qmake -project,它将创建名为BlurPlugin.pro项目文件,修改项目文件设置:

TEMPLATE = lib

TARGET = ErodePlugin

CONFIG += plugin

INCLUDEPATH += . …/ImageEditor

在项目文件中使用lib而不是app作为其模板设置的值。添加了一行CONFIG+=plugin,告诉qmake这个项目是一个Qt插件项目。最后,将ImageView项目的根目录添加为该项目的include路径中的一项,这样编译器就可以找到接口头文件editor_plugin_interface.h。

在这个插件中,还需要OpenCV来实现编辑功能,因此需要在Qt插件项目的设置中添加OpenCV库的信息,库路径和包含库的路径(根据自己的OpenCV安装目录):

unix:!mac {

INCLUDEPATH += /home/brainiac/Program/opencv4/include/opencv4

LIBS += -L/home/brainiac/Program/opencv4/lib -lopencv_core -l opencv_imgproc

}

unix:mac {

INCLUDEPATH += /path/to/opencv/include/opencv4

LIBS += -L/path/to/opencv4/lib -lopencv_world

}

WIN32 {

INCLUDEPATH += C:/path/to/opencv/include/opencv4

LIBS += -lc:/path/to/opencv4/lib/opencv_world

}

在项目文件的结尾,将头文件和C++源文件添加到项目中:

HEADERS += blur_plugin.h

SOURCES += blur_plugin.cpp

插件的项目文件已经完成,开始编写插件。为一个新的功能编写一个插件只是为了提供EditorPluginInterface接口的实现。因此,在blur_plugin.h中声明该接口的一个子类:

#include

#include

#include “editor_plugin_interface.h”

class BlurPlugin:public QObject,public EditorPluginInterface

{

Q_OBJECT;

Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID);

Q_INTERFACES(EditorPluginInterface);

public:

QString name();

void edit(const cv::Mat &input,cv::Mat &output);

};

在包含必要的头文件之后,声明了一个名为BlurPlugin的类,它继承了QObject和EditorPluginInterface。后者是在editor_plugin_interface.h中定义的接口。将插件实现作为QOBject的子类,是因为Qt元对象系统和插件机制的要求。在类的主体中,使用Qt库定义的一些宏添加更多信息:

Q_OBJECT;

Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID);

Q_INTERFACES(EditorPluginInterface);

Q_OBJECT宏与Qt元对象系统有关。

Q_PLUGIN_METADATA(IID EDIT_PLUGIN_INTERFACE_IID);声明此插件的元数据,在这里声明为在editor_plugin_interface.h中定义的插件接口的唯一标识符作为其IID元数据。

使用Q_INTERFACES(EditorPluginInterface);告诉Qt这个类正试图实现的是EditorPluginInterface接口。由于前面的信息的清楚声明,Qt插件系统对这个项目了如指掌:

这是一个Qt插件项目,所以项目的目标是生成库文件。

这个插件是EditorPluginInterface的一个实例,它的IID是EDIT_PLUGIN_INTERFACE_IID,因此Qt应用程序可以加载这个插件。

在接口中声明两个重要成员函数:

public:

QString name();

void edit(const cv::Mat &input,cv::Mat &output);

然后,在blur_plugin.cpp文件中实现这两个函数。对于name函数,只返回一个QString Blur,作为插件的名称(也是操作的名称):

#include “blur_plugin.h”

QString BlurPlugin::name()

{

return “Blur”;

}

对于操作函数:

void BlurPlugin::edit(const cv::Mat &input, cv::Mat &output)

{

blur(input,output,cv::Mat());

}

只需调用由OpenCV库提供的模糊函数。

编译插件项目。与编译普通Qt应用程序的方法相同。

$ qmake -makefile

$ make

$ ls -l .so

-rwxr-xr-x 1 brainiac brainiac 74480 Jul 21 10:10 libBlurPlugin.so

编译过程完成后,使用ls -l _.so_检查输出文件,输出共享对象文件。这些是将加载到应用程序中的插件文件。

在检查输出文件时,可能会发现有许多扩展名类似1.0.0的文件。这些类型的字符串是库文件的版本号。这些文件大多是一个真正的库文件的别名。

如果使用平台并非Linux,那么输出文件可能也会有所不同:在Windows上,这些文件的名称类似于BlurPlugin.dll,而在macOS上,这些文件被命名为libBlurPlugin.dylib.

将插件加载到应用程序中


将Blur插件加载到应用程序中,就可以使用模糊操作。之后,将看到一个名为模糊的新操作,可以在编辑菜单和编辑工具栏上找到。

加载插件首先,修改ImageView项目的项目文件ImageView.pro,并将包含插件接口的头文件添加到HEADERS列表中,同时声明OpenCV库位置:

HEADERS += mainwindow.h editor_plugin_interface.h

unix:!mac {

INCLUDEPATH += /home/brainiac/Program/opencv4/include/opencv4

LIBS += -L/home/brainiac/Program/opencv4/lib -lopencv_core -l opencv_imgproc

}

unix:mac {

INCLUDEPATH += /path/to/opencv/include/opencv4

LIBS += -L/path/to/opencv4/lib -lopencv_world

}

WIN32 {

INCLUDEPATH += C:/path/to/opencv/include/opencv4

LIBS += -lc:/path/to/opencv4/lib/opencv_world

}

然后,在mainwindow.h源文件中引入插件接口头文件。还将使用一个名为QMap的数据结构来保存加载的所有插件的列表,因此还包括QMap的头文件:

#include

#include “editor_plugin_interface.h”

然后,在MainWindow类的声明体中,声明了两个成员函数:

void loadPlugins():用于加载某个目录中出现的所有插件。

void pluginPerform():这是一个公共槽,将连接到加载插件创建的所有操作。在这个slot中,应该区分哪个动作被触发了,哪个动作导致了这个slot被调用,然后找到与该动作相关的插件并执行它的编辑操作。

添加这两个成员函数后,添加一个QMap类型的成员字段来注册所有加载的插件:

QMap<QString, EditorPluginInterface*> editPlugins;

这个映射的键是插件的名称,值将是指向已加载插件实例的指针。

实现loadPlugins函数来加载插件。首先,在mainwindow.cpp:

#include

然后,实现loadPlugins成员函数:

void MainWindow::loadPlugins()

{

QDir pluginsDir(QApplication::instance()->applicationDirPath()+“/plugins”);

QStringList nameFilters;

nameFilters<<“.so"<<".dylib”<<“*.dll”;

QFileInfoList plugins = pluginsDir.entryInfoList(

nameFilters,QDir::NoDotAndDotDot|QDir::Files,QDir::Name);

foreach(QFileInfo plugin,plugins){

QPluginLoader pluginLoader(plugin.absoluteFilePath(),this);

EditorPluginInterface plugin_ptr=dynamic_cast<EditorPluginInterface>(pluginLoader.instance());

if (plugin_ptr){

QAction *action = new QAction(plugin_ptr->name());

editMenu->addAction(action);

editToolBar->addAction(action);

editPlugins[plugin_ptr->name()]=plugin_ptr;

connect(action,SIGNAL(triggered(bool)),this,SLOT(pluginPerform()));

}else{

qDebug()<<“bad plugin:”<<plugin.absoluteFilePath();

}

}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值