% vim: set textwidth=78 autoindent:

\section{Writing a QGIS Plugin in Python}

% when the revision of a section has been finalized,
% comment out the following line:
% \updatedisclaimer

In this section you find a beginner's tutorial for writing a QGIS Python
plugins. It is based on the workshop "Extending the Functionality of QGIS
with Python Plugins" held at FOSS4G 2008 by Dr. Marco Hugentobler, Dr. Horst
D\"uster and Tim Sutton. 

Apart from writing a QGIS Python plugin, it is also possible to use PyQGIS
from a python command line console which is mainly interesting for debugging
or to write standalone applications in Python with their own user interfaces
using the functionality of the QGIS core library.

\subsection{Why Python and what about licensing}

Python is a scripting language which was designed with the goal of being easy
to program. It has a mechanism that automatically releases memory that is no
longer used (garbagge collector). A further advantage is that many programs
that are written in C++ or Java offer the possibility to write extensions in
Python, e.g. OpenOffice or Gimp. Therefore it is a good investment of time to
learn the Python language.

PyQGIS plugins use functionality of libqgis\_core.so and libqgis\_gui.so. As
both are licensed under GNU GPL, QGIS Python plugins must be licenced under the
GPL, too. This means you may use your plugins for any purpose and you are not
forced to publish them. If you do publish them however, they must be
published under the conditions of the GPL license. 

\subsection{What needs to be installed to get started}

On the lab computers, everything for the workshop is already installed. If
you program Python plugins at home, you will need the following libraries and
programs:

\begin{itemize}
\item QGIS
\item Python
\item Qt
\item PyQT
\item PyQt development tools
\end{itemize}

If you use Linux, there are binary packages for all major distributions. For
Windows, the PyQt installer already contains Qt, PyQt and the PyQt
development tools.

\subsection{Programming a simple PyQGIS Plugin in four steps}\label{subsec:pyfoursteps}

The example plugin is intentionally kept simple. It adds a button to the menu
bar of QGIS. If the button is clicked, a file dialog appears where the user
may load a shape file.

For each python plugin, a dedicated folder that contains the plugin files
needs to be created. By default, QGIS looks for plugins in
two locations: \$QGIS\_DIR/share/qgis/python/plugins and \$HOME/.qgis/python/plugins.
Note that plugins installed in the latter location are only visible for one user.

\minisec{Step 1: Make the plugin manager recognise the plugin}

Each Python plugin is contained in its own directory. When QGIS starts up it
will scan each OS specific subdirectory and initialize any plugins it finds. 

\begin{itemize}
\item \nix{Linux and other unices}:\\
./share/qgis/python/plugins \\
/home/\$USERNAME/.qgis/python/plugins
\item \osx{Mac OS X}:\\
./Contents/MacOS/share/qgis/python/plugins \\
/Users/\$USERNAME/.qgis/python/plugins
\item \win{Windows}:\\
C:\textbackslash Program Files\textbackslash QGIS\textbackslash python\textbackslash plugins \\
C:\textbackslash Documents and Settings\textbackslash\$USERNAME\textbackslash .qgis\textbackslash python\textbackslash plugins

\end{itemize}

Once that's done, the plugin will show up in the
\dropmenuopttwo{mActionShowPluginManager}{Plugin Manager...}

\begin{Tip}\caption{\textsc{Two QGIS Python Plugin folders}}
\qgistip{There are two directories containing the python plugins. \$QGIS\_DIR/share/qgis/python/plugins
is designed mainly for the core plugins while \$HOME/.qgis/python/plugins for easy installation of the external plugins. Plugins in the home location are only visible for one user but also mask the core plugins with the same name, what can be used to provide main plugin updates
}
\end{Tip}

To provide the neccessary information for QGIS, the plugin needs to implement
the methods \method{name()}, \method{description()}, \method{version()},
\method{qgisMinimumVersion()} and \method{authorName()} which return descriptive strings.
The \method{qgisMinimumVersion()} should return a simple form, for example ``1.0``. A plugin also needs a method
\method{classFactory(QgisInterface)} which is called by the plugin manager to create
an instance of the plugin. The argument of type QGisInterface is used by the
plugin to access functions of the QGIS instance. We are going to work with
this object in step 2.  

Note that, in contrast to other programing languages, indention is very
important. The Python interpreter throws an error if it is not correct.

For our plugin we create the plugin folder 'foss4g\_plugin' in
\filename{\$HOME/.qgis/python/plugins}. Then we add two new textfiles into this
folder, \filename{foss4gplugin.py} and \filename{\_\_init\_\_.py}.

The file \filename{foss4gplugin.py} contains the plugin class:

\begin{verbatim}
# -*- coding: utf-8 -*-
# Import the PyQt and QGIS libraries
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
# Initialize Qt resources from file resources.py
import resources

class FOSS4GPlugin:

def __init__(self, iface):
# Save reference to the QGIS interface
  self.iface = iface

def initGui(self):
  print 'Initialising GUI'

def unload(self):
  print 'Unloading plugin'
\end{verbatim}

The file \filename{\_\_init\_\_.py} contains the methods \method{name()},
\method{description()}, \method{version()}, \method{qgisMinimumVersion()}
and \method{authorName()} and \method{classFactory}. As
we are creating a new instance of the plugin class, we need to import the
code of this class:

\begin{verbatim}
# -*- coding: utf-8 -*-
from foss4gplugin import FOSS4GPlugin
def name():
  return "FOSS4G example"
def description():
  return "A simple example plugin to load shapefiles"
def version():
  return "0.1"
def qgisMinimumVersion():
  return "1.0"
def authorName():
  return "John Developer"
def classFactory(iface):
  return FOSS4GPlugin(iface)
\end{verbatim}

At this point the plugin already the neccessary infrastructure to appear in
the QGIS \dropmenuopttwo{mActionShowPluginManager}{Plugin Manager...} to be
loaded or unloaded. 

\minisec{Step 2: Create an Icon for the plugin}

To make the icon graphic available for our program, we need a so-called
resource file. In the resource file, the graphic is contained in hexadecimal
notation. Fortunately, we don't need to care about its representation because
we use the pyrcc compiler, a tool that reads the file
\filename{resources.qrc} and creates a resource file. 

The file \filename{foss4g.png} and the \filename{resources.qrc} we use in
this little workshop can be downloaded from
\url{http://karlinapp.ethz.ch/python\_foss4g}. Move these 2 files into the
directory of the example plugin
\filename{\$HOME/.qgis/python/plugins/foss4g\_plugin} and enter there: pyrcc4 -o
resources.py resources.qrc.

\minisec{Step 3: Add a button and a menu}

In this section, we implement the content of the methods \method{initGui()} and
\method{unload()}. We need an instance of the class \classname{QAction} that executes the
\method{run()} method of the plugin. With the action object, we are then able to
generate the menu entry and the button:

\begin{verbatim}
import resources

  def initGui(self):
    # Create action that will start plugin configuration
    self.action = QAction(QIcon(":/plugins/foss4g_plugin/foss4g.png"), "FOSS4G plugin",
self.iface.getMainWindow())
    # connect the action to the run method
    QObject.connect(self.action, SIGNAL("activated()"), self.run)

    # Add toolbar button and menu item
    self.iface.addToolBarIcon(self.action)
    self.iface.addPluginMenu("FOSS-GIS plugin...", self.action)

    def unload(self):
    # Remove the plugin menu item and icon
    self.iface.removePluginMenu("FOSSGIS Plugin...", self.action)
    self.iface.removeToolBarIcon(self.action)
\end{verbatim}

\minisec{Step 4: Load a layer from a shape file}

In this step we implement the real functionality of the plugin in the
\method{run()} method. The Qt4 method \method{QFileDialog::getOpenFileName}
opens a file dialog and returns the path to the chosen file. If the user
cancels the dialog, the path is a null object, which we test for. We then
call the method \method{addVectorLayer} of the interface object which loads
the layer. The method only needs three arguments: the file path, the name of
the layer that will be shown in the legend and the data provider name. For
shapefiles, this is 'ogr' because QGIS internally uses the OGR library to
access shapefiles:

\begin{verbatim}
    def run(self):
    fileName = QFileDialog.getOpenFileName(None,QString.fromLocal8Bit("Select a file:"),
 "", "*.shp *.gml")
    if fileName.isNull():
      QMessageBox.information(None, "Cancel", "File selection canceled")
      else:
      vlayer = self.iface.addVectorLayer(fileName, "myLayer", "ogr")
\end{verbatim}


\subsection{Committing the plugin to repository}

If you have written a plugin you consider to be useful and you want to share with
other users you're welcome to upload it to the QGIS User-Contributed Repository.
\begin{itemize}
\item Prepare a plugin directory containing only necessary files (ensure that there
is no compiled .pyc files, Subversion .svn directories etc).
\item Make a zip archive of it, including the directory. Be sure the zip file
name is exactly the same as the directory inside (except the .zip extension of course).
In other case the Plugin Installer won't be able to relate the available plugin with its
locally installed instance.
\item Upload it to the repository: \url{http://pyqgis.org/admin/contributed} (you
will need to register at first time). Please pay attention when filling the form.
Especially the Version Number field is often filled wrongly what confuses the Plugin
Installer and causes false notifications of available updates.
\end{itemize}

\subsection{Further information}

As you can see, you need information from different sources to write PyQGIS
plugins. Plugin writers need to know Python and the QGIS plugin interface as
well as the Qt4 classes and tools. At the beginning it is best to learn from
examples and copy the mechanism of existing plugins. Using the QGIS plugin
installer, which itself is a Python plugin, it is possible to download a lot
of existing Python plugins and to study their behaviour.

There is a a collection of online documentation that may be usefull for
PyQGIS programers:
 
\begin{itemize}
\item QGIS wiki: \url{http://wiki.qgis.org/qgiswiki/PythonBindings}
\item QGIS API documentation: \url{http://doc.qgis.org/index.html}
\item Qt documentation: \url{http://doc.trolltech.com/4.3/index.html}
\item PyQt: \url{http://www.riverbankcomputing.co.uk/pyqt/}
\item Python tutorial: \url{http://docs.python.org/}
\item A book about desktop GIS and QGIS. It contains a chapter about PyQGIS
plugin programing: \url{http://www.pragprog.com/titles/gsdgis/desktop-gis} 
\end{itemize}

You can also write plugins for QGIS in C++. See Section \ref{cpp_plugin} for
more information about that.

