简介
QGIS的插件框架,结构简单、效果明显。基于插件技术,划分出各个相互独立的模块,可灵活搭建界面和组件库,在协作开发、版本迭代、功能扩展等多个层面都有很好的敏捷性。
插件类型
插件类型定义在QgsPlugin类中,包括UI插件、图层插件、渲染插件三种类型。插件类通过QgisInterface接口,进行模块之间的交互。
enum PluginType
{
UI = 1,
MapLayer,
Renderer,
};
插件元数据类
QgsPluginMetadata,用于记录插件的名称、版本、类型、描述等基本信息,以及插件类的指针对象。
插件注册与加载
在QGIS中,插件注册的本质是把插件的名称和元数据类存储在QMap<QString, QgsPluginMetadata>对象中。 看一下注册插件的核心代码(省略掉不重要代码,关键是理解注册机制):
void QgsPluginRegistry::loadCppPlugin( const QString &fullPathName )
{
QLibrary myLib( fullPathName );
bool loaded = myLib.load();
type_t *pType = ( type_t * ) cast_to_fptr( myLib.resolve( "type" ) );
name_t *pName = ( name_t * ) cast_to_fptr( myLib.resolve( "name" ) );
switch ( pType() )
{
case QgisPlugin::Renderer:
case QgisPlugin::UI:
{
create_ui *cf = ( create_ui * ) cast_to_fptr( myLib.resolve( "classFactory" ) );
if ( cf )
{
QgisPlugin *pl = cf( mQgisInterface );
if ( pl )
{
pl->initGui();
addPlugin( baseName, QgsPluginMetadata( myLib.fileName(), pName(), pl ) );
}
}
break;
default:
}
}
加载插件的代码是在QgisApp类构造函数中调用的:
QgsPluginRegistry::instance()->setQgisInterface( mQgisInterface );
if ( restorePlugins )
{
QgsPluginRegistry::instance()->restoreSessionPlugins( QgsApplication::pluginPath() );
QString myPaths = settings.value( QStringLiteral( "plugins/searchPathsForPlugins" ), "" ).toString();
if ( !myPaths.isEmpty() )
{
QStringList myPathList = myPaths.split( '|' );
QgsPluginRegistry::instance()->restoreSessionPlugins( myPathList );
}
}
当然,在加载插件之前还要对插件的合法性进行验证,代码如下:
bool QgsPluginRegistry::checkCppPlugin( const QString &pluginFullPath )
{
QLibrary myLib( pluginFullPath );
bool loaded = myLib.load();
if ( ! loaded )
{
QgsMessageLog::logMessage( QObject::tr( "Failed to load %1 (Reason: %2)" ).arg( myLib.fileName(), myLib.errorString() ), QObject::tr( "Plugins" ) );
return false;
}
name_t *myName = ( name_t * ) cast_to_fptr( myLib.resolve( "name" ) );
description_t *myDescription = ( description_t * ) cast_to_fptr( myLib.resolve( "description" ) );
category_t *myCategory = ( category_t * ) cast_to_fptr( myLib.resolve( "category" ) );
version_t *myVersion = ( version_t * ) cast_to_fptr( myLib.resolve( "version" ) );
if ( myName && myDescription && myVersion && myCategory )
return true;
QgsDebugMsgLevel( "Failed to get name, description, category or type for " + myLib.fileName(), 2 );
return false;
}
QGIS插件的存放路径一般是运行时的plugins文件夹中,也可以通过环境变量配置默认的插件路径。从下图中,大致可以看出:名称以plugin结尾的是UI类型的插件;以provider结尾的都是图层类型的插件,用于各类数据格式的读写等。
扩展插件的实现
这里只介绍以plugin结尾的UI插件,以拓扑检查的插件topolplugin.dll为例。 插件类要继承QObject, QgisPlugin这两个类,且必须实现initGui()虚函数,其他自定义的函数用于配合实现UI的逻辑。
static const QString sName = QObject::tr( "Topology Checker" );
static const QString sDescription = QObject::tr( "A Plugin for finding topological errors in vector layers" );
static const QString sCategory = QObject::tr( "Vector" );
static const QString sPluginVersion = QObject::tr( "Version 0.1" );
static const QgisPlugin::PluginType sPluginType = QgisPlugin::UI;
static const QString sPluginIcon = QStringLiteral( ":/topology/mActionTopologyChecker.svg" );
class Topol: public QObject, public QgisPlugin
{
Q_OBJECT
public:
explicit Topol( QgisInterface *interface );
public slots:
void initGui() override;
void run();
void showOrHide();
void unload() override;
void help();
private:
QgisInterface *mQGisIface = nullptr;
QAction *mQActionPointer = nullptr;
checkDock *mDock = nullptr;
};
再来看initGui()的定义:
void Topol::initGui()
{
delete mQActionPointer;
mQActionPointer = new QAction( QIcon( sPluginIcon ), sName, this );
mQActionPointer->setObjectName( QStringLiteral( "mQActionPointer" ) );
mQActionPointer->setCheckable( true );
mQActionPointer->setWhatsThis( tr( "Topology Checker for vector layer" ) );
connect( mQActionPointer, &QAction::triggered, this, &Topol::showOrHide );
mQGisIface->addVectorToolBarIcon( mQActionPointer );
mQGisIface->addPluginToVectorMenu( QString(), mQActionPointer );
}
再来看QgisApp类对添加UI控件的实现:
void QgisApp::addPluginToVectorMenu( const QString &name, QAction *action )
{
QMenu *menu = getVectorMenu( name );
menu->addAction( action );
}
这既是实现插件的开发过程,对照前面定义的目录是Vector,名称是Topology Checker,即可在工具栏中找到该控件。
|