\section{Weitere Anwendungen mit den QGIS-Bibliotheken erstellen}

Ein Ziel von QGIS ist es, neben der eigentlichen Applikation, auch einen
Satz von Bibliotheken zur Verfügung zu stellen, mit denen man weitere
Anwendungen erstellen kann.
Dieses Ziel wurde mit dem Refaktorieren der Bibliotheken nach der
Herausgabe der Version 0.8.0 realisiert. Ab der Version 0.9.0 ist nun die
Entwicklung von C++ oder Python-Anwendungen auf Basis der
QGIS-Bibliotheken möglich.

In diesm Kapitel werden wir einen kurzen Blick auf diese Funktionalitäten
werfen und eine Standalone-Anwendung mit Python erstellen.
Der QGIS-Blog stellt weitere Beispiele zum Erstellen von 
PyQGIS\footnote{Eine Anwendung, die Python und QGIS benutzt.}-Anwendungen bereit.

Wir werden eine dieser Beispielanwendungen als Einstiegspunkt benutzen, um
zu sehen, wie man diese Anwendungen erstellt.

Die Funktionalitäten unserer Anwendung sind folgende:

\begin{itemize}
\item eine Vektordatei laden
\item Verschieben
\item Herein- und herauszoomen
\item Auf volle Ausdehnung des Themas zoomen
\item eigene Farben auswählen, wenn ein Thema geladen ist
\end{itemize} 

Das sind natürlich sehr wenige Funktionen. Lassen Sie uns nun mit der
Erstellung der graphischen Oberfläche beginnen; dazu benutzen wir den QT
Designer.

\subsection{Erstellung der graphischen Oberfläche}

Wenn wir schon nur minimalistische Funktionen einbauen, machen wir auch
nur eine minimalistische Oberfläche. Mit dem QT Designer erstellen wir nun
ein einfaches MainWindow ohne Menüs und Werkzeugleisten. Das erzeugt und
ein leeres Fenster. Zur Erstellung sind die folgeden Punkte nötig:

\begin{enumerate}
\item Erstellen Sie einen leeren Ordner, in der die Anwendung entwickelt
wird und wechseln Sie in diesen Ordner.
\item Starten Sie den Qt Designer
\item Der Dialog 'Neues Formular' sollte erscheinen. Falls nicht, wählen Sie 
\textsl{Neues Formular...} aus dem \textsl{Datei} Menü.
\item Wählen Sie 'Main Window' aus der Vorlagen/Formularliste
\item Klicken Sie  \textsl{Neu von Vorlage} 
\item Ziehen Sie das neue Fenster auf eine nutzbare Fenstergröße 
\item Finden Sie das Frame widget in der Liste (unter Container) und
ziehen Sie es in das Fenster 'Main Window' das sie gerade erstellt haben.
\item Klicken Sie außerhalb des Rahmens um das Hauptfenster zu
selektieren.
\item Klicken Sie auf das Werkzeug \textsl{Objekte tabellarisch anordnen}.
Danach sollte der Rahmen das gesamte Hauptfenster füllen.
\item Speicher Sie das Formular als \textsl{mainwindow.ui} 
\item Beenden Sie Qt Designer
\end{enumerate} 

Nun können Sie das Formular mit dem PyQt-Interface kompilieren:

\begin{verbatim}
   pyuic4 -o mainwindow_ui.py mainwindow.ui
\end{verbatim}

Dieser Befahler erzeugt den Python Quellcode für das Hauptfenster. Als
nächstes müssen wir den Anwendungscode erstellen, der das leere Fenster
mit einigen Werkzeugen füllt.

\subsection{Erzeugen des Hauptfensters (MainWindow)}

Nun sind wir soweit, um die \textbf{MainWindow}-Klasse zu schreiben, die
die richtige Arbeit macht.
Da die Anwendung einige Zeilen lang ist, werden wir Diesen in Teilen
anschauen. Wir starten mit der Import-Sektion und den
Umgebungseinstellungen:

\begin{verbatim}
1 # Loosely based on:
2 #   Original C++ Tutorial 2 by Tim Sutton
3 #   ported to Python by Martin Dobias
4 #   with enhancements by Gary Sherman for FOSS4G2007
5 # Licensed under the terms of GNU GPL 2
6
7 from PyQt4.QtCore import *
8 from PyQt4.QtGui import *
9 from qgis.core import *
10 from qgis.gui import *
11 import sys
12 import os
13 # Unsere GUI importieren
14 from mainwindow_ui import Ui_MainWindow
15 # Unsere Icons und Ressourcen importieren (icons)
16 import resources
17 
18 # Umgebungsvariable QGISHOME muss auf das Installationsverzeichnis von 0.9 zeigen
19 # bevor die Anwendung installiert werden kann.
20 qgis_prefix = os.getenv('QGISHOME')
\end{verbatim}

Einiges sieht sehr ähnlich dem der Plugins aus, insbesondere die PyQt4 und
QGIS-Importe. Einige Besonderheiten sind der GUI-Import in Zeile 14 und der
Import unserer Ressourcen in Zeile 16.

Unsere Anwendnung muss wissen, wo es die QGIS-Installation finden kann.
Deswegen setzen wir die Umgebunsvariable QGISHOME, die auf das
Installationsverzeichnis von QGIS 0.9.0 zeigt. In Zeile 20 weisen wir
diesen Wert einer Variable zu, damit wir sie später weiterbenutzen können.

Als nächstes erstellen wir unsere \textbf{MainWindow}-Klasse, die alle
Logik unserer Anwendung enthält.
\begin{verbatim}
21 class MainWindow(QMainWindow, Ui_MainWindow):
22 
23   def __init__(self):
24     QMainWindow.__init__(self)
25 
26     # von Qt4 benötigt, um das UI zu initialisieren
27     self.setupUi(self)
28 
29     # Title setzen
30     self.setWindowTitle('FOSS4G2007 Demo App')
31 
32     # Kartenfenster erzeugen
33     self.canvas = QgsMapCanvas()
34     # Hintergrundfarbe auf hellblau sezten
35     self.canvas.setCanvasColor(QColor(200,200,255))
36     self.canvas.enableAntiAliasing(True)
37     self.canvas.useQImageToRender(False)
38     self.canvas.show()
39 
40     # Widget im MainWindows mit einer vertikalen Box ausrichten.
41     #
42     self.layout = QVBoxLayout(self.frame)
43     self.layout.addWidget(self.canvas)
44 
45     # Actions erzeugen und jedes Werkzeug mit der entsprechenden Methode
46     # verbinden
47     self.actionAddLayer = QAction(QIcon(':/foss4g2007/mActionAddLayer.png'),
48     \
49         'Add Layer', self.frame)
50     self.connect(self.actionAddLayer, SIGNAL('activated()'), self.addLayer)
51     self.actionZoomIn = QAction(QIcon(':/foss4g2007/mActionZoomIn.png'), \
52         'Zoom In', self.frame)
53     self.connect(self.actionZoomIn, SIGNAL('activated()'), self.zoomIn)
54     self.actionZoomOut = QAction(QIcon(':/foss4g2007/mActionZoomOut.png'), \
55         'Zoom Out', self.frame)
56     self.connect(self.actionZoomOut, SIGNAL('activated()'), self.zoomOut)
57     self.actionPan = QAction(QIcon(':/foss4g2007/mActionPan.png'), \
58         'Pan', self.frame)
59     self.connect(self.actionPan, SIGNAL('activated()'), self.pan)
60     self.actionZoomFull = QAction(QIcon(':/foss4g2007/mActionZoomFullExtent.png'), \
61         'Zoom Full Extent', self.frame)
62     self.connect(self.actionZoomFull, SIGNAL('activated()'),
63     self.zoomFull)
64 
65     # Werkzeugleiste erzeugen
66     self.toolbar = self.addToolBar('Map')
67     # Add the actions to the toolbar
68     self.toolbar.addAction(self.actionAddLayer)
69     self.toolbar.addAction(self.actionZoomIn)
70     self.toolbar.addAction(self.actionZoomOut);
71     self.toolbar.addAction(self.actionPan);
72     self.toolbar.addAction(self.actionZoomFull);
73 
74     # Kartenwerkzeuge erzeugen
75     self.toolPan = QgsMapToolPan(self.canvas)
76     self.toolZoomIn = QgsMapToolZoom(self.canvas, False) # false = in
77     self.toolZoomOut = QgsMapToolZoom(self.canvas, True) # true = out
\end{verbatim}

Die Zeilen 21 bis 27 sind grundsätzliche Deklarationen und
Initialisierungen des 
\textbf{MainWindow} und die Einstellungen des UserInterface mit der Methode  
\textsl{setupUi}. Das ist für alle Anwendungen nötig.

Als nächstes setzen wir den Titel für die Anwendung, sodass es etwas
Interessanteres als 'MainWindow' (Zeile 30) anzeigt. Wenn das getan ist,
können wir das Benutzerinterface weiter komplettieren. Als wir es im
Designer erstellt haben, haben wir es sehr sparsam gehalten --- lediglich
ein Hauptfenster und einen Rahmen. Wir hätten bereits im Designer ein Menü
und eine Werkzeugleiste erstellen können, wir machen es aber nun direkt in
Python.

In Zeile 33 wir setzen wir das Kartenfenster, die Hintergrundfarbe auf
Hellblau und schalten das Antialiasing ein.
Außerdem nutzen wir nicht QImage zum Rendern (trauen Sie mir an diesem
Punkt!). Um das Kartenfenster sichtbar zu schalten, nutzen wir die
\textsl{show}-Methode.

Das Thema soll in einer vertikalen Box innerhalb des Rahmens ausgerichtet
werden; in Zeile 43 wird das Kartenbild hinzugefügt.

In den Zeilen 48 bis 63 werden die Actions und Verbindungen für die
Werkzeuge in der Werkzeugleiste verdrahtet. Für jedes Werkzeug erzeugen
wir eine \textbf{QAction} mit einem in unserer Ressourcen-Datei
definierten Icon. Dann verbinden wir das \textsl{Aktivierungssignal} vom
Werkzeug zur Methode in unserer Klasse, welche die Action ausführen wird.
Das ist identisch mit dem Beispiel für Plugins.

Wenn wir die Aktionen und Verbindungen verbunden haben, müssen wir sie zu
der Werkzeugleiste hinzufügen. In den Zeilen 66 bis 72 tun wir dies.

Als letztes erzeugen wir 3 Kartenwerkzeuge für die Anwendung (Zeilen
75-77). Wir benutzen die Kartenwerkzeuge in dem Moment, wenn wir die
Methoden definieren, die unsere Anwendung funktional machen. Lassen Sie
uns auf die Methoden für die Kartenwerkzeuge schauen.

\begin{verbatim}
78   # Kartenwerkzeug auf Hereinzoomen stellen
79   def zoomIn(self):
80     self.canvas.setMapTool(self.toolZoomIn)
81 
82   # Kartenwerkzeug auf Herauszoomen stellen
83   def zoomOut(self):
84     self.canvas.setMapTool(self.toolZoomOut)
85 
86   # Kartenwerkzeug auf Pannen stellen
87   def pan(self):
88    self.canvas.setMapTool(self.toolPan)
89 
90   # Kartenwerkzeug auf volle Ausdehnung stellen
91   def zoomFull(self):
92     self.canvas.zoomFullExtent()
\end{verbatim}

Für jedes Kartenwerkzeug müssen wir eine Methode erstellen, die mit der
Verbindung zu den Actions korrespondiert. In den Zeilen 79-88 definieren
wir je eine Methode für die 3 Werkzeuge, die mit der Karte interagieren.
Wenn ein Werkzeug durch Klicken in der Werkzeugleiste aktiviert wird, wird
die korrespondierende Methode aufgerufen, die dem Kartenbild ``mitteilt'',
dass es aktiv ist.
Das aktive Werkzeug entscheidet, was passieren soll, wenn auf der Karte
geklickt wird.

Der Zoom auf die volle Ausdehnung ist nicht wirklich ein
Kartenwerkzeug---es macht seinen Job auch ohne einen Klick auf die Karte.
Wenn es aktiviert wird, rufen wir die Methode
\textsl{zoomFullExtent} des Kartenfenster auf (Zeile 92).
Dies komplettiert die Implementierung von allen Kartenwerkzeugen---außer
dem zum Hinzufügen von Themen. 
Lassen Sie uns dieser Funktionalität als nächstes widmen:

\begin{verbatim}
93   # Ein OGR-Thema hinzufügen
94   def addLayer(self):
95     file = QFileDialog.getOpenFileName(self, 'Open Shapefile', '.', 'Shapefiles
96     (*.shp)')
97     fileInfo = QFileInfo(file)
98 
99     # Layer hinzufügen
100     layer = QgsVectorLayer(file, fileInfo.fileName(), 'ogr')
101
102    if not layer.isValid():
103      return
104
105    # Farbe des Themas auf grau 
106    symbols = layer.renderer().symbols()
107    symbol = symbols[0]
108    symbol.setFillColor(QColor.fromRgb(192,192,192))
109
110    # Thema zur Kartenregistry hinzufügen
111    QgsMapLayerRegistry.instance().addMapLayer(layer);
112
113    # Ausdehnung auf die Themenausdehnung setzen
114    self.canvas.setExtent(layer.extent())
115
116    # Kartenanwendungslayer setzen
117    cl = QgsMapCanvasLayer(layer)
118    layers = [cl]
119    self.canvas.setLayerSet(layers)
\end{verbatim}

In der \textsl{addLayer} Methode benutzen wir den \textbf{QFileDialog} um
den Namen des Shapefiles zu bekommen, das wir laden wollen. Das geschieht
in Zeile 96.
Beachten Sie, dass wir einen ``Filter'' nutzen, damit im Dialog nur die
Dateien mit der Endung \textsl{.shp} angezeigt werden.

In Zeile 97 erzeugen wir ein \textbf{QFileInfo} Objekt aus dem
Shapefile-Pfad.
Nun ist der Layer fertig zum Ergeugen (Zeile 100). 
Wie benutzen das \textbf{QFileInfo} Objekt um den Dateinamen aus dem Pfad
zu extrahieren und setzen es als Name für den neu erzeugten Layer ein. Um
sicherzugehen, dass der Layer auch gültig ist und keine Probleme beim
Laden macht, überprüfen wir dies in Zeile 102. Falls er fehlerhaft ist,
wird abgebrochen und der als fehlerhaft erkannte Layer wird nicht zum
Kartenbild hinzugefügt.

Normalerweise werden Kartenlayer mit einer zufälligen Farbe hinzugefügt.
Hier wollen wir die Farben der Layer verändern, um eine schönere
Darstellung zu erhalten.
Außerdem wissen wir, dass wir den Layer \textsl{world\_borders} zur Karte
hinzufügen wollen; das sieht schöner auf unserem blauen Hintergrund aus.
Um die Farbe zu ändern, müssen wir das Symbol, welches zum Zeichnen
verwendet wird, holen und eine neue Füllfarbe setzen. Das alles wird in in
den Zeilen 106-108 gemacht.

All diese Dinge sind nötig, um einen neuen Layer zur Registrierung
hinzuzufügen. Außerdem sind einige andere wichtige ``Haushaltsdinge''
erledigt worden (Zeilen 111-119). Dies ist der normale Weg um ein Thema
hinzuzufügen. Das Ergebnis sind die Grenzen der Welt auf einem hellblauen
Hintergrund. Das einzige, was evtl. nicht durchgeführt werden muss, ist
das Setzen der Ausdehnung auf den Layer; insbesondere dann, wenn Sie noch
mehrere Themen zu Ihrer Anwendung hinzufügen wollen.

Das ist das Herz der Anwendung und komplettiert gleichzeitig
ihre \textbf{MainWindow}-Klasse.

\subsection{Das Finale!}

Der unten stehende Teil des Codes erzeugt das Objekt
\textbf{QgsApplication} object, setzt die Pfade zur QGIS-Installation,
definiert die Methode \textsl{main} und startet die Anwendung.
Das einzig erwähnenswerte ist noch die Tatsache, dass wir das
Anwendungsfenster zur oberen linken Ecke des Displays verschieben. Wir
könnten auch die Qt API nutzen und das Fenster zentrieren\dots

\begin{verbatim}
120 def main(argv):
121   # erzeuge die Qt-Anwendung
122   app = QApplication(argv)
123 
124   # QGIS-Bibliotheken initialisieren
125   QgsApplication.setPrefixPath(qgis_prefix, True)
126   QgsApplication.initQgis()
127 
128   # Hauptfenster erstellen
129   wnd = MainWindow()
130   # Fenster zur oberen linken Ecke bewegen
131   wnd.move(100,100)
132   wnd.show()
133 
134   # und los!
135   retval = app.exec_()
136   
137   # beenden
138   QgsApplication.exitQgis()
139   sys.exit(retval)
140 
141 
142 if __name__ == '__main__':
143   main(sys.argv)
\end{verbatim}

\subsection{Anwendung ausführen}

Nun sollten wir die Anwendung starten und sehen, was passiert.
Wahrscheinlich haben Sie es, wie fast jeder Entwickler, bereits unterwegs
ausprobiert.

Bevor wir die Anwendung ausführen können, müssen wir einige
Umgebungsvariablen setzen. Unter GNU/Linux oder Mac OS X geht das so:

\begin{verbatim}
export LD_LIBRARY_PATH=$HOME/qgis_09/lib
export PYTHONPATH=$HOME/qgis_09/share/qgis/python
export QGISHOME=$HOME/qgis_09
\end{verbatim}

Für Windows:
\begin{verbatim}
set PATH=C:\qgis;%PATH%
set PYTHONPATH=C:\qgis\python
set QGISHOME=C:\qgis
\end{verbatim}

Im Falle von GNU/Linux oder Mac OS X haben wir angenommen, dass QGIS in
Ihrem Heimatverzeichnis unter \textsl{qgis\_09} installiert wurde. Für
Windows wurde der Installationspfad \textsl{C:\textbackslash qgis} für
QGIS angenommen.

Wenn nun die Anwendung startet, sieht es wie folgt aus:

\begin{figure}[ht]
\begin{center}
  \caption{Starten der neuen Demoanwendung}\label{fig:demo_app_startup}\smallskip
  % FIXME: das folgende Bild ist falsch!!, auch im Masterdokument.
  \includegraphics[scale=0.8]{getdsn}
\end{center}
\end{figure}

Um den Layer \textsl{world\_borders} hinzuzufügen, klicken Sie auf das
Werkzeug \textsl{Thema hinzufügen} und navigiern zum Verzeichnis mit den
Daten.
Wählen sie das Shapefile aus und klicken Sie auf \textsl{Öffnen}, um es
der Karte hinzufügen.
Unsere eigene Farbe wird nun hinzugefügt und das Ergebnis sollte so
aussehen:

\begin{figure}[ht]
\begin{center}
  \caption{Hinzufügen eines Themas zur Demo-Anwendung}\label{fig:demo_app_done}\smallskip
  \includegraphics[scale=0.8]{getdsn}
  % FIXME: das folgende Bild ist falsch!!, auch im Masterdokument.
\end{center}
\end{figure}

Das Erstellen einer PyQGIS-Anwendung ist sehr einfach, wie sie sehen. In
weniger als 150 Zeilen Code haben wir eine Anwendung erstellt, die
Shapefiles laden kann und mit der man auf der Karte navigieren kann. Wenn
Sie ein wenig mit der Karte herumspielen, werden Sie feststellen, dass
noch weitere eingebaute Funktionen funktionieren, wie beispielsweise das
Mausrad-Zoomen und das Verschieben der Karte unter Zuhilfenahme der
\textsl{Leertaste} bei gleichzeitigem Bewegen der Maus.

Einige komplexe Anwendungen wurden bereits mit PyQGIS erzeugt; es werden
stetig mehr. Das ist sehr beachtlich, denn einige dieser Anwendungen
wurden bereits vor dem offiziellen Release der QGIS-Version  0.9.0
veröffentlicht.

\begin{Tip}\caption{\textsc{Dokumentation von PyQGIS}}
\qgistip{Falls Sie ein Plugin oder eine PyQGIS-Anwendung schreiben wollen,
werden Sie nicht umherkommen, die Dokumentation der QGIS API 
(\url{http://qgis.org}) und der PyQt Python Bindungs
(\url{http://www.riverbankcomputing.com/Docs/PyQt4/pyqt4ref.html})
anzuschauen. Beide Dokumentationen stellen Informationen über Klassen und
Methoden zur Verfügung, die Sie benötigen, um Ihre Python-Anwendung zum
Leben zu erwecken.
}
\end{Tip} 
