Δημιουργία ενός πρόσθετου στην Python.

Προειδοποίηση

A new version of this tutorial is available at Building a Python Plugin (QGIS3)

Τα πρόσθτε είναι ένας υπέροχος τρόπος να επεκτίνουμε την λειτουργικότητα του QGIS. Μπορείτε να γράψετε πρόσθετα χρησιμοποιώντας Python τα οποία μπορούν να είναι από την εισαγωγή ενός απλού κουμπιού μέχρι την δημιουργία μιας εκλεπτισμένης εργαλειοθήκης. Αυτή η εργασία θα παραθέσει την διαδικασία η οποία απαιτείται στην ρύθμιση του περιβάλλοντος ανάπτυξης σας, σχεδιάζοντας την διεπαφή με χρήστη για ένα πρόσθετο και γράφοντας κώδικα με σκοπό να αλληλεπιδράσετε με το QGIS. Παρακαλώ διαβάστε την άσκηση Ξεκινώντας προγραμματισμό με την Python για να εξοικειωθείτε με τα βασικα βήματα.

Περίληψη του αντικειμένου.

Θα δημιουργήσουμε ένα απλό πρόσθετο που λέγεται «Αποθήκευση χαρακτηριστικών» το οποίο θα επιτρέπει στους χρήστες να πάρουν ένα διανυσματικό στρώμα και να γράψουν τα χαρακτηριστικά του σε ένα αρχείο CSV.

Πάρτε τα εργαλεία

Δημιουργός Qt

To Qt <http://www.qt.io/>`_είναι μια πλατφόρμα ανάπτυξης λογισμικού η οποία χρησιμοποιείται για να δημιουργεί εφαρμογέ για Windows, Mac, Linux καθώς και για διάφορα λειτουργικά συστήματα για κινητά. Το ίδιο το QGIS είναι γραμμένο χρησιμοποιώντας την πλατφόρμα Qt. Για την ανάπτυξη πρόσθετων, θα χρησιμοποιήσουμε μια εφαρμογή που λέγεται `Qt Creator για να σχεδιάσουμε την διεπαφή για το πρόσθετό μας.

Μεταφορτώστε και εγκαταστείστε το λογισμικό Qt Creator από το SourgeForge

Δεσμοι Python για το Qt

Δεδομένου οτι αναπτύσουμε ένα πρόσθετο στην Python, χρειάζεται να εγκαταστήσουμε τους δεσμού python για το Qt. Η μέθοδος για την εγκατάσταση αυτή θα εξαρτηθεί από την πλατφόρμα που χρησιμοποιείτε. Για την δημιουργία των πρόσθετων θα χρειαστούμε το εργαλείο γραμμής εντολών pyrcc4.

Windows

Κατεβάστε το OSGeo4W network installer και επιλέξτε το Express Desktop Install. Εγκαταστείστε το πακέτο QGIS. Μετά την εγκατάσταση, θα έχετε πρόσβαση στο εργαλείο pyrcc4 μέσω του OSGeo4W Shell.

Mac

Εγκαταστείστε τον διαχειριστή πακέτων Homebrew <http://brew.sh>». Εγκαταστείστε το πακέτο ``PyQt` τρέχοντας την παρακάτω εντολή.

brew install pyqt

Linux

Λαμβάνοντας υπόψην την διανομή σας, βρείτε και εγκαταστείστε το πακέτο python-qt4. Στα Ubuntu και τις διανομές Debian, μπορείτε να χρησιμοποιείσετε την παρακάτω εντολή.

sudo apt-get install python-qt4

Ενας επεξεργαστής κειμένου ή ένα Python IDE

Κάθε είδους ανάπτυξη λογισμικού χρειάζεται έναν καλό επεξεργαστή κειμένου. Αν έχετε ήδη κάποιον επεξεργαστή κειμένου ή ένα IDE (Ενσωματωμένο περιβάλλον ανάπτυξης), μπορείτε να το χρησιμοποιήσετε για αυτή την άσκηση. Σε διαφορετική περίπτωση, κάθε πλατφόρμα σας δίνει την δυνατότητα να χρησιμοποιήσετε μια μεγάλη ποικιλία από δωρεάν ή επι πληρωμή επεξεργαστές κειμένων. Επιλέξτε έναν που να ταιριάζει με τις ανάγκες σας.

Αυτή η άσκηση χρησιμοποιεί τον επεξεργαστή Notepad++ για Windows

Windows

To Notepad++ είναι ένας καλός, δωρεάν επεξεργαστής για Windows. Κατεβάστε και εγκαταστείστε τον επεξεργαστή Notepad++ editor.

Σημείωση

Αν χρησιμοποιείτε το Notepad++, φροντίστε να έχετε επιλέξει το Replace by space στο Settings ‣ Preferences ‣ Tab Settings. Η Python είναι αρκετά ευαίσθητη όταν υπάρχουν κενά και αυτή η ρύθμιση θα φροντίσει ώστε οι καρτέλες και τα κενά να διαχειρίζονται με τον σωστό τρόπο.

Δημιουργία πρόσθετου

Υπάρχει ένα πολύ χρήσιμο πρόσθετο στο QGIS που ονομάζεται Plugin Builder το οποίο δημιουργεί όλα τα κατάλληλα αρχεία και το πρότυπο του κώδικα για το πρόσθετο. Βρείτε και εγκαταστείστε το πρόσθετο Plugin Builder. Βλέπε Χρησιμοποιώντας Πρόσθετες Λειτουργίες για περισσότερες λεπτομέρειες για το πως να εγκαταστείσετε ένα πρόσθετο.

Μεταφόρτωση πρόσθετου

Αυτός είναι ένας ακόμα βοηθός για τα πρόσθετα ο οποίος επιτρέπει την επαναλαμβανόμενη ανάπτυξη πρόσθετων. Χρησιμοποιώντας αυτό το πρόσθετο, μπορείτε να αλλάξετε τον κώδικα του πρόσθετού σας και να κάνετε τις αλλαγές να αντικατοπτρίζονται στο QGIS χωρίς να πρέπει να κάνετε επανεκκίνηση στο QGIS κάθε φορά. Βρείτε και εγκαταστείστε το πρόσθετο Plugin Reloader. Δείτε το Χρησιμοποιώντας Πρόσθετες Λειτουργίες για περισσότερες λεπτομέρειες για την εγκατάσταση πρόσθετων.

Σημείωση

Ο επαναφορτωτής πρόσθετων είναι ένα πειραματικό πρόσθετο. Φροντίστε να έχετε επιλέξει το Show also experimental plugins στις ρυθμίσεις Plugin Manager αν δεν μπορείτε να το βρείτε.

Διαδικασία

  1. Ανοίξτε το QGIS. Πηγαίνετε στο Plugins ‣ Plugin Builder ‣ Plugin Builder.

../_images/1168.png
  1. Θα δείτε το παράθυρο διαλόγου QGIS Plugin Builder με μία φόρμα. Μπορείτε να ολοκληρώσετε την φόρμα αυτή με λεπτομέρεις που να σχετίζονται με το πρόσθετό σας. Η Class name θα είναι το όνομα από την κατηγορία της Python που περιέχει την λογική για το πρόσθετο. Αυτό θα είναι επίσης και το όνομα του φακέλου που θα περιέχει όλα τα αρχεία του πρόσθετου. Εισάγετε ως όνομα για την κατηγορία το SaveAttributes. Προσθέστε μια περιγραφή στο πεδίο Description. Το Module name θα είναι το όνομα του κεντρικού αρχείου της python για το πρόσθετο. Εισάγετε το ως save_attributes. Αφήστε τους αριθμούς της έκδοσης ίδιους. Η τιμή του Text for menu item θα είναι ο τρόπος με τον οποίο οι χρήστες θα βρίσκουν το πρόσθετό σας στο μενού του QGIS. Εισάγετέ το ως Save Attributes as CSV. Εισάγετε το όνομά σας και την διεύθυσνη του ηλεκτρονικού σας ταχυδρομείου στα κατάλληλα πεδία. Το πεδίο Menu θα καθορίσει την τοποθεσία του πρόσθετού σας όταν αυτό θα προστεθεί στο QGIS. Λαμβάνοντας υπόψιν οτι το πρόσθετό σας είναι για διανυσματικά δεδομένα, επιλέξτε «Vector. Κάντε κλικ στο κουτί Flag the plugin as experimental στο κάτω μέρος της οθόνης. Κάντε κλικ στο OK.

../_images/2138.png
  1. Έπειτα θα σας ζητηθεί να επιλέξετε ένα φάκελο για το πρόσθετό σας. Θα χρειαστεί να μεταφερθείτε στον φάκελο με τα QGIS python πρόσθετα στον υπολογιστή σας και να επιλέξετε Select Folder. Ενας τυπικός φάκελος .qgis2/ βρίσκεται στον βασικό σας φάκελο. Η θέση του φακέλου plugin εξαρτάται από την πλατφόρμα που χρησιμοποιείτε και είναι συνήθως : (Replace username with your login name)

Windows

c:\Users\username\.qgis2\python\plugins

Mac

/Users/username/.qgis2/python/plugins

Linux

/home/username/.qgis2/python/plugins
../_images/379.png
  1. Θα δείτε ένα παράθυρο το οποίο θα σας επιβεβαιώνει οτι το πρότυπο του πρόσθετου σας έχει δημιουργηθεί. Συγκρατήστε την διαδρομή για τον φάκελο του πρόσθετου,

../_images/447.png
  1. Πριν μπορέσουμε να χρησιμοποιήσουμε το πρόσφατα δημιουργημένο πρόσθετο, χρειάζεται να συντάξουμε το αρχείο resources.qrc το οποίο δημιουργήθηκε από το Plugin Builder. Τρέξτε το OSGeo4W Shell σε Windows ή σε ένα τερματικό σε Mac ή Linux.

../_images/541.png
  1. Μεταφερθείτε στον φάκελο του πρόσθετου όπου το εξαγόμενο του Plugin Builder δημιουργήθηκε. Μπορείτε να χρησιμοποιήσετε την εντολή cd ακολουθούμενη από την διαδρομή για τον φάκελο.

cd c:\Users\username\.qgis2\python\plugins\SaveAttributes
../_images/640.png
  1. Αφού μεταφερθείτε στον φάκελο, πληκτρολογίστε make. Αυτό θα τρέξει την εντολή pyrcc4 την οποία εγκαταστήσαμε ως μέρος του Qt bindings για την python.

make
../_images/740.png
  1. Τώρα είμαστε έτοιμοι να δούμε το νέο πρόσθετο που δημιουργήσαμε. Κλείστε το QGIS και ξανατρέξτε το πάλι. Πηγαίνετε στο Plugins ‣ Manage and Install plugins και ενεργοποιήστε το plugin Save Attributes" από την καρτέλα  :guilabel:`Installed`. Θα παρατηρήσετε οτι υπάρχει ένα νέο εικονίδιο στην γραμμή εργαλείων και μια νέα εγγραφή κάτω από το μενού  :menuselection:`Vector --> Save Attributes --> Save Attributes as CSV. Επιλέξτε να τρέξετε το παράθυρο διαλόγου για τα πρόσθετα.

../_images/839.png
  1. Θα παρατηρήσετε ένα νέο κενό παράθυρο διαλόγου με όνομα Save Attributes. Κλείστε αυτό το παράθυρο διαλόγου.

../_images/940.png
  1. Τώρα θα σχεδιάσουμε το δικό μας παράθυρο διαλόγου και θα προσθέσουμε μερικά στοιχεία στην διεπαφή χρήστη. Ανοίξτε το πρόγραμμα Qt Creator και πηγαίνετε στο File –> Open File or Project….

../_images/1047.png
  1. Μεταφερθείτε στον φάκελο του πρόσθετου και επιλέξτε το αρχείο save_attributes_dialog_base.ui. Πατήστε Open.

../_images/1169.png
  1. Τώρα θα δείτε ένα κενό παράθυρο διαλόγου από το πρόσθετο. Μπορείτε να επιλέξετε και να σύρετε στοιχεία από το παράθυρο στα αριστερά, στο παράθυρο διαλόγου. Θα προσθέσουμε ενός είδους Widget:guilabel:Combo Box type of Input Widget. Σύρετέ το στο παράθυρο διαλόγου του πρόσθετου.

../_images/1250.png
  1. Αλλάξτε το μέγεθος στο παράθυρο. Τώρα σύρετε στο παράθυρο διαλόγου ένα :Label type Display Widget.

../_images/1348.png
  1. Πατήστε σην ετικέτα κείμενο και εισάγετε το Select a layer.

../_images/1445.png
  1. Σώστε αυτό το αρχείο πηγαίνοντας στο File ‣ Save save_attributes_dialog_base.ui. Συγκρατήστε οτι το όνομα από το αντικείμενο- κουτί είναι comboBox. Για να αλληλεπιδράσετε με το αντικείμενο χρησιμοποιώντας εντολές στην python, θα πρέπει να το καλείτε με το όνομα αυτό.

../_images/1541.png
  1. Ας τρέξουμε το πρόσθετο ώστε να μπορούμε να δούμε τις αλλαγές στο παράθυρο διαλόγου. Πηγαίνετε στο Plugin ‣ Plugin Reloader ‣ Choose a plugin to be reloaded.

../_images/1639.png
  1. Επιλέξτε SaveAttributes από το παράθυρο διαλόγου Configure Plugin reloader.

../_images/1737.png
  1. Τώρα πατήστε στο κουμπί Save Attributes as CSV. Θα δείτε ένα καινούριο σχεδιασμένο παράθυρο διαλόγου.

../_images/1834.png
  1. Ας προσθέσουμε τώρα λογική στο πρόσθετο το οποίο θα γεμίσει το κουτί με στρώματα τα οποία φορτώνονται στο QGIS. Πηγαίνετε στον φάκελο του πρόσθετου και φορτώστε το αρχείο save_attributes.py σε έναν επεξεργαστή κειμένου. Μεταφερθείτε προς τα κάτω και βρείτε την μέθοδο run(self). Αυτή η μέθοδος θα καλείται όταν πατάτε το κουμπί στην γραμμή εργαλείων ή επιλέγετε το πρόσθετο από το μενού. Προσθέστε τον παρακάτω κώδικα στην αρχή της μεθόδου. Αυτός ο κώδικας παίρνει όλα τα στρώματα τα οποία είναι φορτωμένα στο QGIS και τα προσθέτει στο αντικείμενο κουτί από το παράθυρο διαλόγου του πρόσθετου.

layers = self.iface.legendInterface().layers()
layer_list = []
for layer in layers:
     layer_list.append(layer.name())
     self.dlg.comboBox.addItems(layer_list)
../_images/1925.png
  1. Πίσω στο κύριο παράθυρο του QGIS, φορτώστε το πρόσθετο πηγαίνοντας στο Plugins ‣ Plugin Reloader ‣ Reload plugin: SaveAttributes. Εναλλακτικά, μπορείτε απλά να πατήσετε F5. Για να δοκιμάσετε την λειτουργία, θα πρέπει να προσθέσετε μερικά στρώματα στο QGIS. Αφού προσθέσετε τα δεδομένα, τρέξτε το πρόσθετο πηγαίνοντας στο Vector ‣ Save Attributes ‣ Save Attributes as CSV.

../_images/2022.png
  1. Θα δείτε οτι το κουτί είναι τώρα γεμάτο με τα ονόματα των στρωμάτων τα οποία έχετε φορτώσει στο QGIS.

../_images/2139.png
  1. Ας προσθέσουμε μερικά στοιχεία διεπαφής χρήστη ακόμα. Πηγαίνετε πάλι στο Qt Creator" και φορτώστε το αρχείο ``save_attributes_dialog_base.ui. Προσθέστε μια «Ετικέτα» Display Widget και αλλάξτε το κείμενο στο Select output file. Προσθέστε ένα LineEdit` Input Widget το οποίο θα δείχνει την διεύθυσνη του εξαγόμενου αρχείου που έχει επιλεχθεί από τον χρήστη. Μετά, προσθέστε ένα κουμπί Push Button Button και αλλάξτε την ετικέτα του σε .... Σημειώστε οτι τα ονόματα των αντικειμένων από τα widget καθώς θα πρέπει να τα χρησιμοποιήσετε για να αλληλεπιδράσετε μαζί τους. Σώστε το αρχείο.

../_images/2224.png
  1. Τώρα θα προσθέσουμε κώδικα στην python μέσω του οποίου θα ανοίγει ένας περιηγητής αρχείων όταν ο χρήστης θα πατάει το κουμπί ... και να εμφανίζει την επιλεγμένη διαδρομή σε μια γραμμή κειμένου στο widget. Ανοίξτε το αρχείο save_attributes.py σε έναν επεξεργαστή κειμένου. Προσθέστε το QFileDialog στην λίστα με τα εισαγόμενα στην κορυφή του αρχείου

../_images/2321.png
  1. Προσθέστε μια νέα μέθοδο που λέγεται select_output_file χρησιμοποιώντας τον παρακάτω κώδικα. Αυτός ο κώδικας θα ανοίγει έναν περιηγητή και θα εμφανίζει στην γραμμή του widget την διαδρομή του αρχείου που έχει επιλέξει ο χρήστης.

def select_output_file(self):
    filename = QFileDialog.getSaveFileName(self.dlg, "Select output file ","", '*.txt')
    self.dlg.lineEdit.setText(filename)
../_images/2421.png
  1. Τώρα πρέπει να προσθέσετε κώδικα ώστε όταν το κουμπί ενεργοποιείται, να καλείται η μέθοδος select_output_file. Μεταφερθείτε προς τα πάνω στην μέθοδο __init__ και προσθέστε τις παρακάτω γραμμές στο τέλος της. Αυτός ο κώδικάς θα σβήνει το κείμενο που είχε φορτωθεί προηγουμένως (αν υπάρχει) στην γραμμή του widget και επίσης θα συνδέει την μέθοδο select_output_file με το σήμα που δίνει το «κλικ» όταν ο χρήστης πατάει το κουμπί στο widget.

self.dlg.lineEdit.clear()
self.dlg.pushButton.clicked.connect(self.select_output_file)
../_images/2520.png
  1. Πίσω στο QGIS, φορτώστε το πρόσθετο και ανοίξτε το παράθυρο διαλόγου Save Attributes`. Αν όλα έχουν πάει καλά, θα είστε σε θέση να κάνετε κλικ στο κουμπί ... και επιλέξτε το εξαγόμενο αρχείο κειμένου από τον δίσκο σας.

../_images/2618.png
  1. Όταν πατάτε το OK στο παράθυρο διαλόγου του πρόσθετου, δεν συμβαίνει τίποτα. Αυτό συμβαίνει επειδή δεν έχετε προσθέσει λογική ώστε να τραβάτε τα χαρακτηριστικά της πληροφορίας από το στρώμα και να τα γράφετε στο αρχείο κειμένου. Τώρα έχουμε όλα τα κομμάτια στην θέση τους για να ξεκινήσουμε. Βρείτε το σημείο στην μέθοδο run που λέει pass. Αντικαταστήστε το με τον κώδικα παρακάτω. Η εξήγηση για αυτόν τον κώδικα βρίσκεται στο Ξεκινώντας προγραμματισμό με την Python.

filename = self.dlg.lineEdit.text()
output_file = open(filename, 'w')

selectedLayerIndex = self.dlg.comboBox.currentIndex()
selectedLayer = layers[selectedLayerIndex]
fields = selectedLayer.pendingFields()
fieldnames = [field.name() for field in fields]

for f in selectedLayer.getFeatures():
    line = ','.join(unicode(f[x]) for x in fieldnames) + '\n'
    unicode_line = line.encode('utf-8')
    output_file.write(unicode_line)
output_file.close()
../_images/2717.png
  1. Τώρα το πρόσθετό μας είναι έτοιμο. Μεταφορτώστε το πρόσθετο και δοκιμάστε το. Θα δείτε οτι το εξαγόμενο αρχείο που έχετε επιλέξει έχει όλα τα χαρακτηριστικά από το διανυσματικό στρώμα. Μπορείτε να συμπιέσετε τον φάκελο του πρόσθετου και να το μοιραστείτε με άλλους χρήστες. Αυτού μπορούν να αποσυμπιέσουν τα περιεχόμενα του φακέλου του πρόσθετου και να δοκιμάσουν το πρόσθετό σας. Αν αυτό είναι ένα πραγματικό πρόσθετο, μπορείτε να το ανεβάσετε στο αποθετήριο πρόσθετων του QGIS <https://plugins.qgis.org/>`_ ώστε όλοι οι χρήστες τους QGIS να είναι σε θέση να βρούν και να κατεβάσουν το πρόσθετό σας.

Σημείωση

Αυτό το πρόσθετο είναι για εκπαιδευτικούς λόγους μόνο. Μην το δημοσιεύσετε ή ανεβάσετε το πρόσθετο αυτό στο αποθετήριο του QGIS.

Παρακάτω παραθέτεται το πλήρες αρχείο save_attributes.py ως αναφορά.

# -*- coding: utf-8 -*-
"""
/***************************************************************************
 SaveAttributes
                                 A QGIS plugin
 This plugin saves the attribute of the selected vector layer as a CSV file.
                              -------------------
        begin                : 2015-04-20
        git sha              : $Format:%H$
        copyright            : (C) 2015 by Ujaval Gandhi
        email                : ujaval@spatialthoughts.com
 ***************************************************************************/

/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version.                                   *
 *                                                                         *
 ***************************************************************************/
"""
from PyQt4.QtCore import QSettings, QTranslator, qVersion, QCoreApplication
from PyQt4.QtGui import QAction, QIcon, QFileDialog
# Initialize Qt resources from file resources.py
import resources_rc
# Import the code for the dialog
from save_attributes_dialog import SaveAttributesDialog
import os.path


class SaveAttributes:
    """QGIS Plugin Implementation."""

    def __init__(self, iface):
        """Constructor.

        :param iface: An interface instance that will be passed to this class
            which provides the hook by which you can manipulate the QGIS
            application at run time.
        :type iface: QgsInterface
        """
        # Save reference to the QGIS interface
        self.iface = iface
        # initialize plugin directory
        self.plugin_dir = os.path.dirname(__file__)
        # initialize locale
        locale = QSettings().value('locale/userLocale')[0:2]
        locale_path = os.path.join(
            self.plugin_dir,
            'i18n',
            'SaveAttributes_{}.qm'.format(locale))

        if os.path.exists(locale_path):
            self.translator = QTranslator()
            self.translator.load(locale_path)

            if qVersion() > '4.3.3':
                QCoreApplication.installTranslator(self.translator)

        # Create the dialog (after translation) and keep reference
        self.dlg = SaveAttributesDialog()

        # Declare instance attributes
        self.actions = []
        self.menu = self.tr(u'&Save Attributes')
        # TODO: We are going to let the user set this up in a future iteration
        self.toolbar = self.iface.addToolBar(u'SaveAttributes')
        self.toolbar.setObjectName(u'SaveAttributes')
        
        self.dlg.lineEdit.clear()
        self.dlg.pushButton.clicked.connect(self.select_output_file)
        

    # noinspection PyMethodMayBeStatic
    def tr(self, message):
        """Get the translation for a string using Qt translation API.

        We implement this ourselves since we do not inherit QObject.

        :param message: String for translation.
        :type message: str, QString

        :returns: Translated version of message.
        :rtype: QString
        """
        # noinspection PyTypeChecker,PyArgumentList,PyCallByClass
        return QCoreApplication.translate('SaveAttributes', message)


    def add_action(
        self,
        icon_path,
        text,
        callback,
        enabled_flag=True,
        add_to_menu=True,
        add_to_toolbar=True,
        status_tip=None,
        whats_this=None,
        parent=None):
        """Add a toolbar icon to the toolbar.

        :param icon_path: Path to the icon for this action. Can be a resource
            path (e.g. ':/plugins/foo/bar.png') or a normal file system path.
        :type icon_path: str

        :param text: Text that should be shown in menu items for this action.
        :type text: str

        :param callback: Function to be called when the action is triggered.
        :type callback: function

        :param enabled_flag: A flag indicating if the action should be enabled
            by default. Defaults to True.
        :type enabled_flag: bool

        :param add_to_menu: Flag indicating whether the action should also
            be added to the menu. Defaults to True.
        :type add_to_menu: bool

        :param add_to_toolbar: Flag indicating whether the action should also
            be added to the toolbar. Defaults to True.
        :type add_to_toolbar: bool

        :param status_tip: Optional text to show in a popup when mouse pointer
            hovers over the action.
        :type status_tip: str

        :param parent: Parent widget for the new action. Defaults None.
        :type parent: QWidget

        :param whats_this: Optional text to show in the status bar when the
            mouse pointer hovers over the action.

        :returns: The action that was created. Note that the action is also
            added to self.actions list.
        :rtype: QAction
        """

        icon = QIcon(icon_path)
        action = QAction(icon, text, parent)
        action.triggered.connect(callback)
        action.setEnabled(enabled_flag)

        if status_tip is not None:
            action.setStatusTip(status_tip)

        if whats_this is not None:
            action.setWhatsThis(whats_this)

        if add_to_toolbar:
            self.toolbar.addAction(action)

        if add_to_menu:
            self.iface.addPluginToVectorMenu(
                self.menu,
                action)

        self.actions.append(action)

        return action

    def initGui(self):
        """Create the menu entries and toolbar icons inside the QGIS GUI."""

        icon_path = ':/plugins/SaveAttributes/icon.png'
        self.add_action(
            icon_path,
            text=self.tr(u'Save Attributes as CSV'),
            callback=self.run,
            parent=self.iface.mainWindow())


    def unload(self):
        """Removes the plugin menu item and icon from QGIS GUI."""
        for action in self.actions:
            self.iface.removePluginVectorMenu(
                self.tr(u'&Save Attributes'),
                action)
            self.iface.removeToolBarIcon(action)
        # remove the toolbar
        del self.toolbar

    def select_output_file(self):
        filename = QFileDialog.getSaveFileName(self.dlg, "Select output file ","", '*.txt')
        self.dlg.lineEdit.setText(filename)
        
    def run(self):
        """Run method that performs all the real work"""
        layers = self.iface.legendInterface().layers()
        layer_list = []
        for layer in layers:
                layer_list.append(layer.name())
            
        self.dlg.comboBox.clear()
        self.dlg.comboBox.addItems(layer_list)
        # show the dialog
        self.dlg.show()
        # Run the dialog event loop
        result = self.dlg.exec_()
        # See if OK was pressed
        if result:
            # Do something useful here - delete the line containing pass and
            # substitute with your code.
            filename = self.dlg.lineEdit.text()
            output_file = open(filename, 'w')
           
            selectedLayerIndex = self.dlg.comboBox.currentIndex()
            selectedLayer = layers[selectedLayerIndex]
            fields = selectedLayer.pendingFields()
            fieldnames = [field.name() for field in fields]
            
            for f in selectedLayer.getFeatures():
                line = ','.join(unicode(f[x]) for x in fieldnames) + '\n'
                unicode_line = line.encode('utf-8')
                output_file.write(unicode_line)
            output_file.close()

If you want to give feedback or share your experience with this tutorial, please comment below. (requires GitHub account)