Help us understand the problem. What is going on with this article?

プラグインシステム的なものについて考える ~動的import~

More than 1 year has passed since last update.

MayaとかPhotoshopとかで機能を追加するための『プラグイン』システムがありますが、
そういうのを自作のツールにも設けられないか、というメモです。

importlib

具体的には「importlib」モジュールを使います。

このモジュールの「import_module」関数に モジュール名 を文字列で渡すことで、

import hogehoge

という書き方によらないモジュールのインポートができます。
文字列を受け取ってくれるので、見つけたモジュールを随時代入しながらインポートさせられそうです。

使うのは import_module だけなので、このようにインポートして使います。

from importlib import import_module

(importという文字列の登場頻度の高さ。。。)

引っ掛かりポイント

1: 絶対パスで渡そうとする

てっきりPythonファイルを渡すものだと勘違いしており、このように書いていました。

pluginPath = 'path/to/plugin'

for plugin in os.listdir(pluginPath):
    mod_path = os.path.join(pluginPath,plugin)
    mod = import_module(mod_path,)

ダメでした。
モジュール名を渡すのです。
また、そのためにはプラグイン用Pythonファイルを置いている場所を
sys.path.appendに追加しておく必要があります。
かつ、見つけたPythonファイルも拡張子は除去しておく必要があります。
渡すのはモジュール名だけです。

pluginPath = 'path/to/plugin'
sys.path.append(pluginPath)

for plugin in os.listdir(pluginPath):
    mod_name = os.path.splitext(plugin)[0]
    mod = import_module(mod_name,)

これで、プラグインフォルダにPythonファイルを追加すればインポートしてくれるようになりました。

2: pycが邪魔

インポートしてくれるようになりましたが、
インポートできたらできたで、pyc作られます。読んでほしくありません。

# 前略

for plugin in os.listdir(pluginPath):
    split_name = os.path.splitext(plugin)
    if split_name[1] == '.pyc':
        continue
    mod = import_module(split_name[0],)

見つけたプラグイン分だけボタンを増やす

あとは見つけた分だけボタンを配置します。

plugin.py の仕様

仕様というほどでもありませんが、
プラグインの機能を利用するための関数をあらかじめお約束しておきます。
exec とか doIt とか run とかが考えられます。

また、ボタンに記載するラベルもモジュールに持たせておきましょう。

plugin.py
LABEL = 'create poly sphere'

"""
some your scripts...
"""

def exec():
    pass

# or

def doIt():
    pass

# or

def run():
    pass

#-----------------------------------------------------------------------------
# EOF
#-----------------------------------------------------------------------------

module_name.exec (など)をボタンにconnectします

ボタンへconnect

QPushButtonをつくり、.clicked.connect にさっき決めた実行用関数を渡します。

btn = QtGui.QPushButton( mod.LABEL )
btn.clicked.connect(mod.exec)

これを、モジュール読んでるfor文に加えます。

まとめ

import os
import sys

from PySide import QtCore, QtGui
from importlib import import_module

pluginPath = 'path/to/plugin'
sys.path.append(pluginPath)

for plugin in os.listdir(pluginPath):
    split_name = os.path.splitext(plugin)
    if split_name[1] == '.pyc':
        continue
    mod = import_module(split_name[0],)

    btn = QtGui.QPushButton( mod.LABEL )
    btn.clicked.connect(mod.exec)

こういう感じのをGUIつくってるスクリプトのどこかにいい具合にいれます

結局一番時間かかったのは
import_moduleに絶対パス渡そうとして「通らないなー」って唸ってたあたり
とpluginを pluing とタイポしてたあたり でした…。

ドキュメントよく読もう!


GUIの部分についてはこちらもどうぞ

参考?

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
Comments
No comments
Sign up for free and join this conversation.
If you already have a Qiita account
Why do not you register as a user and use Qiita more conveniently?
You need to log in to use this function. Qiita can be used more conveniently after logging in.
You seem to be reading articles frequently this month. Qiita can be used more conveniently after logging in.
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
ユーザーは見つかりませんでした