rcmdnk's blog

20240229_pythonnuikta_200_200

Pythonでファイルをdrag and dropで渡すアプリを作ろうと思った時、 TkinterとNuiktaを使って作ろうとしたら M1 MacなどのARM Mac用を作るのがちょっと面倒だった話。

PythonでのGUIアプリ開発

tkinter はGUIツールに対するPythonの標準インターフェースです。

PythonでGUIアプリを作るには flet などのフレームワークを使うこともできますが、 今回はdrag and dropでファイルを渡すアプリを作ろうと思って、 fletでは現状サポートされておらず、 tkinterであればそれようのライブラリが既に存在するのでtkinterを使うことにしました。

tkinterはPythonのインストール状況によってはインストールされておらず、

1
2
3
4
>>> import tinker
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ModuleNotFoundError: No module named 'tinker'

のようにエラーが出ることがありますが、その場合は別途インストールする必要があります。

MacやLinuxでHomebrewでPythonをインストールしている場合は、

1
$ brew install [email protected]

などインストールしているのと同じバージョンのtkinterをインストールします。

Linuxでaptを使ってPythonをインストールしている場合は、

1
sudo apt install python3-tk

WindowsならWelcome to Python.orgからインストーラをダウンロードしてインストールするとデフォルトでtkinterもインストールされます。

tkinterdnd2

tkinterdnd2 はtkinterの拡張ライブラリで、 drag and dropをサポートしています。

1
$ pip install tkinterdnd2

でインストールして

drag_and_drop.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
#!/usr/bin/evn python3
import tkinter as tk
from pathlib import Path

from tkinterdnd2 import DND_FILES, TkinterDnD


def drop(event):
    file_path = event.data
    text = Path(file_path).name
    file_label.config(text=text)
    new_width = min(max(300, len(text) * 20), 800)
    app.geometry(f"{new_width}x300")
    app.update()


app = TkinterDnD.Tk()
app.geometry("300x300")
app.title("drag and drop")

file_label = tk.Label(app, text="ファイルをドロップしてください")
file_label.pack()

app .drop_target_register(DND_FILES)
app.dnd_bind('<<Drop>>', drop)

app.mainloop()

みたいなスクリプトを実行するとウィンドウが出てきてそこにファイルを ドロップするとファイル名が表示されるようなアプリができます。

tkinterdnd2-universal

tkinterdnd2はx86_64のMacやWindowsなどで動作しますが、 ARM Macでは動作しません。

そこで、ARM Macでも動作するようにしたのが tkinterdnd2-universal

tkinterdnd2 の代わりに tkinterdnd2-universal をpip installすると 上の同じスクリプトでARM Macでも動作するようになります。

ただ、PyPIにはまだ残っているのでpipでのインストールは可能で ソースコードも直接ダウンロードしたり出来ますが、 GitHubで作者がアカウント自体を削除したのかレポジトリが残ってません。

https://github.com/blacklein/tkinterdnd2-universal

最終リリースは1.7.3で2023/3/21。

tkinterdnd2の方もPyPIにリリースされてるものはそもそもforkされたもので 0.3.0が2021/6/6にリリースされたのが最後です。

他にも似たようなものはいくつかあって、より良いものもあるかもしれませんが、 とりあえず今回は見た感じ一番メジャーなこれを使いました。

Nuitka

Nuitka はWindows用のexeやMac用のappを作るツールです。

同様に実行ファイルを作るツールとしては PyInstaller などがありますが、ほとんどのものはPythonを含めて実行ファイルを作って一時ディレクトリにPythonコードを展開して実行するので 実行ファイルのサイズが大きくなるのと実行時間が遅い、 簡単にソースコードが見えてしまうという問題があります。

NuitkaはPythonのコードをC言語に変換してコンパイルするので 実行ファイルのサイズが比較的小さく、実行時間も速く、 ソースコードも見えないようになります。

今回も最初PyInstallerを使ってうまくいきましたが、 Nuiktaで作れればそちらの方が良いかと思い試してみました。

Nuitkaはpipでインストールできます。

1
$ pip install nuitka

あとはWindowsであれば

1
$ python -m nuitka --mingw64 --enable-plugin=tk-inter --follow-imports --onefile --show-progress --assume-yes-for-downloads --disable-console drag_and_drop.py

のようにしてdrag_and_drop.exeファイルを作ることが出来ます。

Macなら

1
$ python -m nuitka --enable-plugin=tk-inter --follow-imports --standalone --show-progress --assume-yes-for-downloads --disable-console --macos-create-app-bundle  drag_and_drop.py

drag_and_drop..appファイルを作ることが出来ます。

ARM Macでの実行

が、ARM Macで上記コマンドを実行すると

1
FileNotFoundError: [Errno 2] No such file or directory: '...lib/python3.11/site-packages/tkinterdnd2/tkdnd/osx64'

といったエラーが出ます。

上にも書いた通り、tkinterdnd2の方をインストールするとここではエラーは出ずアプリは作れますが実行時に

1
RuntimeError: Unable to load tkdnd library

といったarm用のライブラリではないとエラーが出ます。

tkinterdnd2-universalを使うとスクリプトは実行できますが、 コンパイル時に上のFileNotFoundErrorが出ます。

実際にsite-packagesの中を見てみると tkinterdnd2の方にはosx64win64linux64がありますが、

tkinterdnd2/tkinterdnd2/tkdnd at master · Eliav2/tkinterdnd2

tkinterdnd2-universalの方にはosx-arm64, osx-osx64, linux-arm64, linux-x64, win-arm64, win-x64があります(PyPIにあるコード参照)。

これに関して、NuitkaではライブラリがPythonコード以外のライブラリなどを含む場合、 あらかじめ調べておいてコンパイルするようになっているようです。

Compile seems to not include tkdnd package needed for tkinterdnd2 · Issue #1562 · Nuitka/Nuitka

tkinterdnd2も以前は上記のライブラリが入らず実行ファイルを作っても実行できませんでしたが、 上のIssueで対応されたようです。

が、これはtkinterdnd2の方で対応されたもので、 同じ名前でいれるtkinterdnd2-universalの方でライブラリのディレクトリ名が違うとうまくインストールできません。

tkinterdnd2/tkinterdnd2/TkinterDnD.py の中でも、こちらのtkinterdnd2の方はosx64win64linux64というディレクトリ名から選ぶようになっていますが、 tkinterdnd2-universalの方はアーキテクチャも考慮してosx-arm64, osx-osx64, linux-arm64, linux-x64, win-arm64, win-x64というディレクトリ名から選ぶようになっています。

実際にNuitkaが行うのは必要なライブらいフォルダを実行ファイルの中にコピーすることなので、 ARM Macで行う場合、tkinterdnd2-universalをインストール後、 site-packagesの中にある

  • tkinterdnd2/TkinterDnD.pyのコードの中のosx-osx64osx64に変更
  • tkinterdnd2/tkdndの中のosx-osx64ディレクトリをosx64に変更

してやればOK。

1
2
3
4
5
$ site_packages=$(python -c "import site;print(site.getsitepackages()[0])")
$ cd $site_packages/tkinterdnd2
$ sed -i '' -e 's/osx-osx64/osx64/g' TkinterDnD.py
$ cd $site_packages/tkinterdnd2/tkdnd
$ mv osx-osx64 osx64

これで

1
$ python -m nuitka --enable-plugin=tk-inter --follow-imports --standalone --show-progress --assume-yes-for-downloads --disable-console --macos-create-app-bundle  drag_and_drop.py

でARM Mac用のアプリが作れます。

Intel Macで作る場合は tkinterdnd2の方をインストールしてNuitkaでコンパイルするだけでOKです。

Sponsored Links
Sponsored Links

« Poetryで管理しているパッケージテストをGitHub ActionsでPoetry及びDockerで行う HHKB Studio購入 »

}