qgis 是一个开源的地理信息系统,源代码:https://github.com/qgis/QGIS ,基于Qt进行开发,因为Qt支持Android平台,因此,qgis也可以移植到Android平台下。具体移植方法见于:https://github.com/qgis/QGIS-Android 。

qgis目前主要支持python 形式的插件,文档较为丰富,可用的插件也非常多。但是本人不太会,本人只会使用c++进行插件开发,虽然这是qgis所不太推荐的方式。


在qgis源代码目录中src/plugins/中有一个plugin_builder.py ,是一个python脚本,用来通过一个插件模板生成一个基本的插件。

生成的插件目录如下所示。

plugin.cpp 是插件的实现文件,

CMakeLists.txt是工程控制文件,

plugin.png是插件的图标文件,

plugingui.cpp是一个示例的对话框。

pluginguibase.ui是对话框的窗体。


我们先看plugin.h

插件类 Plugin继承了 QgisPlugin 和 QObject类,

mPluginType 是enum类型,一般都是一样的,都是 QgisPlugin::PLUGINTYPE sPluginType = QgisPlugin::UI;

mQGisIface 是接口类,用来与主程序进行交互。

mQActionPointer 是插件显示在主程序上的按钮相关的QAction。


#ifndef Plugin_H
#define Plugin_H

//QT4 includes
#include <QObject>

//QGIS includes
#include "../qgisplugin.h"

//forward declarations
class QAction;
class QToolBar;

class QgisInterface;

/**
* \class Plugin
* \brief [name] plugin for QGIS
* [description]
*/
class Plugin: public QObject, public QgisPlugin
{
    Q_OBJECT
  public:

    //
    //
    //                MANDATORY PLUGIN METHODS FOLLOW
    //
    //

    /**
    * Constructor for a plugin. The QgisInterface pointer is passed by
    * QGIS when it attempts to instantiate the plugin.
    * @param theInterface Pointer to the QgisInterface object.
     */
    Plugin( QgisInterface * theInterface );
    //! Destructor
    virtual ~Plugin();

  public slots:
    //! init the gui
    virtual void initGui();
    //! Show the dialog box
    void run();
    //! unload the plugin
    void unload();
    //! show the help document
    void help();

  private:

    
    //
    // MANDATORY PLUGIN PROPERTY DECLARATIONS  .....
    //
    

    int mPluginType;
    //! Pointer to the QGIS interface object
    QgisInterface *mQGisIface;
    //!pointer to the qaction for this plugin
    QAction * mQActionPointer;
    
    //
    // ADD YOUR OWN PROPERTY DECLARATIONS AFTER THIS POINT.....
    //
    
};

#endif //Plugin_H

在plugin.cpp中,请注意这几个函数,这几个函数与类无关,是这个插件的导出函数, 

QGISEXTERN 被定义成 __declspec(dllexport),用来导出函数。

/**

 * Required extern functions needed  for every plugin
 * These functions can be called prior to creating an instance
 * of the plugin class
 */
// Class factory to return a new instance of the plugin class
QGISEXTERN QgisPlugin * classFactory( QgisInterface * theQgisInterfacePointer )
{
  return new Plugin( theQgisInterfacePointer );
}
// Return the name of the plugin - note that we do not user class members as
// the class may not yet be insantiated when this method is called.
QGISEXTERN QString name()
{
  return sName;
}

// Return the description
QGISEXTERN QString description()
{
  return sDescription;
}

// Return the category
QGISEXTERN QString category()
{
  return sCategory;
}

// Return the type (either UI or MapLayer plugin)
QGISEXTERN int type()
{
  return sPluginType;
}

// Return the version number for the plugin
QGISEXTERN QString version()
{
  return sPluginVersion;
}

QGISEXTERN QString icon()
{
  return sPluginIcon;
}

// Delete ourself
QGISEXTERN void unload( QgisPlugin * thePluginPointer )
{
  delete thePluginPointer;
}

插件加载过程在 src/app/qgspluginregistry.cpp 中的void QgsPluginRegistry::loadCppPlugin( QString theFullPathName )实现

void QgsPluginRegistry::loadCppPlugin( QString theFullPathName )
{
  QSettings settings;

  QString baseName = QFileInfo( theFullPathName ).baseName();

  // first check to see if its already loaded
  if ( isLoaded( baseName ) )
  {
    // plugin is loaded
    // QMessageBox::warning(this, "Already Loaded", description + " is already loaded");
    return;
  }

  QLibrary myLib( theFullPathName );

  QString myError; //we will only show detailed diagnostics if something went wrong
  myError += QObject::tr( "Library name is %1\n" ).arg( myLib.fileName() );

  bool loaded = myLib.load();
  if ( !loaded )
  {
    QgsMessageLog::logMessage( QObject::tr( "Failed to load %1 (Reason: %2)" ).arg( myLib.fileName() ).arg( myLib.errorString() ), QObject::tr( "Plugins" ) );
    return;
  }

  myError += QObject::tr( "Attempting to resolve the classFactory function\n" );

  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:
    {
      // UI only -- doesn't use mapcanvas
      create_ui *cf = ( create_ui * ) cast_to_fptr( myLib.resolve( "classFactory" ) );
      if ( cf )
      {
        QgisPlugin *pl = cf( mQgisInterface );
        if ( pl )
        {
          pl->initGui();
          // add it to the plugin registry
          addPlugin( baseName, QgsPluginMetadata( myLib.fileName(), pName(), pl ) );
          //add it to the qsettings file [ts]
          settings.setValue( "/Plugins/" + baseName, true );
          QgsMessageLog::logMessage( QObject::tr( "Loaded %1 (Path: %2)" ).arg( pName() ).arg( myLib.fileName() ), QObject::tr( "Plugins" ), QgsMessageLog::INFO );

          QObject *o = dynamic_cast<QObject *>( pl );
          if ( o )
          {
            QgsDebugMsg( QString( "plugin object name: %1" ).arg( o->objectName() ) );
            if ( o->objectName().isEmpty() )
            {
#ifndef WIN32
              baseName = baseName.mid( 3 );
#endif
              QgsDebugMsg( QString( "object name to %1" ).arg( baseName ) );
              o->setObjectName( QString( "qgis_plugin_%1" ).arg( baseName ) );
              QgsDebugMsg( QString( "plugin object name now: %1" ).arg( o->objectName() ) );
            }

            if ( !o->parent() )
            {
              QgsDebugMsg( QString( "setting plugin parent" ) );
              o->setParent( QgisApp::instance() );
            }
            else
            {
              QgsDebugMsg( QString( "plugin parent already set" ) );
            }
          }
        }
        else
        {
          // something went wrong
          QMessageBox::warning( mQgisInterface->mainWindow(), QObject::tr( "Error Loading Plugin" ),
                                QObject::tr( "There was an error loading a plugin."
                                             "The following diagnostic information may help the QGIS developers resolve the issue:\n%1." )
                                .arg( myError ) );
          //disable it to the qsettings file [ts]
          settings.setValue( "/Plugins/" + baseName, false );
        }
      }
      else
      {
        QgsMessageLog::logMessage( QObject::tr( "Unable to find the class factory for %1." ).arg( theFullPathName ), QObject::tr( "Plugins" ) );
      }

    }
    break;
    default:
      // type is unknown
      QgsMessageLog::logMessage( QObject::tr( "Plugin %1 did not return a valid type and cannot be loaded" ).arg( theFullPathName ), QObject::tr( "Plugins" ) );
      break;
  }
}

插件加载的过程可以从中看出来, 用到了Qlib类, 其实就是打开一个dll的操作。
通过对此dll进行加载,获取这个插件的类型,名称,图标。
通过调用classFactory函数产生一个插件类的实例对象的指针。 注意mQgisInterface 变量是主程序中的一个接口指针,初始化了插件中的变量。
      create_ui *cf = ( create_ui * ) cast_to_fptr( myLib.resolve( "classFactory" ) );
      if ( cf )
      {
        QgisPlugin *pl = cf( mQgisInterface );

----------------------------------------------------------------------------------------------------------

在插件中如何与主程序进行交互呢?  没错,就是插件中的 mQGisIface 变量。
QgisInterface类定义于 gui/qgisinterface.h 中,从下可以看出,这是一个虚接口,主要的接口函数都是纯虚函数,我们可能通过这个类调用主程序中的相关功能。
class GUI_EXPORT QgisInterface : public QObject
{
    Q_OBJECT

  public:

    /** Constructor */
    QgisInterface();

    /** Virtual destructor */
    virtual ~QgisInterface();

    /** Get pointer to legend interface
      \note added in 1.4
     */
    virtual QgsLegendInterface* legendInterface() = 0;

    virtual QgsPluginManagerInterface* pluginManagerInterface() = 0;

  public slots: // TODO: do these functions really need to be slots?

    /* Exposed functions */

    //! Zoom to full extent of map layers
    virtual void zoomFull() = 0;

    //! Zoom to previous view extent
    virtual void zoomToPrevious() = 0;

    //! Zoom to next view extent
    virtual void zoomToNext() = 0;

    //! Zoom to extent of the active layer
    virtual void zoomToActiveLayer() = 0;
}


---------------------------------------------------------------------------------------

OK,基本上就是这样了,其他更多的内容,请与作者进行深入交流。

Logo

旨在为数千万中国开发者提供一个无缝且高效的云端环境,以支持学习、使用和贡献开源项目。

更多推荐