% vim: set textwidth=78 autoindent:

\section{3. Writing a QGIS Plugin in Python}

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

Σ'αυτό το κεφάλαιο παρέχουμε ένα απλό μάθημα αρχαρίων για τη γραφή ενός QGIS plugin σε Python. Είναι βασισμένο στο workshop “Extending the Functionality of QGIS with Python Plugins” (Επεκτείνοντας τη λειτουργικότητα του QGIS με plugin γραμμένα σε Pyton) που έγινε στο FOSS4G 2008 απο τους  Dr. Marco Hugentobler, Dr. Horst
D\"uster και Tim Sutton. 

Πέρα απο τη γραφή ενός QGIS plugin σε Python, είναι επίσης δυνατό να χρησιμοποιήσουμε PyQGIS απο μία Python κονσόλα γραμμής εντολών η οποία είναι χρήσιμη για debugging ή γραφή ανεξαρτήτων  εφαρμογών σε Python, με το ίδιο τους το περιβάλλον βασισμένο στη λειτουργικότητα της βασικής βιβλιοθήκης του QGIS.

\subsection{3.1 Γιατί Python και σχετικά με την άδεια}

Η Python είναι μία γλώσσα η οποία σχεδιάστηκε με σκοπό να είναι εύκολη για προγραμματισμό. Διαθέτει ένα μηχανισμό για αυτόματη απελευθέρωση μνήμης που δεν χρησιμοποιείται πλέον (συλλέκτης σκουπιδιών – garbage collector). Ένα περαιτέρω πλεονέκτημα είναι οτι πολλά προγράμματα που είναι γραμμένα σε C++ ή Java προσφέρουν τη δυνατότητα γραφής προεκτάσεων (extension) σε Python, π.χ. Open Office ή GIMP. Οπότε είναι μια καλή επένδυση χρόνου η εκμάθηση της Python.

Τα PyQGIS plugin εποφελούνται απο τη λειτουργικότητα των libqgis\_core.so και libqgis\_gui.so. Καθώς τα προαναφερθέντα έχουν εκδοθεί υπο την άδεια GNU GP, τα plugin του QGIS σε Python πρέπει επίσης να έχουν τη GPL άδεια. Αυτό σημαίνει ότι μπορείτε να χρησιμοποιείτε τα plugins σας για οποιοδήποτε σκοπό, ενω δεν είστε ταυτόχρονα υποχρεωμένοι να τα εκδόσετε. Αν όμως τα εκδόσετε εν τέλει, αυτό πρέπει να γίνει υπό τους όρους της GPL άδειας. 

\subsection{3.2 Τι χρειάζεται να εγκατασταθεί για να ξεκινήσετε}

Θα χρειατείτε τις παρακάτω βιβλιοθήκες και προγράμματα για να δημιουργήσετε μόνοι σας plugins με Python στο QGIS:

\begin{itemize}
\item QGIS
\item Python >= 2.5
\item Qt
\item PyQT
\item Εργαλεία ανάπτυξης PyQt
\end{itemize}

Αν χρησιμοποιείτε Linux, υπάρχουν τα δυαδικά πακέτα για όλες τις κύριες διανομές. Για τα Windows, το πρόγραμμα εγκατάστασης του PyQt εμπεριέχει Qt, PyQt και τα εργαλεία ανάπτυξης του PyQt. 

\subsection{3.3 Προγραμματίζοντας ένα απλό PyQGIS plugin σε τέσσερα βήματα.}\label{subsec:pyfoursteps}

Το παράδειγμα του plugin που παρουσιάζεται εδώ είναι εσκεμένα απλό. Προσθέτει ένα κουμπί στην μπάρα των μενού του QGIS. Όταν το κουμπί πατηθεί, ένας διάλογος εμφανίζεται όπου ο χρήστης μπορέι να φορτώσει ένα shape file.

Για κάθε plugin σε Python, ένας αποκλειστικός φάκελος που περιέχει τα αρχεία του plugin πρέπει να δημιουργηθεί. Εξ ορισμου, το QGIS ψάχνει για plugin σε δύο τοποθεσίες: \$QGIS\_DIR/share/qgis/python/plugins και \$HOME/.qgis/python/plugins.
Σημειώστε ότι τα plugins που εγκαθίστανται σε μετέπειτα είναι ορατά μόνο απο ένα χρήστη. 

\minisec{Βήμα 1ο: Κάντε το διαχειριστή του plugin (Plugin Manager) να αναγνωρίζει το plugin}

Κάθε plugin της Python εμπεριέχεται μέσα σε ένα δικό του κατάλογο. Όταν το QGIS ξεκινάει θα ελέγξει κάθε συγκεκριμένο υποκατάλογο του λειτουργικου συστήματος (OS) και θα ξεκινήσει όποια plugin βρεί.  

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

Αφού τελειώσει, το plugin θα εμφανιστεί στο Διαχειριστη Plugin (Plugin Manager)
\dropmenuopttwo{mActionShowPluginManager}{Plugin Manager...}

Για να παρέχει τις απαραίτητες πληροφορίες για το QGIS, το plugin χρειάζεται να εφαρμόσει τις μεθόδους  \method{name()}, \method{description()}, \method{version()},
\method{qgisMinimumVersion()} και \method{authorName()} οι οποίες επιστρέφουν περιγραφικούς χαρακτήρες.
Η μέθοδος \method{qgisMinimumVersion()} θα έπρεπε να επιστρέφει μια απλή μορφή, για παράδειγμα “1.0”. θα έπρεπε να επιστρέφει μια απλή μορφή, για παράδειγμα “1.0” \method{classFactory(QgisInterface)} η οποία καλείται απο το Διαχειριστή plugin να δημιουργήσει ένα αντικείμενο (instance) του plugin. H παράμετρος του τύπου QgisInterface χρησιμοποιείται απο το plugin για να έχει πρόσβαση σε λειτουργίες της μεταβλητής του QGIS. Θα δουλέψουμε με αυτό το αντικείμενο στο δεύτερο βήμα. 

Σημειώστε ότι σε αντίθεση με άλλες γλώσσες προγραμματισμού η δημιουργία «εσοχών» είναι πολύ σημαντική. Ο μεταφραστής του Python δείχνει λάθος αν δεν είναι σωστή. 

Για το plugin μας δημιουργούμε το φάκελο 'foss4g\_plugin' μέσα στο 
\filename{\$HOME/.qgis/python/plugins}. Μετά προσθέτουμε δύο νέα αρχεία κειμένου σε αυτό το φάκελο,, \filename{foss4gplugin.py} και \filename{\_\_init\_\_.py}.

Το αρχέιο \filename{foss4gplugin.py} εμπεριέχει την παρακάτω κλάση του plugin: 

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

Το αρχείο \filename{\_\_init\_\_.py} περιέχει τις μεθόδους \method{name()},
\method{description()}, \method{version()}, \method{qgisMinimumVersion()}
και \method{authorName()} και \method{classFactory}. Καθώς δημιουργούμε ένα καινουργιο αντικείμενο της κλάσης του plugin χρειάζεται να εισάγουμε τον κώδικα της παρακατω κλάσης:

\begin{verbatim}
# -*- coding: utf-8 -*-
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):
    from foss4gplugin import FOSS4GPlugin
    return FOSS4GPlugin(iface)
\end{verbatim}

Σε αυτό το σημείο, το plugin έχει την απαραίτητη υποδομή να εμφανιστεί στον Διαχειριστή Plugin \dropmenuopttwo{mActionShowPluginManager}{Plugin Manager...} έτσι ώστε να φορτωθέι ή όχι. 

\minisec{Βήμα 2ο: Δημιουργείστε ένα εικονίδιο για το plugin.}

Για να κάνουμε το γραφικό εικονίδιο διαθέσιμο για το πρόγραμμα μας, χρειαζόμαστε το “υποτιθέμενο” πηγαίο αρχείο. Στο πηγαίο αρχείο, το γραφικό περιέχεται σε δεκαεξαδική μορφή (hexadecimal). Ευτυχώς δεν χρειάζεται να ανησυχούμε για την απεικόνιση του γιατί χρησιμοποιούμε τον συντάκτη (compiler) pyrcc, ένα εργαλείο που διαβάζει το αρχείο \filename{resources.qrc} και δημιουργεί ένα πηγαίο (resource) αρχείο.

Το αρχείο \filename{foss4g.png} και το \filename{resources.qrc} που χρησιμοποιούμε σε αυτό το workshop, μπορείτε να τα κατεβάσετε απο: \url{http://karlinapp.ethz.ch/python\_foss4g}, μπορείτε επίσης να χρησιμοποιήσετε το δικό σας εικονίδιο αν προτιμάτε, απλα πρέπει να σιγουρευτείτε ότι θα ονομάζεται
\filename{foss4g.png}. Μετακινήστε αυτά τα δύο αρχεία μέσα στον κατάλογο του plugin στο παράδειγμα: 
\filename{\$HOME/.qgis/python/plugins/foss4g\_plugin} και εισάγετε: \filename{pyrcc4 -o
resources.py resources.qrc}.

\minisec{Βήμα 3ο: Προσθέστε ένα κουμπί και ένα μενού}

Σε αυτό το κεφάλαιο, εφαρμόζουμε το περιεχόμενο των μεθόδων \method{initGui()} και
\method{unload()}. Χρειαζόμαστε μία μεταβλητή της κλάσης\classname{QAction} που εκτελεί τη μέθοδο
\method{run()} του plugin. Με το αντικείμενο action, μετά μπορούμε να αναπαράγουμε την καταχώρηση του μενού και το κουμπί: 

\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{Βήμα 4ο: Φορτώστε ένα επίπεδο απο ένα shape file.}

Σε αυτό το βήμα εφαρμόζουμε την πραγματική λειτουργικότητα του plugin στη μέθοδο
\method{run()}. Η Qt4 μέθοδος \method{QFileDialog::getOpenFileName}
ανοίγει ένα πλαίσιο διαλόγου και επιστρέφει τη διαδρομή για το επιλεγμένο αρχείο. Αν ο χρήστης ακυρώσει το διάλογο, η διαδρομή είναι ένα κενό αντικείμενο, το οποίο ελέγχουμε. Μετά καλούμε τη μέθοδο \method{addVectorLayer} του αντικειμένου που φορτώνει το επίπεδο πληροφορίας. Η μέθοδος χρειάζεται μόνο τρία αντικέιμενα: τη διαδρομή του αρχείου, το όνομα του επιπέδου το οποίο θα φαίνεται στο υπόμνημα και το όνομα του παροχέα δεδομένων. Για τα shapefiles υπάρχει το “org” επειδή το QGIS εσωτερικά χρησιμοποιεί την OGR βιβλιοθήκη για να έχει προσβαση στα 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}

\minisec{Το σήμα’activated ()’ στα plugins της Python αποφεύγεται}

Όταν γράφετε το plugin σας σε python θυμηθείτε ότι το σήμα \texttt{activated ()}
που χρησιμοποιείται για να δώσει σήμα στο plugin ότι έχει ενεργοποιηθεί, αποφεύγεται. Το σήμα έχει εξαφανισθεί απο το Qt4 και αν το Qt4 δεν έχει συνταχθεί (compiled) με το Qt3 και αντίστροφα, είναι απλά ανύπαρκτο, έτσι δεν μπορεί να καλεσθεί καθόλου plugin.

Αντικαταστήστε το \texttt{activated ()} με \texttt{triggered ()}.

\subsection{3.4 Ανεβάζοντας το plugin στο αποθετήριο}

Αν έχετε γράψει ένα plugin που θεωρείτε ότι έιναι χρήσιμο και θέλετε να το μοιραστείτε μαζί με άλλους χρήστες είστε ευπρόσδεκτοι να το ανεβάσετε στο συνησφερόμενο απο τους χρήστες αποθετήριο του QGIS.
\begin{itemize}
\item Ετοιμάστε έναν κατάλογο του plugin περιέχοντας μόνο τα απαραίτητα αρχεία (σιγουρευτείτε ότι δεν υπάρχουν συνταγμένα αρχεία .pyc , κατάλογοι .svn κλπ)
\item Φτιάξτε ένα συμπιεσμένο αρχείο του, συμπεριλαμβάνοντας τον κατάλογο. Σιγουρευτείτε ότι το όνομα του συμπιεσμένου αρχείου είναι ακριβώς το ίδιο όπως του καταλόγου μέσα (εκτός απο την κατάληξη .zip φυσικά), αν το πρόγραμμα εγκατάστασης του plugin δεν θα μπορεί να συσχετίσει τα διαθέσιμα plugin με τις τοπικά εγκατεστημένες μεταβλητές του.
\item Ανεβάστε το στο αποθετήριο: \url{http://pyqgis.org/admin/contributed} (θα χρειαστεί να εγγραφείτε την πρώτη φορά. Παρακαλούμε δώστε προσοχή όταν συμπληρώνετε τη φόρμα. Το πεδίο της Έκδοσης Αριθμού είναι ιδιαίτερα σημαντικό, και αν συμπληρωθεί λανθασμένα μπορεί να μπερδέψει το πρόγραμμα εγκατάστασης του plugin και να προκαλέσει λανθασμένες ειδοποιήσεις διθέσιμων ενημερώσεων. 
\end{itemize}

\subsection{3.5Περαιτέρω πληροφορίες}

Όπως βλέπετε χρειάζεστε πληροφορίες απο διάφορες πηγές για να γράψετε ένα PyQGIS plugin. Όσοι γράφουν plugin πρέπει να γνωρίζουν Python, το περιβάλλον (interface) του QGIS plugin, όπως επίσης και τις Qt4 κλάσεις και τα εργαλεία.  Στην αρχή το καλύτερο είναι να μαθαίνουμε απο παραδείγματα και να αντιγράφουμε τους μηχανισμούς υπαρχόντων plugin. Χρησιμοποιώντας το πρόγραμμα εγκατάστασης του QGIS plugin, το οποίο απο μόνο του είναι ένα plygin Python, είναι δυνατόν να κατεβάσουμε πολλά υπάρχοντα Python plugin και να μελετήσουμε τη συμπεριφορά τους. Είναι επίσης δυνατόν να χρησιμοποιήσουμε τον on-line Python plugin δημιουργο για να δημιοθργήσουμε μια βάση απο την οποία θα δουλέψουμε. Αυτό το on-line εργαλείο θα σας βοηθήσει να γράψετε ένα μηδαμινό plugin το οποίο μπορείτε να χρησιμοποιήσετε ως σημείο αναφοράς για την περαιτέρω εργασία σας στην ανάπτυξη. Το αποτέλεσμα είναι ένα έτοιμο προς χρηση QGIS 1.0 plugin
 το οποίο εφαρμόζει έναν άδειο διάλογο με ΟΚ και Close κουμπιά. Είναι διαθέσιμο εδώ: \url{http://www.pyqgis.org/builder/plugin_builder.py}

Υπάρχει μία συλλογή απο on-line βιβλιογραφία η οποία μπορεί να φανέι χρήσιμη για τους προγραμματιστές PyQGIS:
 
\begin{itemize}
\item QGIS wiki: \url{http://wiki.qgis.org/qgiswiki/PythonBindings}
\item QGIS βιβλιογραφία: \url{http://doc.qgis.org/index.html}
\item Qt βιβλιογραφία: \url{http://doc.trolltech.com/4.3/index.html}
\item PyQt: \url{http://www.riverbankcomputing.co.uk/pyqt/}
\item Μαθήματα Python: \url{http://docs.python.org/}
\item Ένα βιβλίο σχετικά με τα Desktop GIS και QGIS. Περιέχει ένα κεφάλαιο σχετικά με προγραμματισμό PyQGIS plugin: \url{http://www.pragprog.com/titles/gsdgis/desktop-gis} 
\end{itemize}

Μπορείτε επίσης να γράψετε plugins για το QGIS σε C++. Ανατρέξατε το κεφάλαιο \ref{cpp_plugin} για περισσότερες πληροφορίες σχετικά με αυτό. 

