絖綛 N@i.jp  昨日:00018063
 今日:00019234
 総計:00059285
keywords
管理者専用
  Post   Add link   Control Panel 































新しいトピック
最新:10/01 12:07


新しいコメント
最新:07/28 16:47






管理人へMAIL

プライバシーポリシー

cx_Freeze で Macアプリを作成する(3)

QuickIR を cx_Freeze でアプリ化してみよう


6. QuickIR用の setupファイル

 cx_Freeze で QuickIR を Macアプリ化する setupファイル("cxfreeze_mac_setup.py")は以下のようになる。

# -*- coding: utf-8 -*-
"""
This is a cx_Freeze setup.py script

Usage:
    python cxFreeze_setup.py build
"""
import sys
import os
from cx_Freeze import setup, Executable


# Dependencies are automatically detected, but it might need fine tuning.
APPLICATION = 'QuickIR'
VERSION = '1.0.9'

if sys.platform == 'win32':
    python_dir = r'C:¥Python35'    # Python installed directory
    include_files = [
        python_dir + r'¥DLLs¥tcl86t.dll',
        python_dir + r'¥DLLs¥tk86t.dll',
        'images'
    ]
elif sys.platform == 'darwin':
    include_files = [
        ('/Library/Frameworks/Tcl.framework/Resources/Scripts', 'tcl8.5'),
        ('/Library/Frameworks/Tk.framework/Resources/Scripts', 'tk8.5'),
        'images'
    ]
else:
    include_files = []

includes = ['irkit', 'resolve_irkit']
pakages = ['os', 'sys', 'tkinter', 'pybonjour']
excludes = ['logging', 'distutils',
            'multiprocessing', 'pydoc_data',
            'test', 'unittest',
            'xml', 'xmlrpc']
build_exe_options = {'includes': includes,
                     'include_files': include_files,
                     'packages': pakages,
                     'excludes': excludes,
                     'optimize': 2}

bdist_mac_options = {'bundle_name': APPLICATION,
                     'iconfile': 'app.icns',
                     'custom_info_plist': 'Info.plist',
                     'include_frameworks': []}

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(script='QuickIR.py',
                   base=base,
                   targetName='QuickIR.exe',
                   icon='app.ico')
    ]
else:
    exe = [
        Executable(script='QuickIR.py',
                   base=base,
                   targetName=APPLICATION)
    ]


setup(name=APPLICATION,
      version=VERSION,
      description='QuickIR IRKit controller',
      options={
          'build_exe': build_exe_options,
          'bdist_mac': bdist_mac_options
      },
      executables=exe)

 Windowsと共用できるよう、以前 Windows用に作成した"cxfreeze_setup.py"に Mac用の設定を書き加え、Windows と Macで異なる部分はsys.platformによって切り分けるようにしている。cx_Freeze はマルチプラットフォームに対応しているのが特徴とは言うものの、setupファイルはプラットフォームによる違いが多いことが分る。

 また、"Info.plist"は以下のようにした。

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
    <key>CFBundleDevelopmentRegion</key>
    <string>English</string>
    <key>CFBundleDisplayName</key>
    <string>QuickIR</string>
    <key>CFBundleExecutable</key>
    <string>QuickIR</string>
    <key>CFBundleName</key>
    <string>QuickIR</string>
    <key>CFBundleGetInfoString</key>
    <string>QuickIR IRKit controller 1.0.9</string>
    <key>CFBundleIconFile</key>
    <string>icon.icns</string>
    <key>CFBundleIdentifier</key>
    <string>jp.ddo.y-naito.QuickIR</string>
    <key>CFBundleShortVersionString</key>
    <string>1.0.9</string>
    <key>CFBundleVersion</key>
    <string>1.0.9</string>
    <key>NSHumanReadableCopyright</key>
    <string>Copyright (C) 2015-2016, N@i.jp, All Rights Reserved</string>
</dict>
</plist>

7. "QuickIR.py"プログラムの修正

 setupファイルはできたが、まだ"QuickIR.py"スクリプト本体の修正が残っている。先ずはmain()関数を呼び出すif文だ。アプリ名はQuickIRなので、

if __name__ in ('__main__', 'quickir__main__', 'QuickIR__main__'):
    main()

と修正する。これで cx_Freeze でアプリ化してもmain()関数が呼び出されるようになった。

 実は修正はまだある。これで作成したアプリをFinderから実行すると、一旦起動した後にすぐに終了してしまうのだ。調査にかなり苦労したのだが、この原因はinclude_filesでアプリ内に取り込んだ"images"フォルダ配下の画像ファイルを読み込めない事だった。問題のプログラムは以下の部分だ。

    def createToolbar(self, toolbar):
        """make toolbar"""
        self.img_folder_add = PhotoImage(file='images/folder_add.gif')
        self.img_folder_ren = PhotoImage(file='images/folder_rename.gif')
        self.img_folder_del = PhotoImage(file='images/folder_delete.gif')
        self.img_file_add   = PhotoImage(file='images/file_add.gif')
        self.img_file_post  = PhotoImage(file='images/file_post.gif')
        self.img_file_ren   = PhotoImage(file='images/file_rename.gif')
        self.img_file_del   = PhotoImage(file='images/file_delete.gif')

"QuickIR.py"スクリプトと同じ場所に"images"フォルダがあり、その配下に画像ファイルが置かれている事を想定しており、例外の検出もしていない。でも、ちょっと待って。include_files"images"フォルダごとアプリ内に取り込んでいるし、実際アプリ内に取り込まれていることも確認できる。

$ ls ./build/QuickIR.app/Contents/MacOS/images
IRKit.gif       file_delete.gif     file_rename.gif     folder_delete.gif
file_add.gif        file_post.gif       folder_add.gif      folder_rename.gif

Windows でEXEファイルにした時には何の問題もなかった。何故これで画像ファイルの読み込みに失敗するのだろう。
 色々調べたところ、cx_Freeze で作成した Macのアプリの場合、Finderから実行するとカレントディレクトリは「/(ルート)」になるようだ。(py2app ではカレントディレクトリは"QuickIR.app/Contents/Resources"になっていた。)そのためアプリ内にある画像ファイルに辿り着けず読み込みが失敗していたのだ。ネットで検索したらドンピシャの事例が見つかった。これによると、cx_Freeze によって固められた(frozen)場合はos.path.dirname(sys.executable)を前に付けてフルパス化する必要があるということだ。固められていない場合は、os.path.dirname(__file__)でスクリプトの場所のディレクトリ名を前に付けて、同じくフルパス化する。これで cx_Freeze のアプリ実行の場合とスクリプトとして実行された場合のどちらでもファイルに辿り着けるようになる。
 つまり cx_Freeze で固めるなら、include_filesでアプリ内に取り込むファイルを相対パス指定でアクセスしてはいけないと言うことになる。

 と言うことで、事例にあった関数を追加して画像ファイルを読み込む処理を修正する。(関数名は少し変えている。)

def find_image_file(path):
    if getattr(sys, 'frozen', False):
        # The application is frozen
        datadir = os.path.dirname(sys.executable)
    else:
        # The application is not frozen
        # Change this bit to match where you store your data files:
        datadir = os.path.dirname(__file__)
    return os.path.join(datadir, path)

〜略〜

    def createToolbar(self, toolbar):
        """make toolbar"""
        self.img_folder_add = PhotoImage(file=find_image_file('images/folder_add.gif'))
        self.img_folder_ren = PhotoImage(file=find_image_file('images/folder_rename.gif'))
        self.img_folder_del = PhotoImage(file=find_image_file('images/folder_delete.gif'))
        self.img_file_add   = PhotoImage(file=find_image_file('images/file_add.gif'))
        self.img_file_post  = PhotoImage(file=find_image_file('images/file_post.gif'))
        self.img_file_ren   = PhotoImage(file=find_image_file('images/file_rename.gif'))
        self.img_file_del   = PhotoImage(file=find_image_file('images/file_delete.gif'))

上記以外の画像ファイルを読み込む処理も同様に修正した結果、ようやく正常に起動して動作するようになった。

 このように cx_Freeze は py2app, py2exe に比べて色々と癖があり、使い勝手がいまいち良くない。py2app, py2exe が使えるのであれば、それらを使用した方が楽にアプリ化、EXEファイル化できるだろう。



< 過去の記事 [ 12月の プログラミング リスト ] 新しい記事 >

2016 calendar
12月
123
45678910
11121314151617
18192021222324
25262728293031


掲示板
最新:08/15 17:19


GsBlog was developed by GUSTAV, Copyright(C) 2003, Web Application Factory All Rights Reserved.