PythonのDLL群を使ってPythonコードを実行するような
ペイントソフト用のプラグインを作っていて
嵌まったので、ノート替わりにやったことを公開します。
■背景
私が公開しているフリーソフトに
Pythonを使っているソースコードを
プラグインを作成しようとしたのですが
本体がインストーラ不要であることがひとつの長所なのに
「プラグインのためにPythonのインストーラを使わせるなんて
言語道断」…という、わがままによって
エンドユーザーにPythonのインストーラを使わせずに
Pythonのコードを自前のプログラムから実行する方法を模索しました。
■結論
Windows環境であれば、以下、5点を実施すれば可能です。
- Portable Pythonをダウンロードする
- ダウンロードしたPortalbe PythonのConsole-Launcher
を起動して「python -m pip install --upgrade pip」しておく - 必要なPythonのライブラリを「python -m pip install ○○」
で使えるようにしておく - 配布物にPortable PythonのAppフォルダの中身を追加する
- C言語のソースコードでPy_Initialize()する前に
setenv関数等で以下の環境変数を設定する
PATH、PYTHONPATH、PYTHONSTARTUP
(この環境変数は呼び出し元のプロセスの中でのみ有効ですので
ユーザーの環境変数を汚しません)
ちゃんと動く …ハズ。
■手順詳細
1-1. Portable Pythonのサイト(Sourceforge)から
使いたいバージョンのPythonをダウンロードします。
(Portable PythonはUSBメモリに入れて持ち歩くことを
想定したものなのでexe形式の解凍実行ファイルを
ダウンロードすることになります)
1-2. ダウンロードしたexeファイルを実行して
任意のフォルダに解凍します。
2-1. 解凍されたフォルダのConsole-Launcher○○を起動する。
(○○は64bit用なら64、32bit用なら86です)
2-2. コマンド「python -m pip install --upgrade pip」
でpipを最新の状態にしておく。
3. コマンド「python -m pip install ○○」で
必要なライブラリをインストールしておく。
(例えばnumpyなら
「python -m pip install numpy」になります)
これで、配布に必要なファイルが揃います。
4. 配布時にはPortable Pythonを解凍したフォルダにある
「App\Python」フォルダも一緒に配布する必要があります。
5. Python のC API経由でPythonのスクリプトを実行する為には
Pythonの初期化時に必要なライブラリをPYTHONPATHに
設定されたフォルダが参照されますので
Py_Initialize (またはPy_InitializeEx)を呼び出す前に
PYTHONPATHの値を設定する必要があります。
5-1. PYTHONPATHには次の値を設定します。
私は設定にsetenv()関数を使いました。
(1) PATHに Portable Pythonのフォルダ内の
「Python」フォルダのパスを追加
(2) PYTHONPATHにPortable Pythonのフォルダ内の
「Python」フォルダのパスを追加
(3) PYTHONPATHにPortable Pythonのフォルダ内の
「Python\DLLs」フォルダのパスを追加
(4) PYTHONPATHにPortable Pythonのフォルダ内の
「Python\Lib」フォルダのパスを追加
(5) PYTHONPATHにPortale Pythonのフォルダ内の
「Python\Lib\site-packages」フォルダのパスを追加
(6) PYTHONSTARTUPにPortable Pythonのフォルダ内の
「Python\Lib\ppp.py」ファイルのパスを追加
…6つ中、4つがPYTHONPATHですね…
5-2. C言語で書いた関数を呼び出したい場合は
PyMODINIT_FUNC 関数名(void)の関数を作成し、
その中で
(1) static struct PyMethodDef 配列変数名[]で
{"Pythonで呼び出すときの関数名", C言語の関数ポインタ,
METH_VARARGS, "関数のヘルプ文字列"}
の構造体配列を作成
(2) static struct PyModuleDef 変数名で
{PyModuleDef_HEAD_INIT, "適当な文字列",
NULL, -1, (1)の変数}の構造体変数を作成
(3) import_array() 関数を呼び出す。
(NumPyを使わない場合は不要。
これをしないとC言語で書いたPython用関数の中で
PyArg_ParseTuple関数にPyArray_TYPEを指定した際に
クラッシュする)
(4) return PyModule_Create(引数は(2)の変数のアドレス)
の処理を行います。 (1)、(2)の変数はstaticなローカル変数の代わりに
グローバル変数でもO.K.
5-3. Py_Initialize()関数を呼び出す。
…あとはPyObject_CallObject関数やPyObject_CallMethod関数などを使えば
Pythonの中のインスタンス作成やメソッド呼び出しを行えます。
5. 以降の内容は「C言語からPythonの呼び出し」等で検索して出てきた
結果を参考にした方が良いかもしれませんね。
説明は以上です。何かご質問などありましたら、
お気軽にコメントまでどうぞ。