ラベル Python の投稿を表示しています。 すべての投稿を表示
ラベル Python の投稿を表示しています。 すべての投稿を表示

2014年11月7日金曜日

Mac環境でQtを使ったPythonスクリプトをapp形式にまとめる

この記事の対象は、色々なツールを試した(あるいは検討した)けれど、Pythonで書いたソースのapp化に使うツールがしっくりきていない方向けです。

まずは結論から。

負担を軽減したいなら、とにかくPythonの2系でPyQt4を使い、PyInstaller2.1でstand-aloneなアプリを作りましょう。

ツールの導入もフォルダを解凍するだけと、とても簡単です。

たとえQt5を使って書いたソースが手元にあるとしても、Qt4向けに修正する価値はあります。特に基本的な機能のみを使ったアプリなら、Qt5じゃなくても動くはずです。

私はYosemite環境でVirtual Box + Mavericksを起動させ、以下のツールを試しました。基本的にMacPortsを使って、なるべく綺麗な環境で行っています。

環境
・Python3.4.1
・PyQt5.3
・sip4.16.4

ツール
・py2app
・cx_Freeze

とりあえず二大メジャーツールというべき、py2appとcx_Freezeを試しましたが、結局Qt4対応のソースに書き直して、PyInstallerで作りました。

このツールの良いところはデフォルトで複数の有名なライブラリに最適化されていることです。それにQtも含まれるため、非常に簡単にapp化できるのだと思います。

対応しているライブラリはこちら

上記二つのツールを使う際の問題点は、いわゆるsemi-stand-aloneなアプリになりやすいことです。つまり実行可能形式なんだけれど、環境に依存するという形式です。他の環境で試すまで分からなかったりと困った子です。外部ライブラリの取り込みが難しいんですよね。。。そして上手くいかない際の原因が分かりづらいです。。。

一方のPyInstallerですが、こちらはすでにアプリなため、ただ公式サイトからダウンロードしてきて解凍するだけです。

PyInstaller

以前の2.0ではビルドなどに様々なコマンドが必要だったようですが、現在は本当に数ステップです。

①本家サイトから取ってきたファイルを解凍。

②解凍して出来たPyinstallerというフォルダに、app化したいPythonスクリプトを置く。

③その階層で次のコマンドを叩く。

python pyinstaller.py --windowed your_source.py -i your_icon.icns

下線部分は適宜、ご自身の素材に読み替えてください。アイコンは使いたいものがある場合だけのオプションです。デフォルトのアイコンは微妙です。他のオプションは公式サイトからどうぞ。

--windowedコマンドは、UNIXの実行可能形式のみではなく、app形式を生成するためのオプション(だったはず)です。

コマンド自体はたったこれだけです。これでご自分のスクリプト名がついたフォルダが、Pyinstallerのフォルダ内に生成されます。いくつか階層を下れば、app形式のファイルがあるはずです。

私が使った環境です。MacPortsを使って導入しています。

Python27
py27-sip
PyQt4

PyInstaller(公式サイトからダウンロード)

PyQt5で書いたソースをPyQt4に書き直すのは気が引けますが、実際はQtWidgetsをQtGuiに置換(Qt5で独立した部分です)してしまえば、あとの修正部分は驚くほど少ないはずです。

私はencodeの部分で少し分かりにくい齟齬が発生しましたが、それも他のapp化ツールの情報収集に比べれば微々たるものでした。

ライブラリも出そろってきてだいぶ安定してきた3系ですが、それでもQtを使ったアプリの配布用作成には、この記事で挙げているバージョンを採用することで悩む時間がずいぶん減ると思いますよ。

Mac環境でシステムのPythonをアップグレード(インストール)する

Windowsユーザにはなんてことない、Pythonのアップデートですが、Macユーザには意外と手間だったりします。最近ならMacPortsやHomebrewあたりを使ってPATHを書き換えるのが主流のようですね。

そんな主流に反して、システムのPythonをアップデートする方法をメモしておきます。

まず、Macのシステムってどうなってるの、というとこから。Pythonに関わるPATHは以下の三つがあります。

usr/lib
プリインストール(最近だと2.5, 2.6, 2.7あたりが入ってますね)されている、Pythonのライブラリなんかがあります。

usr/bin
pythonコマンドなどのリンクがあります。ターミナルで「python」コマンドを叩くと、このリンクから各pythonの実行ファイルに飛ぶ仕組みです。


/Library/Frameworks/Python.framework/Versions/2.7(各バージョン。私はとある事情から3.3を入れました)

公式サイトのインストーラを使ってあるバージョンをインストールした時にできる階層。ここにpythonコマンドなどが追加されますが、3系なんかのバージョンでは微妙に名称が違うので注意です。

たとえば、Python3.3の場合は、pythonではなくてpython3になっています。

よって、システムのPythonを変更した場合、次の手順を取ることになります。

①インストーラで好きなバージョンをインストール

②usr/bin配下のリンクファイルをいったん削除。

③usr/bin配下に再度、新しく追加したPythonへのリンクを作成。

④新しいPython置き場にあるライブラリのフォルダを、PATHに追加。

具体的なコマンドはこんな感じです。

sudo rm /usr/bin/pydoc
sudo rm /usr/bin/python
sudo rm /usr/bin/pythonw
sudo rm /usr/bin/python-config

まずはリンクを削除しました。続いて、リンクを作成しなおします。今回はPython3.3を例に使いますが、各バージョンに合わせてリンク元の名称は変更します。

ln -s /Library/Frameworks/Python.framework/Versions/3.3/bin/pydoc3 /usr/bin/pydoc
ln -s /Library/Frameworks/Python.framework/Versions/3.3/bin/python3 /usr/bin/python
ln -s /Library/Frameworks/Python.framework/Versions/3.3/bin/pythonw3 /usr/bin/pythonw
ln -s /Library/Frameworks/Python.framework/Versions/3.3/bin/python3-config /usr/bin/python-config

以上で、リンクの再配置が完了しました。
さらに、ライブラリのPATHを追加します。vimで.bash_profileあたりに書き加えましょう。ちなみにこのコマンドのみだと、PyQtなどの各種ツールが配置されるsite-packageが含まれないため、必要な方はそちらも追加しましょう。

PATH="/Library/Frameworks/Python.framework/Versions/3.3/bin:$PATH"
export PATH

後はPATHを反映して、ターミナルでpythonと打って無事に新しいバージョンが起動すればOKです。

2014年11月3日月曜日

OSX10.10でPyenv使ってPytho3.4.1の環境をインストールしてPyQt5入れようとしたら色々と祭りでした

エラーの嵐をなんとかやり過ごしたメモです。本当に対症療法だけで乗り越えています。
というのも、今回の目的はWindows環境で作ったGUIアプリをMac用の実行可能形式にしたかっただけなので、アプリさえ動けばとりあえずOK。

というわけで、私の環境には現在いくつかの機能が入っていません。でも目的のために手段を選んではいられない! ということで、その辺りはこの際、目を瞑りました。なので、こんな感じでひとまず動いたよ! というだけの記事です。

とりあえず、次の環境では色々うまくいかないので、当てはまる場合はpyenvを使わないほうが良いのかもしれません。

OSX10.10
PyQt5.3.2
sip4.16.4
XCode 6.1

まずはこちらのサイト様を参考に、Homebrewを利用してpyenv環境を構築。

Mac OSX で開発環境を構築するための環境構築 (Homebrew, Git, SVN, Ruby, Perl, Python)


トラブル① pyenv global が効かない


こちらのサイト様を参考にいたしました。
どうやってもsystem配下のPythonを優先してしまうので、とりあえずPATHの先頭に加えることで対処。

macでpyenvの環境を整えたい

.bash_profileに以下の記述を追加しました。

export PYENV_ROOT="${HOME}/.pyenv"
if [ -d "${PYENV_ROOT}" ]; then
    export PATH=${PYENV_ROOT}/bin:$PATH
    eval "$(pyenv init -)"
fi

続きまして、こちらのサイト様を参考にsip, PyQt5, Qtを整えていきます。

SIPとQtとPyQt5の再設定

sipとQtに関してはとりあえず公式から取ってきました。バージョンは上の通りです。

ここからはPyqt5のインストールに関してです。
まずはpython configure.pyです。


トラブル② Qt.5.3.2既存エラー


次のエラーが表示されました。
Error: Failed to determine the detail of your Qt installation. Try again using
the --verbose flag to see more detail about the problem.

次のサイト様を参考にしました。

PyQt5をmacにinstall

これにバグがあるとか。


~/Qt/5.3/clang_64/lib/QtCore.framework/Headers/qflags.h

#!if defined(_LP64_) && !defined(Q_QDOC)
Q_DECL_CONSTEXPR inline QFlag(long ai) : i(int(ai)) {}

これの一文目を、次のように変更。先頭の!を削除です。

#if defined(_LP64_) && !defined(Q_QDOC)


トラブル③ pyenv環境によるエラー


次のエラーが表示されました。

UnboundLocalError: local variable 'pylib_dir' referenced before assignment

このエラーなんですが、systemのPython(2.7.6とかだと思います)を対象にmakeをすると表示されません。

dynamic_pylib = '--enable-framework' in config_args

このconfig_argsに'--enable-framework'という値が含まれないため、pylib_dirにpyenv環境のライブラリパスが含まれないことが原因のようです。というわけで、超対処療法です。
これが原因で、最後の妥協を強いられたのかもしれませんが。。。

730行目前後です。
こんな感じのソースがあります。

if dynamic_pylib:
                pyshlib = ducfg.get('LDLIBRARY', '')

このdynamic_pylibはbooleanなのですが、これを無理やり真にしてしまいます。
dynamic_pylib = True

とりあえず物は試しということで、これで作業を続けました。
これでようやくライセンスの確認までたどり着けました。


トラブル④  headerの参照ミス


これもHomebrew環境のみで起こる、単純な参照失敗みたいです。

sip/QtPrintSupport/qprinter.sip:28:10: fatal error: 'qprinter.h' file not found

qprinter.h自体はあるんですが、QtWebKitWidgetsから参照できていないようです。

次の質問が参考になりました。


これを

 'QtWebKitWidgets':      ModuleMetadata(qmake_QT=['webkitwidgets']),

こんな感じに。

 'QtWebKitWidgets':      ModuleMetadata(qmake_QT=['webkitwidgets' 'printsupport']),

というわけで、python configure.py からもう一度やり直すことになりました。

ちなみに。

Qt/5.3/clang_64/lib/QtPrintSupport.framework/Versions/5/Headers/qprinter.h

これを、cpコマンドを使って、次の階層にコピーするという強引な方法でも通りました。

Qt/5.3/clang_64/lib/QtWebKitWidgets.framework/Versions/5/Headers/qprinter.h


トラブル⑤ シンボリックエラー


最新のXCode (2014/10/31時点)はOSX10.9, 10.10向けなため、途中でシンボリックエラーが出る場合があります。私の場合は次の修正を行ったところ、エラーを抜けることができました。

~/Qt/5.3/clang_64/mkspecs/macx-clang/qmake.conf
QMAKE_MACOSX_DEPLOYMENT_TARGET = 10.6


~/Qt/5.3/clang_64 /mkspecs /qdevice.pri
!host_build:QMAKE_MAC_SDK = macosx10.8

これらの値を、とりあえず10.9にしてみました。

トラブル⑥ シンボリックエラー再び


ちなみにこんな感じのエラーです。

Undefined symbols for architecture x86_64

どうもQtというよりもXCodeとの齟齬が原因のようなのですが、エラーには変わりありません。これはdesignerのビルド時に発生していたらしく、この階層のpython.proを色々といじってみましたが、上手くいかず。

ですが、ここで当初の目的であるmac用実行可能形式ファイルを作成する、ということを最優先し、とりあえずdesignerとqmlsceneのビルドをしないことにしました。

PyQt5.proの次の部分を修正しました。

SUBDIRS = QtCore QtGui QtHelp QtMultimedia QtMultimediaWidgets QtNetwork QtOpenGL QtPrintSupport QtQml QtQuick QtSql QtSvg QtTest QtWebKit QtWebKitWidgets QtWidgets QtXmlPatterns QtDesigner QtDBus _QOpenGLFunctions_2_0 QtSensors QtSerialPort QtBluetooth QtMacExtras QtPositioning QtQuickWidgets QtWebSockets Enginio Qt pylupdate pyrcc designer qmlscene

ここから、最後のdesignerとqmlsceneを削除しました。

基本的にmacでアプリのデザインとかしないので、妥協しました。完全なmac環境が必要になった場合は、今の所pyenvを諦めてPythonの3系をsystemのバージョンとしてインストールしたほうが早そうです。。。

以上で、ひとまず別の環境で作ったPyQt5を含むソースを動かせるようにはなりました。あとはcx_Freezeあたりを利用して、appを作成すれば良いかと思います。

以上、対処療法による傷だらけの処置方法でした。