はじめに
Python で作成したプログラムを実行形式にしてインストーラにしておくと、Python のインストールされていない環境で簡単に使えます。できれば、分かりやすいアイコンをデスクトップにでも置いておきたいです。
そこで、アイコン付き実行形式のインストーラの作り方を調べてみたので、忘れないようにまとておきます。
なお、ここではウィンドウが開かない(CUI の)プログラムを扱います。
アイコンをクリックするとコマンドプロンプトが立ち上がって実行される感じです。ただ、GUIでもインストーラの作成はおおよそ同様の流れになります。
ここでは例として、Scratch 2 オフラインに拡張ブロックを加えるためのヘルパーをアイコン付きインストーラにしてみます。
ファイル一式は GitHub に置きます。
準備
- cx_Freeze を
pip install cx_Freeze
などでインストール - Python で作成したプログラムが、
if __name__ == '__main__':
で始まるように変更 - コマンドラインで実行して動くことを確認
- アイコンを用意
- UUID を取得
2以降については、以下で実際の流れを説明していきます。
テスト用プログラムの説明
Scratch 2 のヘルパーに特化した内容も多いため、流れだけ見て飛ばしても結構です。
今回用いる Scratch 2 拡張用ヘルパーのコード (s2extest.py) を以下に示します。(リンク先と同じものですが、修正があれば随時リンク先の方を更新するかもしれません。)
- 元の解説ページのコードそのままではなく、クラスにしてあります。
-
s2extest.main()
で Scratch 2 からのリクエストを受け取るための HTTP サーバが立ち上がるようになっています。 - (注)変数は
volume
となっていますが、特にボリュームを調整する機能があるわけではありません。
# !/usr/bin/env python
# -*- coding: utf-8 -*-
"""
s2extest.py
"""
from aiohttp import web
class S2EXTEST:
""" Scratch 2 offline extension example """
def __init__(self):
self.volume = 0
async def handle_poll(self, request):
""" Handle polling from Scratch """
text = "volume " + str(self.volume) + "\n"
return web.Response(text=text)
async def handle_beep(self, request):
""" Handle beep request from Scratch """
print("play beep!")
print("\007")
return web.Response(text="OK")
async def handle_setvolume(self, request):
""" Handle set volume request from Scratch """
tmp_volume = int(request.match_info['vol'])
if tmp_volume >= 0 and tmp_volume <= 10:
self.volume = tmp_volume
print("set volume= " + str(self.volume))
else:
print("out of range: " + str(tmp_volume))
return web.Response(text="OK")
def main(self):
""" Main routine """
app = web.Application()
app.router.add_get('/poll', self.handle_poll)
app.router.add_get('/playBeep', self.handle_beep)
app.router.add_get('/setVolume/{vol}', self.handle_setvolume)
web.run_app(app, host='127.0.0.1', port=12345)
if __name__ == '__main__':
s2extest = S2EXTEST()
s2extest.main()
このプログラムでは aiohttp を使っているので、pip install aiohttp
としておいてから python s2extest.py
でテストできます。
使い方はおおよそ以下の流れです。詳細はこちらの解説を確認してください。
1. Scratch 2 オフライン版をインストールします。
2. シフトを押しながら [ファイル] を選んで [実験的なHTTP拡張を読み込み] を表示させます。
3. そこから JSON 形式のブロック定義ファイル (test.s2e) を開きます。
4. python s2extest.py
を立ち上げておけば、Scratch 側の[その他] のブロック(たとえば beep
)でテストできます。
アイコンの用意
表示サイズが変わってもアイコンがきれいに表示されるようにするには、複数の解像度の画像がまとまったアイコン画像ファイル(.ico 形式)を用意しておきます。
- 256x256のカラーの画像 (8bitぐらいでよい) を用意もしくは作成します。(今回は png 形式で用意しました。)
- 適当なアイコン作成ツールで読み込んで .ico 形式で保存します。
- 今回は Greenfish Icon Editor Pro を使ってみました。
- [ファイル] > [ファイルを開く] で上のカラー画像を読み込みます
- [アイコン] > [イメージからWindows用のアイコンを作成] を選び作成したい形式はデフォルトのままとします。
- [ファイル] > [名前を付けて保存] で保存します。(今回は
icon_256x256.ico
としました。
)
- icons というフォルダを作り、その下に作成した .ico 形式のファイルを入れておきます。
UUID の取得
UUID (GUID) を生成してくれるページはいろいろあります。Python でも import uuid
とすれば簡単に生成できるようです。
ただし、一度だけ取得しておくことに注意しましょう。毎回生成してはだめです。別のアプリケーションだと認識され、バージョンアップなどがうまくできなくなります。
インストーラの生成
cx_Freeze でインストーラを作るために、セットアップ用のファイル (setup.py) を作成します。
セットアップ用ファイルの用意
以下のようなファイル (setup.py) を用意します。取得した UUID は upgrade_code
に入れておきます。
アイコンのフォルダやファイル名が違う場合は変更しておきます。ここでは icons/icon_256x256.ico
とします。
name
, version
, description
, author
, url
などを書いておきます。
packages
には、必要に応じて Python のモジュールを入れます。実行時にすぐエラーで終了する(立ち上がったコマンドプロンプトが閉じてしまう)場合はここを見直すとよいかもしれません。(後述の「トラブルシューティング」も参考に。)
# -*- coding: utf-8 -*-
from cx_Freeze import setup, Executable
name = "s2extest"
version = "1.0.0"
description = "Scratch 2 extension test"
author = "memakura"
url = "https://github.com/memakura/scratch2-extension"
# UUIDは一度決めたら変更しない
upgrade_code = "{7A37B0C0-64E1-479D-A64C-6CCFCB2E1149}"
# ----------------------------------------------------------------
# セットアップ
# ----------------------------------------------------------------
shortcut_table = [
('DesktopShortcut', # Shortcut
'DesktopFolder', # Directory_
"s2extest", # Name
'TARGETDIR', # Component_
'[TARGETDIR]s2extest.exe', # Target
None, # Arguments
None, # Description
None, # Hotkey
None, # Icon
None, # IconIndex
None, # ShowCmd
'TARGETDIR', # WkDir
)
]
# Table dictionary
msi_data = {'Shortcut': shortcut_table}
# 追加モジュールで必要なものを packages に入れる
build_exe_options = {'packages': ["asyncio"],
'excludes': [],
'includes': [],
'include_files': ["icons/"]
}
bdist_msi_options = {'upgrade_code': upgrade_code,
'add_to_path': False,
'data': msi_data
}
options = {
'build_exe': build_exe_options,
'bdist_msi': bdist_msi_options
}
# CUI : None
base = None
# GUI : 'Win32GUI' if sys.platform == 'win32' else None
icon = "icons/icon_256x256.ico"
# exe にしたい python ファイルを指定
exe = Executable(script="s2extest.py",
targetName="s2extest.exe",
base=base,
icon=icon
)
# セットアップ
setup(name=name,
version=version,
author=author,
url=url,
description=description,
options=options,
executables=[exe]
)
インストーラの生成
-
ビルドします。以下のコマンドを実行すると、
dist
フォルダの下に.msi
形式のインストーラが出来上がります。> python setup.py bdist_msi
-
実行してみると、
Program Files
の下にs2extest
というフォルダができます。(通常はC:\Program Files\s2extest
になります。) -
デスクトップにもアイコンが出来上がります。
(右が今回作成したヘルパーのアイコンです。Scratch 2 のアイコンも左に並べてみます。)
設定ファイルやデモ用ファイルなど
インストール時に設定ファイルやデモ用ファイルを一緒に配布したい場合は、setup.py の build_exe_options
にある include_files
を書き換えます。
- デモ用ファイルとして、拡張ブロック定義ファイル (test.s2e) をいったん Scratch 2 で読み込んで保存したプロジェクトファイル (s2extest.sb2) を用意します。
- 本来はブロックの使い方のデモを少し入れておく方がよいかもしれませんが、今回はブロック定義ファイルを読み込む手間を省くためのプロジェクトファイルということで。
-
include_files
を以下のようにすれば、test.s2e と s2extest.sb2 がProgram Files\s2extest\00scratch
の下に一緒にインストールされます。
'include_files': [
"icons/",
("test.s2e", "00scratch/test.s2e"),
("s2extest.sb2", "00scratch/s2extest.sb2")
]
インストール後の実行方法
- デスクトップにできたアイコンから s2extest.exe を実行すると、ヘルパー(HTTPサーバ)がコマンドプロンプトで立ち上がります。
- (今回の例に特化した話ですが)インストール後に
Program Files\s2extest\00scratch\s2extest.sb2
のショートカットをデスクトップに作っておけば、Scratch 2 のプロジェクトが立ち上がって、拡張ブロックをすぐ使用できます。
トラブルシューティング
- インストール先のPCにて実行してもすぐに閉じてしまう場合:ショートカットのリンク先の先頭に
cmd /k
をつけるとコマンドプロンプトが閉じなくなります。これでデバグできるかもしれません。
KeyError: 'TCL_LIBRARY'
ビルドする際に KeyError: 'TCL_LIBRARY'
が出る場合は、Pythonと一緒にインストールされている Tcl/Tk が見つからないことが原因のようです。
を参考に、setup.py へ以下を追加しておきます。
import os.path
PYTHON_INSTALL_DIR = os.path.dirname(os.path.dirname(os.__file__))
os.environ['TCL_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tcl8.6')
os.environ['TK_LIBRARY'] = os.path.join(PYTHON_INSTALL_DIR, 'tcl', 'tk8.6')
参考ページ
- cx_Freeze のマニュアル
- https://github.com/makewitharduino/Scratio : アイコン付き setup.py の例
- http://www.lisz-works.com/entry/python-cx-freeze : GUI と CUI の切り替え