VS2012基于QT5.1自定接口及插件并实现动态加载

在VS2012中安装了qt-vs-addin-1.2.1-opensource之后,可以直接新建QT5 Designer Plugin项目来构建插件。但是这里的插件都是基于接口QDesignerCustomWidgetInterface实现的,那我们是否可以自定一个插件接口去实现呢?答案是可以的。下面是实现的效果图,具体实现过程见后文。


一、自定义接口

FilterInterface.cpp

#ifndef FILTERINTERFACE_H
#define FILTERINTERFACE_H

#include <QString>
#include <QImage>
#include <QObject>

class FilterInterface
{

public:
    virtual QString name() const=0;
    virtual QImage filter(const QImage &image) const=0;
};

QT_BEGIN_NAMESPACE
    Q_DECLARE_INTERFACE(FilterInterface, "{81AAD42E-1206-443A-8DA2-81C878C23E74}")
QT_END_NAMESPACE

#endif
说明:

1.必须添加QT_BEGIN_NAMESPACE到QT_END_NAMESPACE这一部分,这表明定义了接口。

2.Q_DECLARE_INTERFACE第一个参数是接口的类名,第二个参数是IID。IID在Qt5 Desingner Plugin建立的项目中的QDesignerCustomWidgetInterface所使用的IID是类似于"org.qt-project.Qt.QDesignerCustomWidgetInterface",即在org.qt-project.Qt.后面添加接口的名称,而且在实现接口的类中也采用这样的IID。但是在同一个接口进行多次实现时,如果基于这样的命名方式,有可能导致IID相同,这样在后面动态加载插件时,入口点有可能被认定为是同一个,从而导致两个插件只加载入了一个(我开始时遇到这个问题,折腾了很久才找到这个原因)。后面基于COM组件开发时也有使用IID,而IID是使用GUID的形式生成的,所以我采用了这一形式,将Q_DECLARE_INTERFACE中的IID及后面的IID都使用为GUID。目前正常运行,不知有没有后遗症。

3.将FilterInterface.cpp保存到了Interfaces文件夹下。

二、插件实现

(一)HorizontalPlugin的实现

horizontalplugin.h

#include "../Interfaces/FilterInterface.h"

class FlipHorizontally:public QObject,FilterInterface
{
    Q_OBJECT
        Q_PLUGIN_METADATA(IID "{6A5B6FCE-94D2-40CB-824C-34EEA2FA7367}" FILE "horizontalplugin.json")
        Q_INTERFACES(FilterInterface)
public:
    QString name() const;
    QImage filter(const QImage &image) const;
};
horizontalplugin.cpp
#include "horizontalplugin.h"

QString FlipHorizontally::name() const
{
    return "Horizontally";
}

QImage FlipHorizontally::filter(const QImage &image) const
{
    QImage result( image.width(), image.height(), image.format() );   
    for( int y=0; y<image.height(); ++y )
    {
        for( int x=0; x<image.width(); ++x )
        {
            result.setPixel( x, image.height()-1-y, image.pixel( x, y ) );
        }
    }
    return result;
}
说明:

1.需要继承QObject和自定义接口FilterInterface。

2.一般需要使用Q_OBJECT,这样才能使用信号singlas和槽slot.

3.Q_PLUGIN_METADATA是必须的,其中IID使用GUID。FILE中使用的json文件是必要的,默认是只有大括号{}。具体作用尚不清楚,如有路过的大牛,请指点迷津。

4.Q_INTERFACES是必须的,参数是接口名FilterInterface。

5.可以新建Qt5 Designer Plugin项目,然后将头文件和cpp文件都删除,然后引入FilterInterface.h,添加horizontalplugin.h和horizontalplugin.cpp。

6.项目输出目录修改为../Plugins,以便后面调用。

(二)VerticalPlugin的实现

verticalplugin.h

#include "../Interfaces/FilterInterface.h"

class FlipVertically:public QObject,FilterInterface
{
    Q_OBJECT
        Q_PLUGIN_METADATA(IID "{52D000C5-108A-4A00-B109-8C5509BD8F38}" FILE "verticalplugin.json")
        Q_INTERFACES(FilterInterface)
public:
    QString name() const;
    QImage filter(const QImage &image) const;
};

verticalplugin.cpp

#include "verticalplugin.h"

QString FlipVertically::name() const
{
    return "Vertically";
}

QImage FlipVertically::filter(const QImage &image) const
{    
    QImage result( image.width(), image.height(), image.format() );
    for( int y=0; y<image.height(); ++y )
    {
        for( int x=0; x<image.width(); ++x )
        {   
            result.setPixel(image.width()-1-x,y,image.pixel( x,y));
        }
    }
    return result;
}

说明:参照HorizontalPlugin的说明。

三、动态载入插件

loadplugin.h

#ifndef LOADPLUGIN_H
#define LOADPLUGIN_H

#include <QtWidgets/QMainWindow>
#include <QMap>
#include <QString>
#include <QDir>
#include <QPluginLoader>
#include "../Interfaces/FilterInterface.h"
#include "ui_loadplugin.h"

class LoadPlugin : public QMainWindow
{
    Q_OBJECT

public:
    LoadPlugin(QWidget *parent = 0);
    ~LoadPlugin();
    private slots:
        void filterChanged( QString );
private:
    Ui::LoadPluginClass ui;

    void findFilters();
    QMap<QString, FilterInterface*> filters;
};

#endif // LOADPLUGIN_H
loadplugin.cpp
#include "loadplugin.h"

LoadPlugin::LoadPlugin(QWidget *parent)
    : QMainWindow(parent)
{
    ui.setupUi(this);
        ui.originalLabel->setPixmap( QPixmap( "../Images/source.png" ) );
    connect( ui.filterList, SIGNAL(currentTextChanged(QString)), 
        this, SLOT(filterChanged(QString)) );
    findFilters(); 
    filterChanged( QString() );
}

LoadPlugin::~LoadPlugin()
{

}

void LoadPlugin::findFilters()
{
    QDir path( "../plugins" );
    foreach( QString filename, path.entryList(QDir::Files) )
    {
        QPluginLoader loader( path.absoluteFilePath( filename ) );
        QObject *couldBeFilter = loader.instance();
        if( couldBeFilter )
        {
            FilterInterface *filter = qobject_cast<FilterInterface*>( couldBeFilter );
            if( filter )
            {
                filters[ filter->name() ] = filter;
                ui.filterList->addItem( filter->name() );
            }
        }
    }
}

//////////////////////////////////////////////////////////////////
///slots
void LoadPlugin::filterChanged( QString filter )
{
    if( filter.isEmpty() )
    {
        ui.filteredLabel->setPixmap( *(ui.originalLabel->pixmap() ) );
    }
    else
    {
        QImage filtered = filters[ filter ]->filter( ui.originalLabel->pixmap()->toImage() );
        ui.filteredLabel->setPixmap( QPixmap::fromImage( filtered ) );
    }
}

main.cpp

#include "loadplugin.h"
#include <QtWidgets/QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    LoadPlugin w;
    w.show();
    return a.exec();
}

说明

1.新建Qt5 Application项目,命名为LoadPlugin。

2.在loadplugin.ui添加两个QLabel,分别命名为originalLabel和filteredLabel,再添加一个QListWidget,命名为filterList。

3.使用QDir来加载../Plugins的路径,并用path.entryList来过滤出有入口点的dll,再用QPluginLoader来加载。最后使用qobject_cast<FilterInterface*>来实现强转。

以上就是实现的全过程,具体的源码可以到这里下载http://download.csdn.net/detail/xxdddail/6771015。

转载请注明出处http://blog.csdn.net/xxdddail/article/details/17578191。

你可能感兴趣的:(QPluginLoader,QT加载插件,QT构建插件,QT定义Plugin)