Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 24 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save uchcode/fb2187111581032ec6b1d71e7e2d754f to your computer and use it in GitHub Desktop.
Save uchcode/fb2187111581032ec6b1d71e7e2d754f to your computer and use it in GitHub Desktop.
PythonでスタンドアロンのMac OS Xアプリケーションを作成する

PythonでスタンドアロンのMac OS Xアプリケーションを作成する

Pythonのプログラムを配布可能なアプリケーションを作成する方法について扱います。

Pythonプログラムの実行方式

コンピュータが理解できるのは突き詰めると、0と1だけです。そのため、プログラムを実行するには「プログラミング言語で書かれたテキストのプログラム」を0と1に変換する必要があります。そのやり方には2つあり、ひとつはコンパイラを使うもので、もうひとつはインタプリタを使うものです。

以下に両者の違いについて記載します。

*コンパイラとインタプリタの違い

まず、コンパイラを使う場合ですが、これは「テキストのプログラムをまず0、1のバイナリファイルに変換し、そのファイルを実行する」という動き方をします。まず最初に完全に変換し、そのあとでそれを実行するという形です。

一方、インタプリタ型はコンパイラのように先に変換してしまうのではなく「プログラムの実行時に、テキストで書かれたプログラムを都度0、1に変換しながら実行する」という動き方をします。つまり一気に変換してしまうのではなく、実行しながら今必要な箇所を0、1に変換しながら動いています。図にあるように、Pythonはインタプリタ型のプログラミング言語です。

プログラムの配布と実行方法

さて、インタプリタ型のプログラムを実行するには「都度0、1に変換する」必要があるのでした。これはつまり、プログラムの実行にはプログラムファイルのほかに「0、1に変換するインタプリタ」が必要ということです。以下の図を見てください。

*インタプリタが載っていないマシンではスクリプトを解釈できない

あるPythonスクリプト(プログラム)を配布する際、そのスクリプトを実行できるインタプリタがあるマシンだと、そのマシンが持つインタプリタでスクリプトを解釈して実行することができます。ただ、インタプリタが載っていないマシンではスクリプトは解釈することができません。この場合、プログラムではなく単なるテキストにすぎないので、当然ながら実行することはできません。

要するに、Pythonのプログラムをスクリプトとして配る際には「このPythonのプログラムを動かすにはPythonをインストールしておいてくださいね」と暗黙的に言われているというということです。

次にコンパイラ型言語で作られたプログラムの配布方法ですが、これはもっと簡単です。以下の図を見てください。

*コンパイラ型言語で作られたプログラムの配布方法

まず、配布されるプログラムは、配られる時点ですでにバイナリ(0、1形式のファイル)になっています。そのため、基本的に配布されたプログラムはそのバイナリファイルを動かせるOSであれば、インタプリタがなくても動かすことができます。つまり、OSさえ正しければ、どのマシンでも動かすことができるわけです(正確に言うとハードウェアにも依存しますが、組み込みなどでなければ普通は気にならないです)。

なお、JavaはJVM(Java Virtual Machine)があるため少し特殊ですが、少なくともインタプリタ型とは言われません。あえて言うのであれば「Java実行環境は必ずある」という前提をおいてコンパイラ型に分類される気がします。

Python Scriptのバイナリ化

Pythonのスクリプトをバイナリ化すると、Pythonインタプリタをインストールしていないマシンでもプログラムが実行可能になります。

バイナリ化を行うツールは、そのバイナリの対象OSごとにあり、Windowsの場合はpy2exeなどを使い、Macなどの場合はpy2appを使います。これ以外にもバイナリ化のツールにはいろいろありますので、自分の好きなものを使っていただければと思います。

*Pythonスクリプトのバイナリ化

ツールのインストールの準備

$ pip --version
pip 1.5.6 from /Library/Frameworks/Python.framework/Versions/2.7/lib/pyth    on2.7/site-packages (python 2.7)

すでに入っていれば上記のようにpipのバージョンが表示されるはずです。この場合は改めてインストールをする必要はありません。エラーが出れば、まだインストールされていないものと思われます(もしくはパスが通っていないか)。

pipのインストールは最近非常に簡単になっており、get-pip.pyというプログラムをダウンロードしてきて、それを実行するだけになったようです。ダウンロードリンクはいくつかあるようですが、以下のものが有名なようです。リンク切れなどをしていたら調べて、別の箇所からダウンロードしてください。

https://bootstrap.pypa.io/get-pip.py

これをダウンロードし、Pythonで実行します。

python get-pip.py --user

--userオプションを付加することでpipのインストール先が $HOME/Library/Python になり、特に管理権限がなくともインストールが容易にできます。インストールしたpipを使用できるようにパスを通します。

$ export PATH="$HOME/Library/Python/2.7/bin:$PATH"

永続的にパスを通すには .bash_profile に記述します。

$ echo 'export PATH="$HOME/Library/Python/2.7/bin:$PATH"' >> ~/.bash_profile

pipが使用可能になりましたので、続けて全てのPythonモジュールのアップデートを行います。pipと同様に--userオプションを付けてアップデートします。

$ pip freeze --local | grep -v '^\-e' | cut -d = -f 1 | xargs pip install -U --user

以上でpipのインストールと全モジュールのアップデートは完了です。

PythonプログラムのMacアプリケーション化

pipをインストールしたので、py2appをMacにインストールします。

その前に、今回バイナリ化するサンプルプログラムを作成しておきます。プログラムの細かい話は今回は割愛します。ファイル名はmyapp.pyとしています。

# -*- coding: utf-8 -*-
import objc
from Cocoa import *
from PyObjCTools import AppHelper

class UserInterface (NSObject):
  def init(self):
    self.window = NSWindow.alloc().initWithContentRect_styleMask_backing_defer_(((0,0),(250.0,100.0)), NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask, NSBackingStoreBuffered, False)
    self.window.setTitle_("Welcome to Python")
    self.window.setReleasedWhenClosed_(True)
    self.window.center()
    
    self.btnHello = NSButton.alloc().initWithFrame_(((10, 10), (110, 80)))
    self.btnHello.setTitle_("Hello!")
    self.btnHello.setBezelStyle_(NSRegularSquareBezelStyle)
    self.btnHello.setSound_(NSSound.alloc().initWithContentsOfFile_byReference_("/System/Library/Sounds/Tink.aiff", True))
    self.window.contentView().addSubview_(self.btnHello)
    
    self.btnGoodbye = NSButton.alloc().initWithFrame_(((130, 10), (110, 80)))
    self.btnGoodbye.setTitle_("Goodbye!")
    self.btnGoodbye.setBezelStyle_(NSRegularSquareBezelStyle)
    self.btnGoodbye.setSound_(NSSound.alloc().initWithContentsOfFile_byReference_("/System/Library/Sounds/Basso.aiff", True))
    self.window.contentView().addSubview_(self.btnGoodbye)
    
    return self

class AppDelegate (NSObject):
  def init(self):
    self.ui = UserInterface.alloc().init()
    self.ui.btnHello.setAction_("hello:")
    self.ui.btnGoodbye.setAction_("goodbye:")
    return self
  
  def hello_(self, sender):
    print("hello")
  
  def goodbye_(self, sender):
    print("goodbye")
    NSApp.terminate_(None)
  
  def applicationDidFinishLaunching_(self, notification):
    self.ui.window.makeKeyAndOrderFront_(None)
  
  def applicationShouldTerminateAfterLastWindowClosed_(self, sender):
    return True

if __name__ == "__main__":
  delegate = AppDelegate.alloc().init()
  application = NSApplication.sharedApplication()
  application.setActivationPolicy_(NSApplicationActivationPolicyRegular)
  application.setDelegate_(delegate)
  application.activateIgnoringOtherApps_(True) 
  AppHelper.runEventLoop()

さて、このプログラムを起動するにはターミナルを開いて、

$ python myapp.py

などというようにpythonコマンドでプログラムのファイルを呼び出すのでした。すると以下のようなアプリが起ちあがります。

*起動するアプリのイメージ

逆に言えばpythonコマンドがインストールされていないと起動することができません。このプログラムをバイナリ化してみます。

まずはpy2appのインストールです。これには先ほどのpipを利用します。Linuxのパッケージ管理ツールとよく似た使い方ですね。

$ pip install -U --user py2app
Requirement already up-to-date: py2app ...
...
Successfully installed modulegraph
Cleaning up...
$

インストールできたので、いよいよpy2appを使ったバイナリ化をします。バイナリ化の流れは、

  • バイナリ化するための設定ファイルsetup.pyのテンプレートを作成
  • setup.pyを編集
  • setup.pyを実行

となります。

順に見ていきましょう。まずテンプレートの作成ですが、これは以下のように行います。

$ py2applet --make-setup myapp.py
Wrote setup.py

オプションでsetupを作ることと、その対象となるプログラムを指定しています。これを実行するとmyappをバイナリ化するための設定ファイルであるsetup.pyが作成されているはずです。その中身を見てみます。

"""
This is a setup.py script generated by py2applet

Usage:
    python setup.py py2app
"""

from setuptools import setup

APP = ['myapp.py']
DATA_FILES = []
OPTIONS = {}

setup(
    app=APP,
    data_files=DATA_FILES,
    options={'py2app': OPTIONS},
    setup_requires=['py2app'],
)

これを必要に応じて編集します。編集の詳細はpy2appのページなどで確認してください。

今回は編集なしのデフォルトのままでバイナリ化したいと思います。バイナリ化するにはこのsetup.pyを実行します。

$ python setup.py py2app

するとdistというディレクトリが作成され、その中にmyappというアプリケーションが作成されました。試しにダブルクリックしてみると先ほど作ったアプリケーションが起動するはずです。

*ダブルクリックでカウンターアプリが起動

このmyappはバイナリファイルですので、これをほかのMacに配ってもそこで起動することができます。

バイナリファイルの中身

最後にpy2appで作成したアプリケーションmyappの中身を確認してみます。

*myappの中身

さまざまなファイルからアプリケーションが構成されていることがわかりましたが、そのなかに「もとのスクリプトファイル」とPythonそのものが入っていることがわかります。

バイナリ化したといっても、スクリプトとそれを動かすPythonをセットにして配布しているだけです。Cのバイナリファイルとは根本的に仕組みが違うので注意してください。

開発向けのバイナリ化

先のバイナリ化では完全なパッケージングで配布可能ですが、コードを編集する度にバイナリ化を再度実行しなければなりません。それでは実際の開発で不便ですので、開発向けのバイナリ化をしてコーディングしていきます。開発向けのバイナリ化は--aliasオプションを付けて実行します。

$ python setup.py py2app --alias

先と同様にdistの中のmyappをダブルクリックで実行できます。何かしらmyapp.pyを編集後してアプリケーションを実行すると、編集コードが反映されて起動します。

アプリケーションの起動はコマンドラインからも可能で、以下のように実行します。

$ dist/myapp.app/Contents/MacOS/myapp

これは通常のターミナルでバイナリファイルの実行と同じですので、例えばPythonのprintによる標準出力が表示可能です。これによりprintデバッグやpdbによるデバッグも可能です。

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment