파이썬 플러그인 개발

파이썬 프로그래밍 언어로 플러그인을 생성할 수 있습니다. C++로 작성된 전통적인 플러그인에 비해 더 쉽게 플러그인을 작성하고, 이해하고, 유지하고 배포할 수 있는데, 이는 파이썬 언어의 동적 특성 덕분입니다.

QGIS 플러그인 관리자가 C++ 플러그인과 파이썬 플러그인의 목록을 모두 보여줍니다. 다음 경로에서 플러그인들을 찾습니다.

  • UNIX/Mac: ~/.qgis/python/plugins and (qgis_prefix)/share/qgis/python/plugins
  • Windows: ~/.qgis/python/plugins and (qgis_prefix)/python/plugins

윈도우의 홈 디렉터리(앞에서 ~ 로 표시된)는 보통 C:\Documents and Settings\(user) (윈도우 XP 또는 이전 버전의 경우) 또는 C:\Users\(user) 입니다. QGIS가 파이썬 2.7 버전을 이용하게 된 후부터, 플러그인으로 임포트되려면 파이썬 패키지로 인식되어야 하고, 때문에 이 경로의 하위 디렉터리들은 __init__.py 파일이 있어야 합니다.

주석

기존 디렉터리 경로를 QGIS_PLUGINPATH 에 설정해서, 플러그인 탐색 경로 목록에 이 경로를 추가할 수 있습니다.

개발 과정 :

  1. 발상 : 새 QGIS 플러그인으로 어떤 작업을 하고 싶은지에 대한 생각이 있어야 합니다. 어째서 그 작업을 하는 건가요? 어떤 문제를 해결하고 싶은 건가요? 그 문제를 위한 다른 플러그인이 이미 있는 건 아닌가요?

  2. 파일 생성 : 다음 기술된 파일들을 생성하십시오. 시작점(__init__.py) 생성. 플러그인 메타데이터 (metadata.txt) 작성. 파이썬 플러그인의 주요 본체 파일(mainplugin.py) 생성. Qt 디자이너 폼(form.ui)과 여기에서 사용될 resources.qrc 파일 생성.

  3. 코드 작성 : mainplugin.py 파일에 코드를 작성하십시오.

  4. 테스트 : QGIS를 종료하고 다시 실행한 다음 여러분이 만든 플러그인을 임포트하십시오. 아무 이상이 없는지 확인하세요.

  5. 공개 : QGIS 공식 저장소(repository)에 여러분의 플러그인을 올리거나, 자신만의 “GIS 무기”의 “무기고”로서 여러분 자신의 저장소를 만드십시오.

플러그인 작성

QGIS가 파이썬 플러그인을 지원하기 시작하면서, 수많은 플러그인들이 생겨났습니다. 플러그인 저장소 위키 페이지 에서 그중 일부를 찾아볼 수 있습니다. 여러분은 여기에 올려진 플러그인의 소스를 통해 PyQGIS의 프로그래밍에 대해 더 배우거나, 다른 사람이 했던 고생을 되풀이 하고 있는 것은 아닌지 확인할 수 있습니다. 또한 QGIS 팀에서 공식 파이썬 플러그인 저장소 를 운영하고 있습니다. 플러그인을 개발할 준비는 됐지만 뭘 해야 할지 모르겠다고요? 파이썬 플러그인 아이디어 위키 페이지 에서 PyQGIS 커뮤니티가 원하는 플러그인 기능들을 찾아볼 수 있답니다!

플러그인 파일

다음은 우리가 만들 예시 플러그인의 디렉터리 구조입니다.

PYTHON_PLUGINS_PATH/
  MyPlugin/
    __init__.py    --> *required*
    mainPlugin.py  --> *required*
    metadata.txt   --> *required*
    resources.qrc  --> *likely useful*
    resources.py   --> *compiled version, likely useful*
    form.ui        --> *likely useful*
    form.py        --> *compiled version, likely useful*

이 파일들이 의미하는 바는 다음과 같습니다.

  • __init__.py = 플러그인의 시작점입니다. 이 파일 내부에 classFactory() 메소드는 반드시 있어야 하고, 다른 초기화 코드도 들어갈 수 있습니다.

  • mainPlugin.py = 플러그인의 주요 작업 코드입니다. 이 파일은 플러그인이 수행할 작업에 대한 모든 정보와 주요 코드를 담고 있습니다.

  • resources.qrc = Qt 디자이너가 생성한 .xml 문서입니다. 폼이 사용하는 리소스의 상대 경로를 담고 있습니다.

  • resources.py = 방금 설명한 .qrc 파일을 파이썬으로 번역한 파일입니다.

  • form.ui = Qt 디자이너가 생성한 GUI입니다.

  • form.py = 방금 설명한 form.ui 파일을 파이썬으로 번역한 파일입니다.

  • metadata.txt = Required for QGIS >= 1.8.0. Containts general info, version, name and some other metadata used by plugins website and plugin infrastructure. Since QGIS 2.0 the metadata from __init__.py are not accepted anymore and the metadata.txt is required.

웹페이지 를 통해 온라인 상에서 자동화된 방법으로, 전형적인 QGIS 파이썬 플러그인의 (골격이 되는) 기본 파일들을 생성할 수 있습니다.

또 인터넷 연결 없이도 QGIS에서 플러그인 템플릿을 생성할 수 있는 Plugin Builder 라는 QGIS 플러그인도 있습니다. QGIS 2.0 버전과 호환되는 소스를 생성하므로 이 플러그인을 사용하는 편이 좋습니다.

경고

공식 파이썬 플러그인 저장소 에 플러그인을 업로드할 계획이라면, 여러분의 플러그인이 플러그인 검증 에 필요한 몇 가지 추가 규칙을 따르는지 확인해야 합니다.

플러그인 구성요소

이제 앞에서 설명한 파일 구조 내부의 각 파일들에 어떤 내용을 추가해야 하는 지를 설명하고 예시를 보여 드리겠습니다.

플러그인 메타데이터

플러그인 관리자가 플러그인 명칭, 설명 등등 플러그인에 관한 기본 정보를 검색할 수 있어야 합니다. 이 정보들이 metadata.txt 파일이 이 정보들이 들어가야 할 곳입니다.

중요

모든 메타데이터는 UTF-8로 인코딩이어야 합니다.

메타데이터 명

필수 여부

설명

name

플러그인의 명칭을 담은 짧은 문자열

qgisMinimumVersion

점으로 구분 표기된, 플러그인 구동을 위한 QGIS 최하 버전

qgisMaximumVersion

점으로 구분 표기된, 플러그인 구동을 위한 QGIS 최상 버전

description

플러그인을 설명하는 짧은 텍스트, HTML 사용 불가

about

플러그인을 자세히 설명하는 긴 텍스트, HTML 사용 불가

version

플러그인의 버전을 점으로 구분해 표기한 짧은 문자열

author

작성자 명

email

email of the author, will not be shown on the web site
changelog

문자열이며 줄바꿈 가능, HTML 사용 불가

experimental

실험적인 플러그인인지 여부, True 또는 False

deprecated

중요도가 떨어져 더 이상 사용되지 않고 앞으로는 사라지게 될 플러그인임을 표시, True 또는 False, 업로드된 버전뿐만이 아니라 플러그인의 모든 버전에 적용

tags

검색을 위한 태그. 쉼표로 분리된 목록임, 개별 태그 내부 공백 허용

homepage

플러그인의 홈페이지를 가리키는 유효한 URL

repository

소스 코드 저장소를 가리키는 유효한 URL

tracker

버그 티켓 및 버그 보고 사이트를 가리키는 유효한 URL

icon

a file name or a relative path (relative to the base folder of the plugin’s compressed package)
category

플러그인의 범주. 지정된 범주 하위 메뉴로 플러그인이 들어감. Raster, Vector, Database, Web 중의 하나

기본적으로 플러그인은 Plugins 메뉴에 들어가게 되지만 (여러분의 플러그인을 메뉴 항목에 추가하는 방법은 다음 단락에서 설명하겠습니다) Raster, Vector, Database, Web 메뉴에 추가할 수도 있습니다.

메타데이터의 “category” 항목이 플러그인을 어떤 메뉴에 추가할지 설정하는 데 쓰이기 때문에, 이 항목이 플러그인 분류에도 쓰일 수 있습니다. 이 메타데이터 항목이 사용자가 어디에서(어떤 메뉴에서) 플러그인을 찾을 수 있는지 알려주는 역할을 합니다. “category”에 쓸 수 있는 값은 Vector, Raster, Database 또는 Web 4가지입니다. 예를 들어 Raster 메뉴에서 플러그인을 사용할 수 있게 하려면 metadata.txt 에 다음 내용을 추가하십시오.

category=Raster

주석

qgisMaximumVersion 항목이 비어 있으면, 공식 파이썬 플러그인 저장소 에 업로드 시 자동적으로 현재 QGIS 메인 버전에 .99 를 더한 값으로 설정됩니다.

다음은 metadata.txt 파일의 예시입니다.

; the next section is mandatory

[general]
name=HelloWorld
email=me@example.com
author=Just Me
qgisMinimumVersion=2.0
description=This is an example plugin for greeting the world.
    Multiline is allowed:
    lines starting with spaces belong to the same
    field, in this case to the "description" field.
    HTML formatting is not allowed.
about=This paragraph can contain a detailed description
    of the plugin. Multiline is allowed, HTML is not.
version=version 1.2
; end of mandatory metadata

; start of optional metadata
category=Raster
changelog=The changelog lists the plugin versions
    and their changes as in the example below:
    1.0 - First stable release
    0.9 - All features implemented
    0.8 - First testing release

; Tags are in comma separated value format, spaces are allowed within the
; tag name.
; Tags should be in English language. Please also check for existing tags and
; synonyms before creating a new one.
tags=wkt,raster,hello world

; these metadata can be empty, they will eventually become mandatory.
homepage=http://www.itopen.it
tracker=http://bugs.itopen.it
repository=http://www.itopen.it/repo
icon=icon.png

; experimental flag (applies to the single version)
experimental=True

; deprecated flag (applies to the whole plugin and not only to the uploaded version)
deprecated=False

; if empty, it will be automatically set to major version + .99
qgisMaximumVersion=2.0

__init__.py

이 파일은 파이썬의 임포트 체계에서 필요로 합니다. 또한, QGIS는 이 파일 내부에 QGIS가 플러그인을 불러올 때 호출되는 classFactory() 함수가 들어 있을 것을 요구합니다. 이 함수는 QgisInterface 클래스 인스턴스의 참조값을 받아서 mainplugin.py 파일에 있는 플러그인 클래스의 인스턴스를 반환해야만 합니다. 본 예시에서는 TestPlugin 이라는 이름이군요(다음 코드를 보세요). __init__.py 파일에는 다음과 같은 내용이 들어갑니다.

def classFactory(iface):
  from mainPlugin import TestPlugin
  return TestPlugin(iface)

## any other initialisation needed

mainPlugin.py

이 파일이 실제 작업을 수행하는 파일이며, 다음과 같은 (예시: mainPlugin.py) 내용을 담고 있습니다.

from PyQt4.QtCore import *
from PyQt4.QtGui import *
from qgis.core import *

# initialize Qt resources from file resources.py
import resources

class TestPlugin:

  def __init__(self, iface):
    # save reference to the QGIS interface
    self.iface = iface

  def initGui(self):
    # create action that will start plugin configuration
    self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow())
    self.action.setObjectName("testAction")
    self.action.setWhatsThis("Configuration for test plugin")
    self.action.setStatusTip("This is status tip")
    QObject.connect(self.action, SIGNAL("triggered()"), self.run)

    # add toolbar button and menu item
    self.iface.addToolBarIcon(self.action)
    self.iface.addPluginToMenu("&Test plugins", self.action)

    # connect to signal renderComplete which is emitted when canvas
    # rendering is done
    QObject.connect(self.iface.mapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest)

  def unload(self):
    # remove the plugin menu item and icon
    self.iface.removePluginMenu("&Test plugins", self.action)
    self.iface.removeToolBarIcon(self.action)

    # disconnect form signal of the canvas
    QObject.disconnect(self.iface.mapCanvas(), SIGNAL("renderComplete(QPainter *)"), self.renderTest)

  def run(self):
    # create and show a configuration dialog or something similar
    print "TestPlugin: run called!"

  def renderTest(self, painter):
    # use painter for drawing to map canvas
    print "TestPlugin: renderTest called!"

플러그인의 메인 소스 파일(예를 들어 mainPlugin.py) 안에 다음과 같은 플러그인 함수들이 반드시 들어가야 합니다.

  • __init__ –> QGIS 인터페이스에 접근할 수 있게 해줍니다.

  • initGui() –> 플러그인이 로드될 때 호출됩니다.

  • unload() –> 플러그인이 언로드될 때 호출됩니다.

앞의 예시 코드에서 볼 수 있듯이, addPluginToMenu() 함수가 쓰이고 있습니다. 이 함수가 Plugins 메뉴에 플러그인과 연결되는 메뉴 액션을 추가할 것입니다. 다른 메뉴에 액션을 추가하는 유사 메소드도 있습니다. 다음은 그 메소드들의 목록입니다.

  • addPluginToRasterMenu()
  • addPluginToVectorMenu()
  • addPluginToDatabaseMenu()
  • addPluginToWebMenu()

이들은 모두 addPluginToMenu() 메소드와 동일한 문법을 따릅니다.

플러그인 항목을 구성하는 방법의 일관성을 유지하려면, 여러분의 플러그인 메뉴를 이런 미리 정의된 메소드를 이용해 추가하는 편이 좋습니다. 하지만, 여러분이 임으로 만든 메뉴 그룹을 다음 예시에서와 같이 직접 메뉴 바에 추가할 수도 있습니다.

def initGui(self):
    self.menu = QMenu(self.iface.mainWindow())
    self.menu.setObjectName("testMenu")
    self.menu.setTitle("MyMenu")

    self.action = QAction(QIcon(":/plugins/testplug/icon.png"), "Test plugin", self.iface.mainWindow())
    self.action.setObjectName("testAction")
    self.action.setWhatsThis("Configuration for test plugin")
    self.action.setStatusTip("This is status tip")
    QObject.connect(self.action, SIGNAL("triggered()"), self.run)
    self.menu.addAction(self.action)

    menuBar = self.iface.mainWindow().menuBar()
    menuBar.insertMenu(self.iface.firstRightStandardMenu().menuAction(), self.menu)

def unload(self):
    self.menu.deleteLater()

메뉴와 툴바의 사용자 지정 기능을 이용할 수 있도록, QActionQMenu 클래스의 objectName 을 플러그인의 고유한 명칭으로 설정하는 일을 잊지 마십시오.

리소스 파일

앞의 예시 코드에서 initGui() 함수를 보면 리소스 파일(이 경우 파일명 resources.qrc)에 있는 아이콘을 사용했다는 사실을 알 수 있습니다.

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

It is good to use a prefix that will not collide with other plugins or any parts of QGIS, otherwise you might get resources you did not want. Now you just need to generate a Python file that will contain the resources. It’s done with pyrcc4 command

pyrcc4 -o resources.py resources.qrc

이게 전부입니다... 복잡할 게 없죠. ^_____^

이 모든 과정을 정확히 작업했다면 플러그인 관리자가 아무 문제 없이 여러분의 플러그인을 찾아 불러올 것입니다. 그리고, 툴바 아이콘 또는 적당한 메뉴 항목을 선택하면 콘솔에 메시지가 보일 것입니다.

실제 플러그인 작업 시에는 다른 (작업용) 디렉터리에서 플러그인을 개발하고 UI 및 리소스 파일을 생성할 makefile을 작성한 다음, 그 결과물을 QGIS의 플러그인 폴더에 설치하는 것이 좋습니다.

문서화

플러그인을 위한 문서는 HTML 도움말 파일로 만들 수 있습니다. 이 파일로 다른 QGIS 도움말과 마찬가지로 도움말을 제공하는 방법을 qgis.utils 모듈의 showPluginHelp() 함수가 제공합니다.

The showPluginHelp`() function looks for help files in the same directory as the calling module. It will look for, in turn, index-ll_cc.html, index-ll.html, index-en.html, index-en_us.html and index.html, displaying whichever it finds first. Here ll_cc is the QGIS locale. This allows multiple translations of the documentation to be included with the plugin.

또한 showPluginHelp() 함수는 도움말이 보여질 특정 플러그인을 지정하는 packageName 파라미터, 탐색의 대상이 될 파일명의 “index” 부분을 대체할 수 있는 filename 파라미터, 그리고 웹브라우저에서 문서의 어느 부분을 보여줄 것인지 지정하는 HTML 앵커 태그(anchor tag)의 명칭인 section 파라미터를 사용할 수 있습니다.