Разработка простого расширения для QGIS на Python

Материал из GIS-Lab
Перейти к навигации Перейти к поиску
Эта страница опубликована в основном списке статей сайта
по адресу http://gis-lab.info/qa/qgis-dev-python.html


Описание процесса создания элементарного расширения для QGIS.

QGIS — свободная пользовательская ГИС, обладающая развитым API и продвинутой системой расширений (модулей), которые могут быть использованы для расширения функциональности программы. QGIS написана на объектно-ориентированном языке высокого уровня C++. Так же в QGIS встроены привязки (bindings), которые реализуют практически весь функционал QGIS на языке Python. Графический интерфейс пользователя QGIS базируется на библиотеках Qt4. Разработка для QGIS может вестись на двух языках, Python и C++. Для разработки на языке Python необходимы привязки PyQt4, обеспечивающие взаимодействия Python'а с Qt4. В PyQt4 реализовано 440 классов Qt4 в виде модулей Python. Помимо расширений библиотеки QGIS могут быть также использованы для разработки отдельных приложений, что подробно описано в статье «Создание приложения на базе набора библиотек QGIS на Python».

Данная статья описывает процесс создания каркаса расширения на языке Python. Разработанный каркас можно в дальнейшем использовать как основу для других расширений, выполняющих нужные функции.

Создание расширения состоит из нескольких этапов:

  • Идея
  • Создание файлов
  • Разработка
  • Тестирование
  • Публикация

В данной статье идея расширения достаточно проста: создать расширение, которое добавит новую кнопку на панель инструментов и подменю в меню расширений и будет выполнять какое-либо простое действие. Это расширение можно будет использовать как каркас для других будущих, более функциональных расширений. Далее мы более подробно разберем остальные шаги.

Получение исходного кода

Код расширения, проверенно работающий под QGIS 1.0 можно получить несколькими способами.

Его можно скачать в виде архива.

Его можно получить через SVN

svn co http://svn.gis-lab.info/testplugin testplugin

Рабочий вариант расширения можно загрузить через репозиторий расширений GIS-Lab, открыв Установщик модулей QGIS («Модули → Загрузить модули») и добавив сторонний репозиторий:

Name: GIS-Lab
URL: http://gis-lab.info/programs/qgis/qgis-repo.xml

Подготовка

Для разработки расширения понадобится

  • Любой текстовый редактор
  • Python, лучше версии 2.5
  • PyQt
  • Qt Opensource
  • собственно QGIS соответствующей версии, для проверки работоспособности расширения.

Разработку можно вести прямо в папке, где хранятся расширения или переместить его в нее после разработки, в QGIS эта папка либо QGIS\python\plugins либо C:\Documents and Settings\username\.qgis\python\plugins\.

В папке с плагинами необходимо создать новую, назвав ее так, чтобы бы по ее названию было бы легко определить, что именно расширение делает. Назовем нашу папку testplugin. Это название следует запомнить, так как оно будет фигурировать в различных компонентах кода.

Перед собственно разработкой, нам понадобится создать в этой папке несколько новых, пока пустых текстовых файлов, называемых следующим образом:

  • __init__.py — начальная точка, содержит информацию о расширении, версию, имя разработчика, в нем создается экземпляр основного класса, он передается в qgis
  • plugin.py — основной код расширения. Содержит всю информацию о всех действиях расширения и их код.
  • resources.qrc — xml файл, создаваемый Qt Designer или вручную и содержащий относительные пути к ресурсам расширения, формам, иконкам и т.п.

Помимо этих файлов, если расширение использует формы, могут также присутствовать файлы:

  • form.ui — форма созданная с помощью Qt Designer
  • form.py — она же, сохраненная в виде программы на языке Python

Для примера из данной статьи эти файлы не понадобятся.

Итак, пустые файлы созданы и лежат в нужной папке.

Разработка

Разработка начинается с файла __init__.py, содержащего некоторые основные параметры расширения, такие как его название name, т.е. то, как он будет показываться в Менеджере модулей, его описание description, показывается там же. Если мы хотим использовать в коде кириллицу, в том числе в комментариях, необходимо в самом его начале объявить кодировку utf-8. Нужно также указать минимальную версию QGIS, для которой разработано это расширение (при попытке загрузки в QGIS меньшей версии плагин будет отключен) и другие параметры:

# -*- coding: utf-8 -*-
def name():
  return "My testing plugin"
def description():
  return "This plugin has no real use."
def qgisMinimumVersion(): 
  return "1.0" 
def version():
  return "Version " + "0.1.21"
def authorName():
  return "Ivan Developer"
...

Важным фрагментом кода __init__.py является импорт исполняемой части расширения, содержащейся в файле plugin.py (поэтому from plugin) и содержащей всю содержательную часть кода.

...
def classFactory(iface):
  # загрузка класса TestPlugin из файла plugin.py
  from plugin import TestPlugin
  return TestPlugin(iface)

Название главного импортируемого класса TestPlugin должно быть равно названию класса в коде plugin.py, причем название — регистрозависимое, если вызывается TestPlugin, а класс носит название testplugin, будет выдано сообщение об ошибке.

Рассмотрим plugin.py.

Как и во всех программах на языке Python, все начинается с импорта необходимых для работы классов, в нашем случае это будут классы PyQt — обертки для Qt на языке Python: PyQt4.QtCore и PyQt4.QtGui, активно используемые QGIS и сами классы QGIS: qgis.core

# -*- coding: utf-8 -*-
from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *

Помимо этого нужно также импортировать ресурсы самого расширения, в нашем случае они будут задаваться через resources.py, как его создать мы разберем чуть позже.

import resources

После импорта идет раздел главного класса, импортируемого в __init__.py и содержащего все нужные функции. Необходимо обратить внимание, что название класса должно быть равно названию используемому для его вызова в __init__.py. Определение функции начинается с ключевого слова def, за которым следует имя функции и ее аргументы.

Объявим наш основной класс и функцию инициализации:

class TestPlugin:

  def __init__(self, iface):
    # сохраним ссылку на интерфейс QGIS
    self.iface = iface

Одна из важнейших функций — создание элементов интерфейса расширения initGui. При загрузке расширения к пользовательскому интерфейсу iface.self могут добавляться кнопки addToolBarIcon или строки в меню «Модули», addPluginToMenu. Не забываем документировать функции с помощью тройных кавычек.

import resources  def initGui(self):
    """Иннициализируем графический интерфейс пользователя"""
    # создадим действие, которое будет запускать конфигурацию расширения
    self.action = QAction(QIcon(":/plugins/testplugin/icon.png"), "Test plugin", self.iface.mainWindow())
    self.action.setWhatsThis("Configuration for test plugin")
    
    # связь действия с функцией run
    QObject.connect(self.action, SIGNAL("activated()"), self.run)
    
    # добавим кнопку на панель инструментов
    self.iface.addToolBarIcon(self.action)
    
    # добавим строку вызова в новое подменю
    self.iface.addPluginToMenu("&Test plugin", self.action)

Следующая важная функция расширения описывает то, что происходит при его выгрузке, выключении через Менеджер модулей — unload(self)

  def unload(self):
    """Действия при выгрузке расширения"""
    # удалить меню расширения и иконку
    self.iface.removePluginMenu("&Test plugin",self.action)
    self.iface.removeToolBarIcon(self.action)
    # отключить сигнал обновления вида
    QObject.disconnect(self.iface.mapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest)

И наконец функция выполняющая единственное действие в нашем расширении — run(). Функция использует функцию QString для создания строки сообщения и QMessageBox для показа этого сообщения в главном окне программы.

  def run(self):
    """Действия при запуске расширения"""
    # создать и показать сообщение
    infoString = QString("This is a test")
    QMessageBox.information(self.iface.mainWindow(),"About",infoString)

Какие функции из API доступны через Python

К сожалению, не все функции API QGIS доступны через Python (через C++ доступны все). Чтобы определить, доступны ли конкретные классы и функции, необходимо сначала найти их в документации к API GIS http://doc.qgis.org. А затем в консоли Python определить с помощью команды dir видны ли эти классы и функции из Python.

Например, определим доступные классы:

import qgis.core
dir(qgis.core)

Результат:

['DEFAULT_LINE_WIDTH', 'DEFAULT_POINT_SIZE', 'ELLPS_PREFIX_LEN', 'GEOCRS_ID', 'GEOPROJ4', 'GEOSRID', 'GEOWkt',
'GEO_EPSG_CRS_ID', 'LAT_PREFIX_LEN', 'MINIMUM_POINT_SIZE', 'PROJ_PREFIX_LEN', 'QGis', 'QgsApplication', 'QgsContextHelp',
'QgsContinuousColorRenderer', 'QgsContrastEnhancement', 'QgsContrastEnhancementFunction', 'QgsCoordinateReferenceSystem', 
'QgsCoordinateTransform', 'QgsDataProvider', 'QgsDataSourceURI', 'QgsDistanceArea', 'QgsFeature', 'QgsField', 'QgsGeometry',
'QgsGraduatedSymbolRenderer', 'QgsLabel', 'QgsLabelAttributes', 'QgsLogger', 'QgsMapLayer', 'QgsMapLayerRegistry',
'QgsMapRenderer', 'QgsMapToPixel', 'QgsMarkerCatalogue', 'QgsMessageOutput', 'QgsMessageOutputConsole', 'QgsPoint',
'QgsProject', 'QgsProviderMetadata', 'QgsProviderRegistry', 'QgsRasterBandStats', 'QgsRasterDataProvider', 'QgsRasterLayer',
'QgsRasterPyramid', 'QgsRasterShader', 'QgsRasterShaderFunction', 'QgsRasterTransparency', 'QgsRasterViewPort', 'QgsRectangle',
'QgsRenderContext', 'QgsRenderer', 'QgsScaleCalculator', 'QgsSingleSymbolRenderer', 'QgsSnapper', 'QgsSnappingResult',
'QgsSpatialIndex', 'QgsSymbol', 'QgsSymbologyUtils', 'QgsUniqueValueRenderer', 'QgsVectorDataProvider',
'QgsVectorFileWriter', 'QgsVectorLayer', 'USER_CRS_START_ID', '__doc__', '__file__', '__name_]

Далее, после выбора нужного класса, например QgsFeature, посмотрим его методы:

dir(qgis.core.QgsFeature)

Результат:

['__class__', '__delattr__', '__doc__', '__getattribute__', '__hash__', '__init__', '__module__',
'__new__', '__reduce__', '__reduce_ex__', '__repr__', '__setattr__', '__str__', '__weakref__', 'addAttribute',
'attributeMap', 'changeAttribute', 'clean', 'deleteAttribute', 'geometry', 'geometryAndOwnership', 'id',
'isDirty', 'isValid', 'setAttributeMap', 'setFeatureId', 'setGeometry', 'setGeometryAndOwnership', 'setTypeName',
'setValid', 'typeName']

Ресурсы

Для разработки расширения понадобится создать специальный файл, который будет содержать указатели на используемые расширением ресурсы, такие как например иконки. Пример такого файла:

<RCC>
<qresource prefix="/plugins/testplugin" >
    <file>icon.png</file>
</qresource>
</RCC>

Рекомендуется использовать уникальный qresource prefix, который не будет конфликтовать с другими расширениями.

После создания этого файла, необходимо его скомпилировать с помощью pyrcc4 в формат, который можно импортировать с помощью Python:

pyrcc4 -o resources.py resources.qrc 

И как уже упоминалось выше, импортировать получившиеся ресурсы в основном коде plugin.py.

Заключение

Разработка каркаса нашего расширения завершена. Если она велась в нужной папке, то после запуска QGIS оно должно автоматически в списке расширений и можно начать его тестирование, отладку и, конечно, наполнение нужными полезными функциями.

Помните, что лучшим пособием по разработке для QGIS являются расширения созданные другими авторами.

О том как организовать репозиторий расширений на своем сайте, для удобного их широкого распространения мы расскажем в следующих статьях.

Ссылки по теме