16.1. Python プラグインを構成する

プラグインを作成するための主な手順:

  1. 考え: 新しいQGISプラグインで何をしたいのか、考えを持ちましょう。

  2. セットアップ: プラグインのファイルを作る。プラグインの種類によって、必須のものもあれば、オプションのものもあります

  3. 開発: 適切なファイルに コードを書く

  4. ドキュメント: プラグインのドキュメントを書く

  5. オプションで: 翻訳: 別の言語に プラグインを翻訳する

  6. テスト :すべてがうまく行くかどうかを プラグインをリロード して確認します。

  7. 公開:出来上がったプラグインをQGISリポジトリに公開するか、ご自身のリポジトリを作って個人的な「GISウェポン」の兵器廠にしましょう。

16.1.1. はじめる

新しいプラグインを書き始める前に、 公式なPythonプラグインリポジトリ を見てみてください。既存のプラグインのソースコードを見ることで、プログラミングについてより深く学ぶことができます。また、似たようなプラグインがすでに存在し、それを拡張したり、少なくともそれをベースにして自分のプラグインを開発することができるかもしれません。

16.1.1.1. プラグインのファイル構造をセットアップする

新しいプラグインを始めるには、必要なプラグインファイルをセットアップする必要があります。

始めるのに役立つ、プラグインテンプレートのリソースが2つあります:

  • 教育目的や最小限のアプローチが必要な場合は、minimal plugin template が有効なQGIS Pythonプラグインを作成するために必要な基本的なファイル(スケルトン)を提供します。

  • より完全な機能を持つプラグインテンプレートとして、Plugin Builder は、ローカライズ(翻訳)やテストなどの機能を含む、複数の異なるプラグインタイプのテンプレートを作成できます。

典型的なプラグインディレクトリには、以下のファイルを含みます:

  • metadata.txt - 必須 - 一般的な情報、バージョン、名前、そしてプラグインのウェブサイトやプラグインインフラストラクチャで使用されるその他のメタデータが含まれています。

  • __init__.py - 必須 - プラグインの開始点です。classFactory() メソッドを持つ必要があり、その他の初期化コードも持つことができます。

  • mainPlugin.py - コア・コード - プラグインで主に働くコードです。プラグインのアクションに関するすべての情報とメインコードが含まれています。

  • form.ui - カスタムGUIを持つプラグイン用 - Qt Designerで作成したGUIです。

  • form.py - コンパイルしたGUI - 上記で説明したform.uiをPythonに翻訳したものです。

  • resources.qrc - オプション - Qt Designer によって作成される .xml ドキュメントです。GUIフォームで使用されるリソースへの相対パスが含まれています。

  • resources.py - コンパイルされたリソース、オプション - 上記で説明した .qrc ファイルを Python に翻訳したものです。

警告

プラグインを 公式なPythonプラグインリポジトリ にアップロードする場合は、プラグインが 検証 に必要ないくつかの追加ルールに従っているかどうかを確認する必要があります。

16.1.2. プラグインのコードを書く

上記で紹介した各ファイルに、どのような内容を追加していけばよいのか、次のセクションで紹介します。

16.1.2.1. metadata.txt

まず、プラグインマネージャはプラグインの名前、説明などの基本情報を取得する必要があります。この情報は metadata.txt に保存されます。

注釈

メタデータのエンコーディングはすべてUTF-8でなければなりません。

メタデータ名

必須

name

Y

そのプラグインの名前から成る短い文字列

qgisMinimumVersion

Y

ドット記法による最小QGISバージョン

qgisMaximumVersion

N

ドット記法による最大QGISバージョン

description

Y

プラグインを説明する短いテキスト。HTMLは不可

about

Y

プラグインの詳細を説明するより長いテキスト。HTMLは不可

version

Y

バージョンをドット記法で記した短い文字列

author

Y

著者の名前

email

Y

著者のEメール。ウェブサイトでログインしたユーザのみに表示されるが、プラグインをインストールした後はプラグインマネージャで見ることができる

changelog

N

文字列。改行して複数行になってもよい。HTML不可

experimental

N

ブール値フラグ、True または False - このバージョンが実験的であれば True

deprecated

N

ブール値のフラグ、True または False, アップロードされたバージョンだけでなく、プラグイン全体に適用されます

tags

N

コンマで区切ったリスト。個々のタグの内部ではスペース可

homepage

N

プラグインのホームページを示す有効なURL

repository

Y

ソースコードのリポジトリの有効なURL

tracker

N

チケットとバグ報告のための有効なURL

icon

N

画像のファイル名もしくは(プラグインの圧縮パッケージのベースフォルダからの)相対パス。画像はウェブに適したフォーマット(PNG, JPEG)で

category

N

Raster, Vector, Database, Mesh and Web のいずれか

plugin_dependencies

N

インストールする他のプラグインをPIPライクなカンマ区切りでリストアップし、メタデータのnameフィールドにあるプラグイン名を使います

server

N

ブール値のフラグ、True または False プラグインがサーバーインタフェースを持っているかどうかを決めます

hasProcessingProvider

N

ブール値のフラグ、True または False、プラグインがプロセシングアルゴリズムを提供するかどうかを決めます

デフォルトでは、プラグインは プラグイン メニューに配置されます(プラグインのメニューエントリーを追加する方法は次のセクションで説明します)が、 ラスタ, ベクタ, データベース, メッシュ および Web メニューにも配置することができます。

表示するメニューを特定するために、対応する "category" メタデータエントリが存在します。これに従ってプラグインは分類することができます。このメタデータエントリは、ユーザへのヒントとして使われ、このプラグインを使うためにはどのメニューを探せばよいかを伝えます。"category" に使うことのできる値は、Vector、Raster、Database、Web です。例えば、あなたのプラグインを Raster メニューから使えるようにするには、 metadata.txt にそのように追加します。

category=Raster

注釈

qgisMaximumVersion が空欄の場合、公式なPythonプラグインリポジトリ にアップロードする際に、自動的にメジャーバージョン+ .99 に設定されます。

metadata.txt のための例です。

; the next section is mandatory

[general]
name=HelloWorld
email=me@example.com
author=Just Me
qgisMinimumVersion=3.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
tracker=http://bugs.itopen.it
repository=http://www.itopen.it/repo
; 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=https://www.itopen.it
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=3.99

; Since QGIS 3.8, a comma separated list of plugins to be installed
; (or upgraded) can be specified.
; The example below will try to install (or upgrade) "MyOtherPlugin" version 1.12
; and any version of "YetAnotherPlugin".
; Both "MyOtherPlugin" and "YetAnotherPlugin" names come from their own metadata's
; name field
plugin_dependencies=MyOtherPlugin==1.12,YetAnotherPlugin

16.1.2.2. __init__.py

このファイルはPythonのインポートシステムで必要とされます。同時にQGISはこのファイルの classFactory() 関数を必要とします。この関数はプラグインがQGISに読み込まれる時に呼ばれます。この関数は QgisInterface ` のインスタンスへの参照を受け取り、 :file:`mainplugin.py ファイルのあなたのプラグインオブジェクトを返さなければなりません(ここでの事例では TestPlugin がそれに当たります。以下を見てください)。 __init__.py は次のようにならなければなりません。

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

# any other initialisation needed

16.1.2.3. mainPlugin.py

このファイルが魔法の起こる場所です。魔法はこんな風です(例えば mainPlugin.py の場合)。

from qgis.PyQt.QtGui import *
from qgis.PyQt.QtWidgets import *

# initialize Qt resources from file resources.py
from . 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("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")
    self.action.triggered.connect(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
    self.iface.mapCanvas().renderComplete.connect(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
    self.iface.mapCanvas().renderComplete.disconnect(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 )に必須のプラグイン関数は以下の3つだけです。

  • __init__ はQGIS interfaceへのアクセスを与えます。

  • initGui() はプラグインが読み込まれた時に呼ばれます。

  • unload() はプラグインがアンロードされた時に呼ばれます。

上記の例では、 addPluginToMenu() が使われています。これにより、対応するメニューアクションが プラグイン メニューに追加されます。別のメニューにアクションを追加する別の方法も存在します。ここでは、それらのメソッドの一覧を示します:

いずれも addPluginToMenu() メソッドと同じ構文になっています。

これらのあらかじめ定義されたメソッドのうちのひとつに、あなたのプラグインメニューを追加することは、プラグインエントリがどのように組織されているかの一貫性を保つためにも推奨されます。しかし、次の例が示すように、独自のカスタムメニューグループを直接メニューバーに追加することもできます。

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

    self.action = QAction(QIcon("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")
    self.action.triggered.connect(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()

カスタマイズが可能になるように、QAction クラスと QMenu クラスの objectName にあなたのプラグインの固有名を設定するのを忘れないでください。

ヘルプやアバウトのアクションはカスタムメニューに追加することもできますが、QGISのメインメニューである ヘルプ ► プラグイン メニューで利用できるようにすると便利です。これは pluginHelpMenu() メソッドを使って行われます。

def initGui(self):

    self.help_action = QAction(
        QIcon("testplug:icon.png"),
        self.tr("Test Plugin..."),
        self.iface.mainWindow()
    )
    # Add the action to the Help menu
    self.iface.pluginHelpMenu().addAction(self.help_action)

    self.help_action.triggered.connect(self.show_help)

@staticmethod
def show_help():
    """ Open the online help. """
    QDesktopServices.openUrl(QUrl('https://docs.qgis.org'))

def unload(self):

    self.iface.pluginHelpMenu().removeAction(self.help_action)
    del self.help_action

実際のプラグインを作成する場合は、別の(作業)ディレクトリでプラグインを書き、UIとリソースファイルを生成し、QGISのインストールディレクトリにそのプラグインをインストールするmakefileを書くのが賢明でしょう。

16.1.3. プラグインのドキュメントを書く

プラグインのドキュメントは、HTMLヘルプファイルとして記述することができます。qgis.utils モジュールは関数 showPluginHelp() を提供し、他のQGISヘルプと同じようにヘルプファイルのブラウザを開きます。

showPluginHelp() 関数は、呼び出したモジュールと同じディレクトリにあるヘルプファイルを探します。index-ll_cc.htmlindex-ll.htmlindex-en.htmlindex-en_us.htmlindex.html を順に探し、最初に見つかった方を表示します。ここで ll_cc はQGISのロケールです。これにより、プラグインに複数の翻訳されたドキュメントを含めることができます。

showPluginHelp() 関数は、ヘルプを表示する特定のプラグインを特定する packageName、検索するファイル名の "index" と置き換える filename、ブラウザが置かれるドキュメント内の html アンカータグの名前 section をパラメータとして受け取ることもできます。

16.1.4. プラグインを翻訳する

いくつかのステップで、プラグインをローカライズするための環境を設定し、コンピュータのロケール設定によってプラグインが異なる言語で読み込まれるようにすることができます。

16.1.4.1. ソフトウェア要件

すべての翻訳ファイルを作成し管理する最も簡単な方法は、Qt Linguist をインストールすることです。DebianベースのGNU/Linux環境では、次のように入力するとインストールすることができます:

sudo apt install qttools5-dev-tools

16.1.4.2. ファイルとディレクトリ

プラグインを作成すると、メインのプラグインディレクトリの中に i18n フォルダーが見つかります。

すべての翻訳ファイルは、このディレクトリ内になければなりません。

16.1.4.2.1. .pro ファイル

まず、.pro ファイルを作成します。これは、Qt Linguist で管理できる プロジェクト ファイルです。

この .pro ファイルでは、翻訳したいファイルやフォームをすべて指定する必要があります。このファイルは、ローカライズファイルと変数をセットアップするために使用されます。 example plugin の構造と一致する、プロジェクトファイル:

FORMS = ../form.ui
SOURCES = ../your_plugin.py
TRANSLATIONS = your_plugin_it.ts

プラグインはもっと複雑な構造をしていて、複数のファイルにまたがる場合があります。この場合、.pro ファイルを読み込んで翻訳可能な文字列を更新するために使用するプログラムである pylupdate5 はワイルドカード文字を展開しないので、すべてのファイルを .pro ファイルに明示的に配置する必要があることに留意してください。そうすると、あなたのプロジェクトファイルは次のようなものになります:

FORMS = ../ui/about.ui ../ui/feedback.ui \
        ../ui/main_dialog.ui
SOURCES = ../your_plugin.py ../computation.py \
          ../utils.py

さらに、your_plugin.py ファイルは、QGISツールバーのプラグインのすべてのメニューとサブメニューを 呼び出す ファイルであり、それらをすべて翻訳したいのです。

最後に TRANSLATIONS 変数で、必要な翻訳言語を指定することができます。

警告

ts ファイルの名前は必ず your_plugin_ + language + .ts のようにします。そうしないと言語の読み込みに失敗します!言語の2文字のショートカットを使用します(イタリア語は it、ドイツ語は de など...)

16.1.4.2.2. .ts file

.pro を作成したら、プラグインの言語用の .ts ファイルを生成する準備が整いました。

ターミナルを開き、your_plugin/i18n ディレクトリに移動し、次のように入力します:

pylupdate5 your_plugin.pro

your_plugin_language.ts ファイルが表示されます。

.ts ファイルを Qt Linguist で開き、翻訳を開始します。

16.1.4.2.3. .qm file

プラグインの翻訳が完了したら(一部の文字列が完成していない場合、その文字列のソース言語が使用されます)、.qm ファイル(QGISで使用されるコンパイル済みの .ts ファイル)を作成する必要があります。

ターミナルを開き、your_plugin/i18n ディレクトリにcdし、次のように入力するだけです:

lrelease your_plugin.ts

これで、i18n ディレクトリに your_plugin.qm ファイルが表示されます。

16.1.4.3. Makefileを使って翻訳する

また、Plugin Builderでプラグインを作成した場合は、makefileを使ってpythonコードやQtダイアログからメッセージを抽出することができます。Makefileの冒頭には、LOCALES変数があります:

LOCALES = en

この変数に言語の略語を追加します。例えばハンガリー語は:

LOCALES = en hu

これで、ソースから hu.ts ファイル(と en.ts も)を生成したり更新したりすることができるようになりました:

make transup

これで、LOCALES変数に設定されたすべての言語の .ts ファイルが更新されました。プログラムメッセージの翻訳には Qt Linguist を使用します。翻訳が終わると、transcompile: で .qm ファイルを作成することができます:

make transcompile

プラグインと一緒に .ts ファイルを配布する必要があります。

16.1.4.4. プラグインを読み込む

プラグインの翻訳を確認するには、QGISを開き、言語を変更(設定 ► オプション ► 一般)してQGISを再起動します。

プラグインが正しい言語で表示されるはずです。

警告

プラグインに何か変更を加えた場合(新しいUI、新しいメニューなど)、.ts.qm の両方のファイルの更新版を 再び生成する 必要があるので、上記のコマンドを再度実行してください。

16.1.5. プラグインを共有する

QGISは、プラグインリポジトリで何百ものプラグインをホストしています。あなたのプラグインを共有することを検討してください!それはQGISの可能性を広げ、人々はあなたのコードから学ぶことができます。ホストされているすべてのプラグインは、QGISのプラグインマネージャーから検索してインストールすることができます。

情報および要件はこちらです: plugins.qgis.org

16.1.6. コツと技

16.1.6.1. プラグイン・リローダー

プラグインの開発中、テストのためにQGISに再読み込みする必要が頻繁に生じます。これは、Plugin Reloader プラグインを使用することで非常に簡単に行うことができます。このプラグインは プラグインマネージャ で見つけることができます。

16.1.6.2. qgis-plugin-ciでパッケージング、リリース、翻訳を自動化

qgis-plugin-ci は、QGISプラグインの自動パッケージングと展開をコンピュータ上で行うためのコマンドラインインタフェースを提供します。また、GitHubワークフローGitlab-CI 、翻訳用の Transifex <https://www.transifex.com/> などの継続的統合機能も使用できます。

CLIやCIアクションで、XMLプラグインリポジトリファイルのリリース、翻訳、公開、生成を行うことができます。

16.1.6.3. プラグインにアクセスする

インストールされたプラグインの全てのクラスにQGIS内からpythonを使ってアクセスすることができ、デバッグの際に便利です。

my_plugin = qgis.utils.plugins['My Plugin']

16.1.6.4. ログ・メッセージ

プラグインは ログメッセージパネル 内に独自のタブを持ちます。

16.1.6.5. リソースファイル

プラグインによっては、例えば resources.qrc のように、アイコンなどのGUI用のリソースを定義したリソースファイルを使用するものがあります:

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

他のプラグインや他のQGISのパーツと衝突しないよう接頭辞を使うのは良いことです。さもないと望んでいないリソースを読み込んでしまうかもしれません。さてあと必要なのはこのリソースを含むPythonファイルを生成することです。これは pyrcc5 コマンドを使って次のようにして行われます。

pyrcc5 -o resources.py resources.qrc

注釈

Windows 環境では、コマンドプロンプトや Powershell から pyrcc5 を実行しようとすると、おそらく "Windows cannot access the specified device, path, or file [...]" というエラーが発生します。 最も簡単な解決策は OSGeo4W Shell を使用することですが、PATH 環境変数を変更したり、実行ファイルへのパスを明示的に指定することができる場合は、 <Your QGIS Install Directory> \bin\pyrcc5.exe に見つけることができるかもしれません。