smellman's Broken Diary

クソみたいなもんです

2025/03/11の日記 - QGISプラグインをVSCodeでデバッグ実行してみる

QGISVSCodeデバッグ実行ができるようになりました。

環境はmacOS + QGIS 3.42.0です。

QGIS側のセットアップ

QGISでまずはdebugvsプラグインをインストールします。

github.com

QGISプラグインリストからdebugvsを選んでインストールします。

ですが、このままだと動作しません。

まずQGISpython環境にdebugpyをインストールします。

/Applications/QGIS.app/Contents/MacOS/bin/pip3 install debugpy

次に、debugvsに弊社長瀬さんが開発したパッチを当てます。

cd ~/Library/Application\ Support/QGIS/QGIS3/profiles/default/python/plugins/debug_vs
curl https://patch-diff.githubusercontent.com/raw/lmotta/debug_vs_plugin/pull/18.patch | patch -p 1

Windowsの方は頑張ってパッチを当ててください。

ここで一旦QGISを落としておきます。

デバッグプラグインの準備

今回はサンプルとして qgis-plugin-qmapcompareを利用します。

github.com

では、適当な所にチェックアウトしてからデバッグ実行ができるまで設定しましょう。なお、途中でVSCodeを開いています。

git clone git@github.com:MIERUNE/qgis-plugin-qmapcompare.git
cd qgis-plugin-qmapcompare
code .
cd ~/Library/Application\ Support/QGIS/QGIS3/profiles/default/python/plugins/
ln -s PATH_TO/qgis-plugin-qmapcompare .

次にQGISを起動してプラグインリストを開いてQMapCompareを有効にします。

この後、プラグインのメニューからEnable Debug for Visual Studioメニューからリモートでバッグを可能にします(パッチが当たってないと新しいQGISが起動してしまい、デバッグもできないので必ずパッチを上げてください)。

次にVSCodeを開いて、.vscode/launch.jsonに以下のコードを書きます。

{
    "version": "0.2.0",
    "configurations": [
        {
            "name": "Attach to QGIS",
            "type": "debugpy",
            "request": "attach",
            "connect": {
                "host": "localhost",
                "port": 5678
            },
            "pathMappings": [
                {
                    "localRoot": "${workspaceFolder}",
                    "remoteRoot": "${workspaceFolder}",
                }
            ],
            "justMyCode": true
        }
    ]
}

あとは適当なブレイクポイントを設定してから、VSCodeの実行とデバッグでAttach to QGISを選択します。

今回は起動時の動作を見たかったので QMapCompareの__init__の中にブレイクポイントを仕掛けてから、プラグインリローダーを使ってプラグイン自体のリロードをしてみます。

https://i.gyazo.com/787dfde0aff2253c03e78a62863ddc13.png

うまくデバッグができるようになりました。変数にはちゃんとifaceのオブジェクトがなんなのかとかしっかり表示されます。

型がチェックしやすいので型安全なコードも書きやすくなるのではないでしょうか?

ポイントとしてはremoteRootが何故か動かないのでローカルのファイルとpluginsフォルダにあるファイルをSymbolic linkで同期するようにすることです。

なお、これから作るって言う方はいかのように一旦必要なメソッドをpassするようにしたPythonファイルからデバッグしながら作り上げていくとよいでしょう。

from login_dialog import LoginDialog
from qgis.gui import QgisInterface
from typing import Optional

class Lobsta:
    """QGIS lobsta plugin main class."""

    def __init__(self, iface: QgisInterface):
        self.iface = iface

        # declare instance attributes
        self.actions = []
        self.menu = "Lobsta"

        self.toolbar = self.iface.addToolBar("Lobsta")
        self.toolbar.setObjectName("Lobsta")

        self.dialog: Optional[LoginDialog] = None
    
    def initGui(self):
        pass

    def unload(self):
        pass

ポイントはinitGuiとunloadを最低限実装しているところです。unloadがないとプラグインのリロードで詰まります(unloadが呼び出せないのでリロード時のunload処理が動かない)。

これでQGISの開発もがりがりできますね!