% vim: set textwidth=78 autoindent:

\section{Ein QGIS Plugin in Python schreiben}

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

In diesem Kapitel wird beispielhaft vorgestellt, wie man ein Python Plugin in
QGIS schreibt. Das Beispiel basiert auf einem Workshop von der FOSS4G 2008
und wurde von Dr. Marco Hugentobler, Dr. Horst Düster und Tim Sutton erstellt.

Abgesehen von der Möglichkeit, QGIS Python Plugins zu schreiben, kann PyQGIS
auch in der Kommandozeile verwendet werden, was überwiegend interessant für
Debugging oder das Erstellen eigenständiger Applikationen in Python zu
erstellen ist und darin die Funktionalitäten der QGIS Kernbibliotheken zu
nutzen. 

\subsection{Warum Python und wie sieht es mit der Lizensierung aus}

Python ist eine Skriptsprache, die einfach zu programmieren ist. Es besitzt
einen Mechanismus, der automatisch nicht mehr benötigten Speicher freigibt
(garbage collector). Ein weiterer Vorteil besteht darin, dass viele
Programme, die in C++ oder Java geschrieben sind, die Möglichkeit bieten,
Erweiterungen in Python zu schreiben, z.B. OpenOffice or Gimp. Daher ist es
eine gute Investition, Python zu lernen. 

PyQGIS Plugins nutzen die Funktionalitäten der libqgis\_core.so und
libqgis\_gui.so Bibliotheken. Da beide unter der GNU GPL lizensiert sind,
müssen auch Python Plugins unter der GPL lizensiert werden, wenn sie auf
diese Bibliotheken zugreifen. Das bedeutet, Sie können die Plugins für jeden
Zweck verwenden und sind nicht dazu verpflichtet sie zu veröffentlichen.
Falls Sie sie aber veröffentlichen wollen, dann nur unter den
Lizensbedingungen der GNU GPL.

\subsection{Welche Software muss vor dem Start installiert werden}

Wenn Sie ein Python Plugin programmieren möchten, muss folgende Software auf
ihrem Rechner installiert sein:

\begin{itemize}
\item QGIS
\item Python >= 2.5
\item Qt
\item PyQt
\item PyQt development tools
\end{itemize}

Unter GNU/Linux gibt es Binärpakete für fast jede Distribution. Unter MS
Windows enthält der PyQt Installer bereits QT, PyQt und die PyQt development
tools.

\subsection{Ein einfaches PyQGIS Plugin in vier Schritten schreiben}\label{subsec:pyfoursteps}

Das Beispiel ist einfach gehalten. Es fügt einen Eintrag in die Menüleiste von
QGIS ein und ein Icon in die Werkzeugleiste. Wenn das Icon angeklickt wird,
öffnet sich ein Dialog, über den man ein Shapefile laden kann.  

Für jedes Python Plugin wird ein eigener Ordner erstellt. Standardmäßig sucht
QGIS unter GNU/Linux an den Orten \$QGIS\_DIR/share/qgis/python/plugins und
\$HOME/.qgis/python/plugins. Beachten Sie, dass Plugins im Homeverzeichnis
nur von dem jeweiligen Benutzer sichtbar sind.

\minisec{Schritt 1: Erkennen des neuen Plugins im Plugin Manager}

Jedes Python Plugin liegt in einem eigenen Ordner. Wenn QGIS gestartet wird,
werden die entsprechenden Ordner durchsucht und alle darin enthaltenen
Plugins initialisiert.

\begin{itemize}
\item \nix{GNU/Linux und andere 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{MS 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}

Danach wird das Plugin im \dropmenuopttwo{mActionShowPluginManager}{Plugin
Manager} angezeigt.

\begin{Tip}\caption{\textsc{Zwei QGIS Python Plugin Ordner}}
\qgistip{Es gibt zwei Ordner, die Python Plugins enthalten können:
\$QGIS\_DIR/share/qgis/python/plugins ist überwiegend für Kernplugins
vorgesehen, während \$HOME/.qgis/python/plugins für die Installation externer
Plugins verwendet wird. Plugins im Homeverzeichnis sind nur für den
jeweiligen Benutzer sichtbar und maskieren Kernplugins mit demselben Namen.
Dies kann verwendet werden, um Kernplugins upzudaten.
}
\end{Tip}

Um die notwendigen Informationen für QGIS bereitzustellen, müssen im Plugin
folgende Methoden integriert werden, die dann entsprechende, beschreibende
Texte zurückgeben: \method{name()}, \method{description()},
\method{version()}, \method{qgisMinimumVersion()} and \method{authorName()}.
Ein Plugin benötigt auch die Methode \method{classFactory(QgisInterface)},
welche durch den Plugin Manager aufgerufen wird, um eine Instanz des Plugins
zu starten. Das Argument des Typs QGisInterface wird verwendet, um auf
Funktionalitäten zuzugreifen. Es wird in Schritt 2 integriert. 

Beachten Sie, dass im Gegensatz zu anderen Programmiersprachen, das Einrücken
ist sehr wichtig. Der Python Interpreter zeigt eine Fehlermeldung, wenn es
nicht korrekt ist. 

Für das Beispielplugin soll ein Ordner \filename{foss4g\_plugin} im
Verzeichnis \filename{\$HOME/.qgis/python/plugins} erstellt werden. Danach
werden darin zwei neue Dateien \filename{foss4gplugin.py} und
\filename{\_\_init\_\_.py} ergänzt.

Die Datei \filename{foss4gplugin.py} enthält die Plugin Klasse:

\begin{verbatim}
# -*- coding: utf-8 -*-
# Importiere die PyQt und QGIS Bibliotheken
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *
# Initialisiere Qt Ressourcen aus der Datei 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}

Die Datei \filename{\_\_init\_\_.py} enthält die Methoden  \method{name()},
\method{description()}, \method{version()}, \method{qgisMinimumVersion()},
\method{authorName()} und \method{classFactory}. Da eine neue Instanz der
Plugin Klasse erstellt wird, muss der Quelltext dieser Klasse importiert
werden.

\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}

An dieser Stelle hat das Plugin bereits alles, um im
\dropmenuopttwo{mActionShowPluginManager}{Plugin Manager} geladen und
entladen zu werden.

\minisec{Schritt 2: Ein Icon für das Plugin erstellen}

Um das Icon verfügbar zu machen, muss eine sogenannte resource Datei erstellt
werden. In der \filename{resource} Datei wird die Grafik als hexadezimal
Angabe abgelegt. Um die Darstellung kümmert sich der pyrcc Compiler, der die
Datei \filename{resources.qrc} ausliest und die resource Datei erstellt.

Die Datei \filename{foss4g.png} und die \filename{resources.qrc} Datei können
von der URL \url{http://karlinapp.ethz.ch/python\_foss4g} heruntergeladen
werden. Verschieben Sie die Dateien in den Ordner
\filename{\$HOME/.qgis/python/plugins/foss4g\_plugin} und geben Sie den
Befehl ein: 

\begin{verbatim}
pyrcc4 -o resources.py resources.qrc
\end{verbatim}

\minisec{Schritt 3: Ein neues Icon und einen Menüeintrag hinzufügen}

In diesem Abschnitt fügen wir den Inhalt der Methoden \method{initGui()} und
\method{unload()} ein. Wir benötigen eine Instanz der Klasse
\classname{QAction}, welche die \method{run()} Methode des Plugins startet.
Mit dem action Objekt ist es möglich, den Menüeintrag und das Icon zu
erstellen: 

\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{Schritt 4: Das Laden eines Shapefiles}

An dieser Stelle bauen wir die eigentliche Funktionalität des Plugins in die
\method{run()} Methode ein. Die Qt4 Methode
\method{QFileDialog::getOpenFileName} öffnet einen Dateidialog und gibt den
Pfad zur ausgewählten Datei zurück. Wenn der Benutzer den Dialog abbricht,
ist der Pfad ein Null-Objekt, auf das getestet wird. Danach wird die Methode
\method{addVectorLayer} aufgerufen, welche den Vektorlayer lädt. Die Methode
benötigt nur drei Argumente: den Dateipfad, den Namen des Layers der in der
Legende angezeigt werden soll und den 'Dataprovider' Namen. Für Shapefiles
ist es 'ogr', da QGIS intern die OGR-Bibliothek verwendet, um auf Shapefiles
zuzugreifen.

\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{Das Plugin in ein Repository committen}

Wenn Sie ein Plugin geschrieben haben, das ihrer Meinung nach nützlich für
andere Anwender ist, können Sie es zum QGIS User-Contributed Repository
hinzufügen.  

\begin{itemize}
\item Bereiten Sie einen Plugin Ordner nur mit den notwendigen Dateien vor.
(Stellen Sie sicher, dass keine vorkompilierte .pyc, Subversion .svn oder
ähnliche Dateien dabei sind.)
\item Erstellen Sie daraus einen ZIP-File mit dem Ordner. Stellen Sie sicher,
dass der ZIP-File denselben Namen trägt, wie der Ordner darin (ohne die .zip
Endung). Ansonsten ist der Plugin Installer nicht in der Lage, das Plugin mit
seiner lokal installierten Instanz zu verbinden.
\item Laden Sie das Plugin in das Repository:
\url{http://pyqgis.org/admin/contributed} (Sie müssen sich zuerst
registrieren). Achten Sie bitte darauf, wenn Sie das Anmeldeformular
ausfüllen. Besonders ds Feld 'Version Number' ist oft falsch ausgefüllt,
verwirrt dann den Plugin Installer und führt zu fehlerhaften Anmerkungen über
vorhandene Updates.
\end{itemize}

\subsection{Weiterführende Informationen}

Man benötigt eine Reihe von Informationen aus unterschiedlichen Quellen, um
ein PyQGIS Plugin zu schreiben. Man muss python kennen und das QGIS Plugin
Interface, sowie die Qt4 Klassen und Werkzeuge. Am Anfang ist es am besten,
wenn man von Beispielen lernt. Mit Hilfe des Plugin Installer, der selbst
auch ein Python Plugin ist, kann man eine Vielzahl von Plugins herunterladen
und studieren.

Als Online Dokumentation sind für PyQGIS Programmierer folgende Links
nützlich: 
 
\begin{itemize}
\item QGIS wiki: \url{http://wiki.qgis.org/qgiswiki/PythonBindings}
\item QGIS API Dokumentation: \url{http://doc.qgis.org/index.html}
\item Qt Dokumentation: \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 Ein Buch über Desktop GIS und QGIS. Es enthält ein Kapitel über PyQGIS
Plugin Programmierung: \url{http://www.pragprog.com/titles/gsdgis/desktop-gis} 
\end{itemize}

Sie können Plugins auch in C++ schreiben. Weitere Informationen dazu finden
Sie in Kapitel~\ref{cpp_plugin}.


