メインページ/QGIS/coding-compilation guide/plugins writing in python/

提供: OSGeo.JP Wiki
2009年8月25日 (火) 19:37時点におけるKayama (トーク | 投稿記録)による版

(差分) ←前の版 | 最新版 (差分) | 次の版→ (差分)
移動: 案内検索
% 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 we provide a beginner's tutorial for writing a simple QGIS Python
plugin. 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 useful for debugging or writing 
standalone applications in Python, with their own user interfaces
based on the functionality of the QGIS core library.
 
\subsection{Why Python and what about licensing}
 
Python is a scripting language that was designed with the goal of being easy
to program. It has a mechanism for automatically releasing 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 take advantage of the functionality of libqgis\_core.so and libqgis\_gui.so. 
As both libqgis\_core.so and libqgis\_gui.so are licensed under GNU GPL, QGIS Python 
plugins must also be licenced under the GPL. 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}
 
You will need the following libraries and programs to create QGIS python plugins yourself:
 
\begin{itemize}
\item QGIS
\item Python >= 2.5
\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 contains Qt, PyQt and the PyQt development tools.
 
\subsection{Programming a simple PyQGIS Plugin in four steps}\label{subsec:pyfoursteps}
 
The example plugin demonstrated here is intentionally kept simple. It adds a button to the menu
bar of QGIS. When 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 is done, the plugin will show up in the
\dropmenuopttwo{mActionShowPluginManager}{Plugin Manager...}
 
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 has 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 worry 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 workshop can be downloaded from \url{http://karlinapp.ethz.ch/python\_foss4g}, you 
can also use your own icon if you prefer, you just need to make sure it is named 
\filename{foss4g.png}. Move these 2 files into the directory of the example plugin 
\filename{\$HOME/.qgis/python/plugins/foss4g\_plugin} and enter: \filename{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}
 
\minisec{Signal 'activated ()' in python plugins is deprecated}
 
When writing your python plugin, remember that the \texttt{activated ()}
signal of the 'QAction class', used to signal the plugin it has been
activated, is deprecated. This signal has disappeared from Qt4 and if Qt4 is
not compiled with the Qt3 backward compatibility, it is simply non existent,
so no plugin can be called at all.
 
Please replace \texttt{activated ()} with \texttt{triggered ()}.
 
\subsection{Uploading the plugin to the repository}
 
If you have written a plugin you consider to be useful and you want to share with
other users you are welcome to upload it to the QGIS User-Contributed Repository.
\begin{itemize}
\item Prepare a plugin directory containing only the 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), if not 
the Plugin Installer will be unable 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.
The Version Number field is especially important, and if filled out incorrectly it may confuse the Plugin Installer and cause false notifications of available updates.
\end{itemize}
 
\subsection{Further information}
 
As you can see, you need information from many different sources to a write PyQGIS
plugin. Plugin authors need to know Python, the QGIS plugin interface, as well 
as the Qt4 classes and tools. In 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 many 
existing Python plugins and to study their behaviour. It is also possible to use 
the on-line Python plugin generator to create a base plugin to work off of. This on-line 
tool will help you to build a minimal plugin that you can use as a starting point in your 
development. The result is a ready to install QGIS 1.0 plugin that implements an 
empty dialog with Ok and Close buttons. It is available here: \url{http://www.pyqgis.org/builder/plugin_builder.py}
 
There is a a collection of on-line documentation that may be useful for PyQGIS programmers:
 
\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.