Friday, January 18, 2013

How to Create a QGIS Plugin in C++

FalconView has a rich plugin-architecture that I've been writing to for many years. Though I'm very comfortable writing FalconView plugins, I'm in the process now of writing a plugin that needs to work in FalconView and in Quantum GIS.  This post is about my learning experience in writing a plugin for Quantum GIS.  I find that the documentation on QGIS plugins is sparse, so perhaps what I've learned will help others who want to do something similar.

The full source code for this plugin may be found here.  This is a minimalistic plugin that simply draws a rectangle on the screen using screen coordinates.  It demonstrates how to register a plugin with Quantum, how to add an action to a QGIS menu, how to register to receive a callback, and how to draw on the map canvas.  I call it the Hello World plugin.


(Before I started this process, I had no experience coding to QGIS or to Qt.  I'm sure that some of what I've done here does not reflect best practices with either technology, so I invite members of either community to comment here in order to help me and others to understand best practices.  I will note that my plugin does not conform to the Quantum GIS coding standards. Instead, I've conformed to the FalconView coding standards since this project is part of a larger effort related to FalconView.)

[[ EDIT: Most QGIS plugins are done in Python, and there is much better documentation for these plugins. ]]


The Header File

The complete header file may be found here.  There are two things of interest in the header file.  First, notice that the HelloWorldPlugin is a QObject.  This comes by deriving from QObject and by including the QOBJECT macro in the class.


class HelloWorldPlugin : public QObject, public QgisPlugin
{
   Q_OBJECT
...

The next point of interest is the declaration of the Qt slots.  These are callback methods.

public slots:
   void StartOverlay();
   void DrawOverlay(QPainter* painter);

Functions Exported from the Library

There are a handful of functions exported from the library that are required for QGIS to load plugin.  These are all in the cpp file, and are all declared with the QGISEXTERN macro.

Adding a Menu Item to a QGIS Menu

We do this in the initGui method.  The important thing to note is how we register for a callback using connect(...) and how we call into the QGIS interface to add the action to the Plugin menu.

/*virtual*/ void HelloWorldPlugin::initGui()
{
   std::cout << "HelloWorldPlugin::initGui" << std::endl;

   // add an action to the menu
   m_action = new QAction(QIcon("" ), tr("Hello World"), this);
   m_action->setWhatsThis(tr("Draw on the map canvas."));
   connect(m_action, SIGNAL(triggered()), this, SLOT(StartOverlay()));
   m_qgis_if->addRasterToolBarIcon(m_action);
   m_qgis_if->addPluginToMenu(tr("&Hello World"), m_action);
}

Handling the Menu Item Selection Event

StartOverlay() handles this event.  It gets the map canvas and connects to the render complete signal.  I have some code in this sample that demonstrates how to zoom out the map.  This is not necessary for drawing the rectangle, but I've left it in for pedagogical reasons.

void HelloWorldPlugin::StartOverlay()
{
   std::cout << "HelloWorldPlugin::StartOverlay" << std::endl;

   // get the map canvas
   QgsMapCanvas* canvas = m_qgis_if->mapCanvas();

   // connect to the render complete signal
   connect(canvas, SIGNAL(renderComplete(QPainter*)),
      this, SLOT(DrawOverlay(QPainter*)));

   // zoom out for fun
   canvas->zoomOut(); // wheeeeee!!!
}

Drawing the Rectangle

Now that we are registered to receive calls to DrawOverlay when the canvas refreshes, drawing the rectangle is easy.

void HelloWorldPlugin::DrawOverlay(QPainter* painter)
{
   std::cout << "HelloWorldPlugin::DrawOverlay" << std::endl;
   painter->drawRect(20, 20, 60, 60);
}

Building the Project

The Qt project file is here.  First, run qmake qgis_hello_world.pro to make the Makefile.  Next, run make to build the library.  Notice that Qt will generate some code files that you will find in the project folder.

Trying it Out

Copy the library to your QGIS plugins folder.  The command that I use to do this on my Ubuntu system is sudo cp libqgis_hello_world.so.1.0.0 /usr/lib/qgis/plugins/libqgis_hello_world.so.

The Hello World plugin should now appear in the QGIS plugin manager menu.  Turn it on.  Next, select the menu item that we added to the Plugin menu.  The rectangle should draw.

Finding More Help

Be sure to look at the QGIS header files (in /usr/include/qgis on my system) and API reference (http://www.qgis.org/).  Also the samples under src/plugins are helpful.


1 comment:

Rafael Isaias said...

Hello!

I'm trying to execute your example but I receive this error:

In file included from qgis_hello_world.cpp:1:0:
qgis_hello_world.h:4:24: fatal error: qgisplugin.h: No such file or directory

I installed the QGis 1.8.0, QT Libraries... My folder user/local/include is empty and there isn't a folder "qgis" in usr/incluse.

Do you have any idea to help me?

Thanks a lot.