この記事は2018年3月くらいに一時公開していたものの再投稿です。
目標
「QGIS プラグインから他のプラグインを呼び出してみる」を書いていたらプラグインの動的ロードと組み合わせたら色々できるのではないかと閃いたので試してみたいと思います。
ついでに動的ロードしたプラグイン同士をシグナルで繋げて動かしてみよう。
下準備
Githubにテスト用のダミープラグインを用意しました。ダウンロードはこちらです。
QGIS3のプラグインフォルダにaaa_plugin
、bbb_plugin
というフォルダを作成してその中にダウンロードしたソースファイルをコピーしておきます。
Pythonコンソールから動的ロード
QGISの[プラグイン]メニューから[Pythonコンソール]を起動します。
まずは現在読み込まれているプラグインリストを確認してみましょう。
>>> qgis.utils.plugins.keys()
dict_keys(['pluginbuilder3', 'plugin_reloader', 'db_manager', 'processing'])
aaa_plugin
もbbb_plugin
も読み込まれていませんね。
ではaaa_plugin
をロードしてみます。
>>> plugin_name = 'aaa_plugin'
>>> qgis.utils.loadPlugin(plugin_name)
True
>>> qgis.utils.startPlugin(plugin_name)
True
>>> qgis.utils.plugins.keys()
dict_keys(['pluginbuilder3', 'plugin_reloader', 'db_manager', 'processing', 'aaa_plugin'])
aaa_plugin
がロードされてplugins
に追加されていることが確認できました。
ではaaa_plugin
のメソッドを叩いてみましょう。
>>> plugin = qgis.utils.plugins[plugin_name]
>>> plugin.handle_trigger('dynamic_load')
引数に与えた文字列がメッセージダイアログで表示されました。
問題なくプラグインのロードができたようです。
プラグイン内で動的ロード&シグナル
今度はプラグイン(aaa_plugin)内で他のプラグイン(bbb_plugin)をロードして、ロード先からロード元のシグナルにコネクトして、ロード元のシグナルを受けてロード先がメッセージを表示するってのをやってみます。
言葉で書くと訳が判らないのでなんちゃってシーケンス図を貼っておきます。
プラグインを動的ロードするloadAndExec
とカスタムシグナルを使ってシグナルを受け渡す、emitMessage
とconnectTrigger
を追加します。
Gitからソースをダウンロードした方は、適宜コメント箇所を外してください。
カスタムシグナルの使い方は以下の記事を参考にさせていただきました。
PyQt5とpython3によるGUIプログラミング[1]
ソースはこんな感じになります。
import os
from PyQt5.QtCore import QSettings, QTranslator, qVersion, QCoreApplication, pyqtSignal, QObject
from PyQt5.QtGui import QIcon
from qgis.utils import plugins, loadPlugin, startPlugin
from PyQt5.QtWidgets import QAction, QMessageBox
# Initialize Qt resources from file resources.py
from .resources import *
class CustomSignal(QObject):
mySignal = pyqtSignal(str)
class AAA:
myObject = CustomSignal()
"""QGIS Plugin Implementation."""
def __init__(self, iface):
self.iface = iface
self.plugin_instance = None
def initGui(self):
pass
def unload(self):
pass
def run(self):
pass
def loadAndExec(self, plugin_name, function_name, function_param):
plugin_list = plugins.keys()
if not plugin_name in plugin_list:
# ロード実行
try:
loadPlugin(plugin_name)
startPlugin(plugin_name)
self.plugin_instance = plugins[plugin_name]
# メソッド実行
eval("self.plugin_instance." + function_name + "('" + function_param + "')")
except:
QMessageBox.critical(self.iface.mainWindow(), 'error', "%s error" % plugin_name)
def connectTrigger(self, plugin_name):
self.parent_plugin_instance = plugins[plugin_name]
self.parent_plugin_instance.myObject.mySignal.connect(self.handle_trigger)
def handle_trigger(self, message):
QMessageBox.information(self.iface.mainWindow(), 'info', "%s" % message)
def emitMessage(self, message):
self.myObject.mySignal.emit(message)
では一旦プラグインの状態をリセットしたいのでQGISを再起動してからPythonコンソールを開き直します。
aaa_plugin
をロードしてインスタンスを取得するところまでは同じですが、今度はloadAndExec
を実行してbbb_plugin
をロードしてconnectTrigger
メソッド実行させます。
>>> plugin_name = 'aaa_plugin'
>>> qgis.utils.loadPlugin(plugin_name)
True
>>> qgis.utils.startPlugin(plugin_name)
True
>>> plugin = qgis.utils.plugins[plugin_name]
>>> plugin.loadAndExec('bbb_plugin', 'connectTrigger', plugin_name)
これでbbb_pluginからaaa_pluginにコネクトできました。
aaa_pluginのシグナルを起こしてみましょう。
>>> plugin.emitMessage('test')
引数に与えた文字列がメッセージダイアログで表示されました。
目的達成です。
最後に
良い感じに連携が取れることが確認できました。
QGISを使った小中規模の業務システムならこの仕組みを使うことで柔軟な設計ができそうです。
以上です。
本記事のライセンス
この記事は クリエイティブ・コモンズ 表示 4.0 国際 ライセンスの下に提供されています。