QuickIR を cx_Freeze で Windows の EXEファイルにしてみた(2)
Windows GUIプログラムをEXE化してみる
5. Windowsでtkinterを使ったGUIアプリを.exe化してみる
さて、目標に近づくため話を Windows GUIアプリの EXE化に移そう。以後は Windows 10、Python 3.5.2+cx_Freeze 5.0 で作業する。
1) サンプルプログラム("SimpleTkApp.py" )
これは cx_Freeze に付属していた tkinter を使用した簡単なサンプルプログラムを少し手直ししたものだ。ポイントは最後の__name__ を判定しているif文だ。__name__ が'simpleapp__main__' の場合にもmain() 関数を呼び出すようにしている。(すなはち、作成するEXEファイル名は"simpleapp.exe" でなければならない。)
#!/usr/bin/env python
# -*- coding: utf-8 -*-
try:
from tkinter import Tk, Label, Button, BOTTOM
except ImportError:
from Tkinter import Tk, Label, Button, BOTTOM
def main():
root = Tk()
root.title('Button')
Label(text='I am a button').pack(pady=15)
Button(text='Button').pack(side=BOTTOM)
root.mainloop()
if __name__ == '__main__' or __name__ == 'simpleapp__main__':
main()
|
2) setupスクリプト("cxFreeze_setup.py" )
# -*- coding: utf-8 -*-
import sys
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
includes = []
include_files = []
packages = ['os', 'sys', 'tkinter']
excludes = []
build_exe_option = {
'includes': includes,
'include_files': include_files,
'packages': packages,
'excludes': excludes
}
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
exe = [
Executable('SimpleTkApp.py', base=base, targetName='SimpleApp.exe')
]
setup(name='Simple_Tkinter',
version='0.1',
description='Sample cx_Freeze Tkinter script',
options={'build_exe': build_exe_option},
executables=exe)
|
今度は base を'Win32GUI' に置き換える if文は重要だ。こうしないと Windows の GUIアプリは作られない。それ以外に、'tkinter' をexcludesリストから packagesリストへ移動している。tkinter を使用しているので当然だ。
また、targetNmaeで作成するEXEファイル名を"SimpleApp.exe" に指定している。先ほどの__name__ と違って一部が大文字になっているけど大丈夫。Windowsではファイル名の大文字、小文字は区別されないので、cx_FreezeはEXEファイル名を全て小文字化してプログラムに渡すようだ。
3) EXE化してみる
コマンドプロンプトを開き、以下のように入力して EXE化させてみる。
C:¥> python cxFreeze_setup.py build
running build
running build_exe
Traceback (most recent call last):
〜略〜
tclSourceDir = os.environ["TCL_LIBRARY"]
File "C:¥Python35¥lib¥os.py", line 725, in __getitem__
raise KeyError(key) from None
KeyError: 'TCL_LIBRARY'
|
エラーが出た。"TCL_LIBRARY" 環境変数が設定されていないことが原因のようだ。これ以外にも"TK_LIBRARY" 環境変数も必要で、これらの環境変数で Tcl/Tk の場所を指定してやる必要がある。
Tcl/Tk は Python と一緒にインストールされている筈なので、Python をインストールした場所の中を探す。Python インストールした際、特にインストール場所を変更しなかったのであれば"C:¥Python35" とか、"C:¥Users¥(ユーザ名)¥AppData¥Local¥Programs¥Python¥Python35" にインストールされていると思う。その中に tclフォルダがあれば、そこが Tcl/Tk の場所だ。tclフォルダ配下に"tcl8.6", "tk8.6" と言った名前のフォルダがある。
(tclフォルダが無いのなら、おそらくインストール時にカスタム設定で Tcl/Tk を除外したのだろう。その場合は再インストールで Modify しよう。)
"TCL_LIBRARY", "TK_LIBRARY" 環境変数を以下のように設定して、
C:¥> set TCL_LIBRARY="C:¥Python35¥tcl¥tcl8.6"
C:¥> set TK_LIBRARY="C:¥Python35¥tcl¥tk8.6"
|
再度コマンドを入力しても良いが、ここではsetupスクリプトの中に書いてしまおう。Pythonのインストール場所は変わることはないのだから、EXE化のたびに環境変数を設定するのも面倒だし、このsetupスクリプトは今後他のスクリプトでも使用する雛型になるものだからだ。
setupスクリプトを以下のように修正する。
# -*- coding: utf-8 -*-
import sys
import os
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
python_dir = r'C:¥Python35' # Python installed directory
includes = []
include_files = []
packages = ['os', 'sys', 'tkinter']
excludes = []
build_exe_option = {
'includes': includes,
'include_files': include_files,
'packages': packages,
'excludes': excludes
}
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
os.environ['TCL_LIBRARY'] = python_dir + r'¥tcl¥tcl8.6'
os.environ['TK_LIBRARY'] = python_dir + r'¥tcl¥tk8.6'
exe = [
Executable('SimpleTkApp.py', base=base, targetName='SimpleApp.exe')
]
setup(name='Simple_Tkinter',
version='0.1',
description='Sample cx_Freeze Tkinter script',
options={'build_exe': build_exe_option},
executables=exe)
|
Windowsのパス区切り文字「¥」はエスケープ文字でもあるので、普通の文字列内で文字の「¥」として扱ってもらうには「¥¥」と2つ重ねて書く必要がある。これは面倒なので文字列の前に rを付けて RAW文字列にしている。こうすると単独の「¥」が現れてもエスケープ文字ではなく、文字の「¥」として扱ってくれる。
python のインストールパス名を python_dir で指定し、os.environ 関数で"TCL_LIBRARY", "TK_LIBRARY" 環境変数に設定するようにした。
これでエラーが出ることなく、EXEファイルが作られるはずだ。
4) 作成された EXEファイルを実行してみる
C:¥> python cxFreeze_setup.py build
|
がエラーなく終了すると、"build¥exe.win32-3.5" とか"build¥exe.win-amd64-3.5" などと言った名前のフォルダ配下に
"SimpleApp.exe" が作られているはずなので、これをダブルクリックして実行してみる。
すると、今度は以下のようなエラーダイアログが出た。
今度はDLLが見つからないと言っている。
原因をネットで検索すると、Tcl/Tk の DLLを一緒に EXE実行環境に入れてやる必要があるそうだ。Tcl/Tk の DLLは "Pythonのインストールパス¥DLLs"(例えば"C:¥Python35¥DLLs" )配下にある。一緒に入れたいファイルは include_filesリストで指定する。よってsetupスクリプトは以下のようになる。
# -*- coding: utf-8 -*-
import sys
import os
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
python_dir = r'C:¥Python35' # Python installed directory
includes = []
include_files = [python_dir + r'¥DLLs¥tcl86t.dll',
python_dir + r'¥DLLs¥tk86t.dll']
packages = ['os', 'sys', 'tkinter']
excludes = []
build_exe_option = {
'includes': includes,
'include_files': include_files,
'packages': packages,
'excludes': excludes
}
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
os.environ['TCL_LIBRARY'] = python_dir + r'¥tcl¥tcl8.6'
os.environ['TK_LIBRARY'] = python_dir + r'¥tcl¥tk8.6'
exe = [
Executable('SimpleTkApp.py', base=base, targetName='SimpleApp.exe')
]
setup(name='Simple_Tkinter',
version='0.1',
description='Sample cx_Freeze Tkinter script',
options={'build_exe': build_exe_option},
executables=exe)
|
再び EXE化させて、出来上がった"SimpleApp.exe" をダブルクリックして実行してみる。"I am a button" というラベルと「Button」ボタンがあるウィンドウが表示されたら成功だ。
これでやっと cx_Freeze の setupスクリプトの雛型が完成したことになる。
6. 最終的な setupスクリプト
最終的に出来上がった cx_Freeze の setupスクリプト(雛形)を示す。
# -*- coding: utf-8 -*-
import sys
import os
from cx_Freeze import setup, Executable
# Dependencies are automatically detected, but it might need fine tuning.
python_dir = r'C:¥Python35' # Python installed directory
includes = []
include_files = [python_dir + r'¥DLLs¥tcl86t.dll',
python_dir + r'¥DLLs¥tk86t.dll']
packages = ['os', 'sys', 'tkinter']
excludes = ['email', 'html', 'http', 'urlib',
'logging', 'ctypes', 'distutils',
'multiprocessing', 'pydoc_data',
'test', 'unittest',
'xml', 'xmlrpc']
build_exe_options = {'includes': includes,
'include_files': include_files,
'packages': packages,
'excludes': excludes,
'optimize': 2}
base = None
if sys.platform == 'win32':
base = 'Win32GUI'
os.environ['TCL_LIBRARY'] = python_dir + r'¥tcl¥tcl8.6'
os.environ['TK_LIBRARY'] = python_dir + r'¥tcl¥tk8.6'
exe = [
Executable('Sample.py', base=base, targetName='Sample.exe', icon='sample.ico')
]
setup( name = 'sample',
version = '1.0',
description = 'sample application',
options = {'build_exe': build_exe_options},
executables = exe )
|
ポイントは、
- include_filesリストに Tcl/Tk のDLLを入れる
"TCL_LIBRARY", "TK_LIBRARY" 環境変数で Tcl/Tkの場所を指定する
- 作成する EXEファイルの名前を targetName で指定し、プログラム側でも以下のように __name__ の対策を施す
if __name__ == '__main__' or __name__ == 'sample__main__':
main()
|
__name__ は、(小文字化されたEXEファイル名) + '__main__' になる。
赤字の部分がEXE化したいプログラムによって変更する箇所になる。
また、cx_Freeze は様々なパッケージを一緒に入れ込んでくれる。使用していない余計なものまで入れてしまわないようするには、excludesリストで除外指定する。ただ、自分のスクリプトでは使用していなくても、実はimportしているパッケージの方で使用していましたということがあるので、excludes リストへは動作テストしながら少しづつ追加していった方が良いだろう。
|