QGIS3 のプラグインから別のUIを表示したい
Plugin Builder
を使って生成した QGIS3 のプラグインで、元の .ui ファイルではなく別の .ui ファイルを新しく作成し、これを表示する方法をまとめます。
具体的には DockWidget のプラグインを作成、そこから別のダイアログを表示させるという内容です。
手順
Plugin Builder で Dock を作成する
ということで Plugin Builder で DockWidget を作成してボタンを配置します。プラグイン作成の手順は以下のページを参考にどうぞ。
[QGIS] プラグインを作って動かすまでの手順 │ Web備忘録
ということで作成したプラグインのデータは以下のような感じ。
プラグインの名前はシンプルに parent としました。この親から子のUIを呼び出す感じですね。
parent_dockwidget_base.ui ファイルを編集して UI を構築します。Qt Designer
を起動して .ui のファイルをドロップします。
ボタンを配置しただけのシンプルな UI です。ボタンの名前は showDialogButton
です。
表示するダイアログの .ui ファイルを作成する
Qt Designer で新しいフォームを作成します。とりあえずテンプレート “Dialog with Buttons Button” で作成します。デフォルトからラベルを追加しただけです。
ファイル名は “child_dialogwidget.ui” としました。これをプラグインのフォルダに放り込みます。
このダイアログをプラグインの Python コードから起動します。
child_dialogwidget.py を作成
ダイアログのウィジェットクラスを用意します。ファイル名は .ui ファイルに合わせて child_dialogwidget.py
とします。
内容は以下の通りです。
import os
from PyQt5 import QtWidgets, uic
from PyQt5.QtCore import pyqtSignal
FORM_CLASS, _ = uic.loadUiType(os.path.join(
os.path.dirname(__file__), 'child_dialogwidget.ui'))
class ChildDialogWidget(QtWidgets.QDialog, FORM_CLASS):
closingPlugin = pyqtSignal()
def __init__(self, iface, parent=None):
"""Constructor."""
super(ChildDialogWidget, self).__init__(parent)
self.iface = iface
self.setupUi(self)
self.initUI()
def closeEvent(self, event):
"""×ボタン押下で閉じたときのイベント"""
self.closingPlugin.emit()
event.accept()
def initUI(self):
pass
コンストラクタで iface を受け取っていますが、不要であればなくてもいいです。parent=None は何かわかりませんがこれで動くのでまあいいでしょう。このコードはダイアログのプラグインで生成されるコードを参考にしています。
initUI()
は何かしら初期化するときのコードを書くように用意しました。
parent.py にダイアログ表示のコードを追加
では parent.py
にダイアログを呼び出すコードを書いていきます。
from .child_dialogwidget import ChildDialogWidget
class Parent:
# ...
def run(self):
# ..
if not self.pluginIsActive:
# ..
# 以下追記
self.dockwidget.showDialogButton.clicked.connect(self.showChildDialog)
def showChildDialog(self):
self.dialog = ChildDialogWidget(self.iface)
self.dialog.setModal(True)
self.dialog.show()
とりあえず、run()
の末尾に表示用のコードを追加しましょう。ダイアログのインスタンスを生成し、モーダル表示に設定します。そして show()
で表示です。
self.dialog
でダイアログのインスタンスを保持しています。こうせずにローカル変数にしてしまうと、ダイアログが一瞬だけ表示されてすぐに消えます。すぐに破棄されるからでしょうか。
これで動作します。
ダイアログの戻り値を取得する
ButtonBox には OK, キャンセルボタンが配置され、それぞれロールが設定されています。どちらを押してもダイアログが閉じますが、以下のようにして呼び出し元、ダイアログ双方でイベントを捕まえて処理を実行できます。
以下、print コンソールに文字をはいているだけです。
# parent.py
self.dialog.buttonBox.accepted.connect(lambda: print("accept"))
self.dialog.buttonBox.rejected.connect(lambda: print("reject"))
# child_dialogwidget.py
self.buttonBox.accepted.connect(lambda: print("accept"))
self.buttonBox.rejected.connect(lambda: print("reject"))
ただ、上記の方法だとダイアログを閉じるのをキャンセルすることができません。
例えば OK ボタンを押された後、なにがしかのチェックを行い問題なければ閉じるし、そうでなければ閉じないというような処理が実装できません。できるのかもしれませんが方法を見つけられませんでした。
なので自分でボタンをそれぞれ配置してやれば、やりたいことは実現できるので問題ありません。
以上。
コメントを書く