コンポーネント 1
QGIS Live Driver
Harnessから独立した汎用制御プラグイン — QGIS側の「手」。
qgis-plugin/qgis_live_driver/ は
UIを持たない制御専用プラグインで、実行中QGISを外部から遠隔操作する面を提供する。
QWebEngineView を持たないため、WebEngineを使わない任意のQGIS環境・
任意の他プラグインに対しても使える汎用部品。
pyeval でQGISプロセス内の任意Pythonを同期実行できるため、
PyQGISで書ける操作は全て制御可能: レイヤー/プロジェクト操作、
キャンバス、メニュー/アクション、qgis.utils.plugins 経由の他プラグイン、
Processing、QgsSettings、任意widgetの grab() による画像化 等。
さらに status/open_plugin/reload_plugin/bridge_call のnamedコマンドと、
plugin_id による他プラグインへのコマンド転送(ハブ機能)を持つ。
コマンド
| コマンド | 動作 |
|---|---|
pyeval |
QGISプロセス内で任意Pythonを同期実行し結果を返す。名前空間に iface / QgsProject / QgsApplication / driver を持つ → iface.activeLayer().name() や QgsProject.instance().mapLayers() 等で内部状態を直接取得・操作できる |
screenshot |
iface.mapCanvas().saveAsImage(path) で地図キャンバスをPNG保存。target:"mainwindow" でQGISメインウィンドウを grab() |
status |
pid / http_port / live_dir / ロード中プラグインと各driver・view有無を返す(生存確認・一覧用) |
open_plugin |
plugin_id のプラグインをload+startし、open_dialog があれば開く |
reload_plugin |
plugin_id のプラグインを sys.modules 掃除+reloadPlugin+open_dialog でホットリロード(QGIS再起動不要。自プラグイン自身も可) |
bridge_call |
plugin_id + method + args で対象プラグインのbridgeオブジェクトをプロセス内で直接呼ぶ。WebChannelを通らないためJSレンダラー飢餓中でも動作する。JSON文字列の返値はパースして返す |
eval / devreload |
viewを持たないため unsupported エラーを返す(ハングしない)。ただし plugin_id を付けると対象プラグインへ転送される(下記) |
plugin_idルーティング(ハブ機能)
eval / pyeval / screenshot / devreload に plugin_id を付けると、
そのプラグインのLiveDriver(plugin.driver/plugin._live)へコマンドが
転送され、結果は呼び出し元の results.jsonl に返る。
つまりこのドライバのlive dir / HTTPポート1本で全プラグインを制御できる。
- 転送先ドライバは
_handle(cmd, reply)のreply引数に対応している必要がある(本リポジトリ同梱の live_driver.py 実装を他プラグインに同梱すれば対応可) pyevalを転送すると対象プラグインの名前空間(そのview/driver)で実行される
制御可能な範囲(pyeval)
| 対象 | pyevalの例 |
|---|---|
| レイヤー/プロジェクト | iface.addVectorLayer(...) / QgsProject.instance().write() |
| キャンバス | iface.mapCanvas().setExtent(...) / .refresh() |
| メニュー/ツールバー | iface.actionZoomIn().trigger()、mainWindow().findChildren(QAction) で全アクション列挙・実行 |
| 他プラグイン | qgis.utils.plugins["<id>"] で他プラグインのオブジェクトに到達 |
| Processing | processing.run("native:buffer", {...}) |
| 設定 | QgsSettings().setValue(...) |
| 任意ウィジェットの画像化 | widget.grab().save(path)(pyeval経由) |
制約:
- 同期GUIスレッド実行 — pyeval実行中はGUIがブロックし、逆に再描画等でGUIビジーの間はポーリングが飢餓する(一時的)
- OSネイティブダイアログ — Qt外のダイアログはwidget introspection経由の間接操作。Qtモーダルは
QApplication.activeModalWidget()でaccept()/reject()可能 - 実マウス/実キー —
QMouseEvent/QTest.mouseClickのpostで模倣可能だが座標変換等の下ごしらえが必要 - Python API未露出の内部機能 — 稀だがC++ privateな振る舞いは不可
AIアシスタントパネル
プラグイン > QGIS Live Driver > AI アシスタント で、
LLM APIを接続してQGISを対話操作するチャット型ドックを開ける
(ai_panel.py + ai_client.py)。
モデルは上記コマンドに対応するツール群(qgis_pyeval /
qgis_screenshot / …)をfunction callingでループ実行し、
結果をチャットに返す。リモート制御トグルOFFでも動作する
(外部公開面を持たないパネル専用の内部ドライバを使う)。
- プロバイダ: OpenAI互換(
{base}/chat/completions+ function calling)とGemini({base}/models/{model}:generateContent+ functionDeclarations)。エンドポイントを変えればOllama・LM Studio・ OpenRouter等の互換サーバも使える。設定はプロバイダごとに別保存 - モデル選択: 編集可コンボ。隣の「取得」ボタンで
GET {base}/modelsから利用可能モデル一覧を取り込む (GeminiではgenerateContent非対応モデルを除外)。 一覧は種別(テキスト(画像入力可)/テキスト/画像生成/埋め込み/その他)で グループ化され、各項目に価格(入力/出力, $/1Mトークン)を併記。 価格はOpenRouter等が返すpricing/modality/context_lengthを優先し、無ければ主要モデルの既知価格表 (参考値)にフォールバック。既定エンドポイント利用時は「取得」前から 推奨モデル一覧を予め表示し、選択中モデルの種別・価格・コンテキスト長は 欄の下にも表示される - vision: 「スクリーンショットを画像として送信」をONにすると
qgis_screenshotの結果を画像としてモデルに返す (非対応モデルではAPIエラーになるためOFFに) - APIキー: OSの資格情報ストアに保存(Windows=DPAPI / macOS=キーチェーン / Linux=Secret Service)。エンドポイント・モデルは QSettingsにプロバイダ別キーで保持
接続面(4系統・全て同じdispatchへ)
- ファイルJSONL —
commands.jsonl追記 → QTimerが実行 →results.jsonl返却。 言語非依存の最下位輸送層(各行にtoken必須、後述) - HTTP REST —
POST 127.0.0.1:<port>/api/pyeval{"code":"..."}等。 MCPを知らないfunction calling系AI・curl・自作エージェント向け - MCP Streamable HTTP —
POST 127.0.0.1:<port>/mcp(JSON-RPC) - MCP stdio — 同梱
mcp_stdio.pyをspawnするだけ(stdlibのみ、spawn側にQGIS/Node不要)
②③はプラグイン内蔵の http.server(stdlibのみ・127.0.0.1・空きポート自動選択)が提供。
ワーカースレッドが受けた命令は driver.submit() でメモリキューに入り、
GUIスレッドのQTimerがファイルJSONLと同じ要領で実行する(dispatchは1本に統一)。
ポート番号・live dir・認証トークン等の接続情報は ~/.qgis-live/<pid>-qgis_live_driver.json に書き出され、
クライアントはここから自動検出できる(QGIS_LIVE_REGISTRYで変更可)。
このファイルと live_dir/token はユーザ限定ACLで作成される。
flowchart TB
subgraph EXT["外部クライアント(Harnessでも第三者アプリでも)"]
APP["任意言語のアプリ"]
LP["Harness: LivePage<br/>(live-drive.mjs)"]
HTTP["MCP/RESTクライアント<br/>(curl・AIエージェント等)"]
end
subgraph TR["輸送層"]
CMD["commands.jsonl"]
RES["results.jsonl"]
SRV["http.server 127.0.0.1:<auto><br/>/mcp + /api/*"]
end
subgraph QGIS["常駐QGISプロセス"]
LD["LiveDriver<br/>QTimer poll + メモリキュー"]
CORE["iface / QgsProject /<br/>他プラグイン / widget全般"]
LD --> CORE
end
APP & LP --> CMD
HTTP --> SRV
CMD --> LD
SRV -->|"submit()"| LD
LD --> RES
RES --> APP & LP
HTTP RESTエンドポイント
| エンドポイント | 内容 |
|---|---|
GET /api/health |
{"ok":true,"pid":...,"plugin":"qgis_live_driver"} |
GET /api/tools |
ツール仕様JSON(function calling系AIが読む用) |
GET /api/status |
status コマンドの結果 |
POST /api/cmd |
コマンドJSONをそのままdispatch({"type":"reload_plugin","plugin_id":"..."} 等すべて有効) |
POST /api/pyeval |
{"code":"...", "plugin_id":"..."} → {"result":...} |
POST /api/eval |
{"expression":"...", "plugin_id":"<id>"} → 対象プラグインへ転送(plugin_id無しなら unsupported) |
POST /api/screenshot |
{"path":"...","plugin_id":"...","target":"..."} → PNGバイナリ(Content-Type: image/png) |
POST /mcp |
MCP JSON-RPC (initialize/tools/list/tools/call/ping) |
MCP tools は qgis_<コマンド種別> の命名で、引数はコマンドJSONの
キーにそのまま対応する(plugin_id / target / method / args 等)。
ファイルプロトコル詳細
ドライバが起動すると <live_base>/qgis-<pid>/qgis_live_driver/ をポーリングする
(live base は WEBENGINE_LIVE_DIR env、またはメニュートグル利用時は
~/.qgis-live/run):
外部アプリ → commands.jsonl に1行1JSONを追記(各行に "token" 必須)
→ QTimer(200ms)が読み取り実行
→ results.jsonl に {id, result|error} を追記で返却
- 輸送層はファイルのみ。言語非依存(JSON Linesを追記・読取できれば何でもよい)
- 各行の
tokenはドライバが生成するセッショントークン。live_dir/tokenまたはレジストリJSONから読む。不一致は{"error":"unauthorized"}で破棄される commands.jsonlは append-only。切り詰めるとドライバのoffsetがずれてmid-line読み(JSONDecodeError+コマンド喪失)を起こす- 同一live dirへの同時アクセスは 単一クライアント 前提
外部公開(Copilot Studio等からの接続)
/mcp は Copilot Studio が対応する Streamable HTTP transport 互換。
本プラグインは localhost限定運用を設計前提とするが、
どうしてもクラウドから接続したい場合は以下の手順が使える(非推奨・自己責任)。
1. 認証キーと公開ツールの絞り込み(QGIS起動前にenvを設定)
全エンドポイントは常にトークン照合を要求する
(/api/health除く)。既定のキーは起動ごとに自動生成される
セッショントークン(レジストリJSON/live_dir/token参照)。
QGIS_LIVE_API_KEY を設定するとその固定キーも追加で受け付ける。
$env:QGIS_LIVE_API_KEY = "<ランダムなキー>"
# 推奨: 公開ツールを限定。pyevalは任意コード実行のため除外
$env:QGIS_LIVE_TOOLS = "status,screenshot"
npm run live:ensure
- 親シェルのenvは
live:ensure→ powershell →Start-Process経由でQGISへ引き継がれる。既にQGISがlive中の場合は再起動が必要(envは起動時に読む) - 認証は
Authorization: Bearer <key>/X-Api-Key:/api-key:のいずれかのヘッダで行う(不一致は401)。Host/Origin がloopback以外を指すリクエストは403で拒否されるため、トンネル経由のHostヘッダでは 403になる点に注意(外部公開は設計対象外) QGIS_LIVE_TOOLSはコマンド種別名のカンマ区切り(qgis_前置も可、*または未設定で全許可)。/mcpの tools/list・tools/call と/api/*の両方に効く- 有効状態は
~/.qgis-live/<pid>-qgis_live_driver.jsonのhttp_tools/mcp_urlで確認できる
2. HTTPSで公開(トンネル)
Copilot Studioはクラウドのため公開URLが必要。ローカルポートをトンネルする:
devtunnel host -p <port> --allow-anonymous
# または
ngrok http <port>
(TLS終端とFQDN払い出しはトンネル側が担い、認証は /mcp 側のAPIキーが担う)
3. Copilot Studio 側の設定
エージェントの Tools → Add a tool → New tool → Model Context Protocol で
MCP onboarding wizardを開き、Server URL に https://<トンネルFQDN>/mcp、
認証に API key を選んで QGIS_LIVE_API_KEY の値を入力する。
generative orchestration が有効なエージェントが必要。
追加後は qgis_status 等がツールとして使える。
REST面を使う場合は Power Apps カスタムコネクタ経由で /api/* を包む方法もある。
Deploy Mode ダイアログ(開発/利用の切替)
プラグイン > QGIS Live Driver > 配置モード で、Harness管理下の
deployモードをQGIS内から確認・切替できる(Harnessリポジトリ利用時のみ。
切替は内部で deploy-plugin.ps1 を起動する)。
- 状態表示: 現在モード(配置実態優先。宣言と乖離時は
(declared: ...)を併記) + 各管理プラグインの実配置形態 (junction/dir/未配置)を一覧。利用版が退避中なら(+parked)と表示 - 開発モードへ: 既存の実体プラグイン(利用版)を
profiles/<profile>/qgis_live_parked/へ退避してからjunctionを張る (repo編集が直接届く開発状態) - 利用モードへ: junctionを外して退避版をそのまま復元 (退避も既存実体も無ければ未配置のまま)
- リリース: 「現在のrepoを利用版としてインストール」ボタンで repoの現状を利用版として配置(旧parkedは破棄)
- 「切替後にプラグインをリロード」(既定ON): 切替直後に非ドライバの プラグインを即リロードし、新しい配置のコードを反映する
切替はUI操作のみ(AIからの書込は不可)。bridgeメソッド deployMode() で
状態をJSONで読取専用に公開している。詳細な対応関係は
使い方・deployモード を参照。
GeoSuite共有ツールバー
管理対象プラグインのツールバー項目は GeoSuite ツールバーに 集約される(どのプラグインが先に有効化されても同じツールバーに載る find-or-create方式)。ドライバ有効中は常時表示が強制され (表示メニューからの非表示化は無効)、先頭にはモードバッジ (開発=「開発モード」の赤背景白文字、利用=歯車アイコンのみ、 5秒毎に自動更新)を載せ、 クリックすると配置モードダイアログを開く。
Harnessなしでの単独導入
Driverの利用にこのリポジトリは要らない:
# このリポジトリからなら一括deploy(任意)
powershell -ExecutionPolicy Bypass -File scripts/deploy-plugin.ps1 -Install
# 手動でも可: qgis-plugin/qgis_live_driver/ を
# python/plugins/qgis_live_driver/ へコピーし、QGISのプラグイン管理で有効化
あとは WEBENGINE_LIVE_DIR を設定した状態でQGISを起動するか、
プラグインメニューの QGIS Live Driver → リモート制御 (localhostのみ) を
ONにするだけで、live dirとレジストリJSONが作られ、上記4系統のいずれでも接続できる
(未設定・OFFなら何もしない無害なプラグイン)。
セキュリティ:
pyevalは任意コード実行であり、本プラグインは localhost限定のリモート制御面として以下で保護される: HTTPは127.0.0.1バインドのみ・全エンドポイントでセッショントークン必須・ Host/Originがloopback以外なら拒否(DNSリバインド・ブラウザ経由対策)。 ファイルJSONLも各行のtoken照合必須。トークンはユーザ限定ACLの ファイル(live_dir/token・レジストリJSON)にのみ保存される。 脅威モデルの境界: 同一ユーザ権限の任意プロセスはトークンを 読めるため防げない(あらゆるlocalhostサービスの限界)。 信頼境界はローカルユーザアカウントである。 詳細は セキュリティモデル を参照。