はじめに
- PythonでGUIのアプリを作りたい。
- TkinterやKiviなどは試したけど、凝ったレイアウトを作ろうとして挫折した。
- GUIをHTML/CSS/JavaScriptのWEB技術で作りたい。でも、Electronを使うのは腰が重い。
そんなときピッタリなPythonのGUIライブラリがEelです。
Eelは、とてもシンプルなElectronライクなGUIライブラリで、NumpyやPandasなどデータ処理などに秀でているPythonと、D3.jsなど描画に優れているJavaScriptをつなぎ合わせたアプリを、いとも簡単に構築することができます。
以下、公式GitHubのReadmeに沿って、説明します。
- 実行環境
- Windows 10
- Python3.6
- Eel 0.9.7
インストール
pip install eel
しましょう。
Hello World
ルートにmain.py
を作成し、WEB関係のファイルはweb
ディレクトリの下に置きます。
main.py
web/
main.html
css/
js/
わずか3行のスクリプトを書き、
import eel
eel.init("web")
eel.start("main.html")
適当なHTMLを書いてpython main.py
と実行すると
<html>
<head>
<meta charset="UTF-8">
<title>Eel</title>
</head>
<body>
Hello World!!
</body>
</html>
PythonからJavaScript関数を呼ぶ
eel.js
を読み込んだうえで、Pythonから呼びたい関数をeel.expose(js_function)
します。
<html>
<head>
<meta charset="UTF-8">
<title>Eel</title>
</head>
<body>
<div id="target"></div>
<script type="text/javascript" src="/eel.js"></script>
<script type="text/javascript">
eel.expose(js_function)
function js_function(values) {
var s = ""
for (var i = 0; i < values.length; i++) { s += values[i] + "<br>" }
document.getElementById("target").innerHTML = s
}
</script>
</body>
</html>
eel.js_function()
でexpose
されたJavaScript関数を呼ぶことができます。
import eel
import numpy as np
eel.init("web")
eel.js_function(np.random.rand(4).tolist()) # JSON serializableでないとダメ
eel.start("main.html")
JavaScriptの関数の戻り値をPythonで取得
JavaScriptの関数の戻り値をPythonで取得するには、Callbackと同期待ちの2種類の方法があります。
Callbackを設定
1つは、JavaScript関数が終了された後に実行されるCallback関数を設定する方法です。
eel.expose(js_function);
function js_function(args) {
return "hogehoge";
}
eel.js_function(args)(lambda val: print(val + " from JavaScript"))
同期待ち
もう1つは、空のカッコをつけて、JavaScript関数の終了を待つ方法です。eel.start()
の前に呼ぶとフリーズしてしまいます。
val = eel.js_function(args)()
JavaScriptからPython関数を呼ぶ
JavaScriptから呼びたいPython関数を、@eel.expose
でデコレートします。
import eel
@eel.expose
def python_function(val):
print(val + " from JavaScript")
eel.init("web")
eel.start("main.html")
eel.js
を読み込んだうえで、eel.python_function(args)
でPython関数を呼ぶことができます。
JavaScriptの配列はPythonのlist
に、連想配列はdict
へと変換されます。
<html>
<head>
<meta charset="UTF-8">
<title>Eel</title>
</head>
<body>
<script type="text/javascript" src="/eel.js"></script>
<script type="text/javascript">
eel.python_function("aa")
</script>
</body>
</html>
Pythonの関数の戻り値をJavaScriptで取得
async
await
を使います。
@eel.expose
def python_function2():
return "Hello"
async function run() {
let val = await eel.python_function2()();
console.log(val + " from Python")
}
run();
起動時のオプション設定
web_app_options = {
"mode": "chrome-app", # chromeのアプリケーションモードで起動
# "chrome" とすると、通常のchromeで起動
"port": 8080,
"chromeFlags": [
"--start-fullscreen", # フルスクリーンで起動 他のChromeが起動していると機能しない?
# "--window-size=800,600",
# "--window-position=0,0",
]
}
eel.start("main.html", options=web_app_options)
マルチスレッド
eel.sleep()
や、eel.spawn()
を使うことで、簡単にマルチスレッド化できます。下の例は、
- WEBサーバをホストするスレッド
my_other_thread
- メインスレッド
の3つが走るスクリプトです。
import eel
eel.init("web")
def my_other_thread():
while True:
print("I'm a thread")
eel.sleep(1.0) # time.sleep()でなくて、eel.sleep()を使うこと
eel.spawn(my_other_thread)
eel.start("main.html", block=False) # block=Falseとしないと、ここで止まってしまう
while True:
print("I'm a main loop")
eel.sleep(1.0)
動作サンプル ( Smoothie によるリアルタイムなグラフ)
Pythonでグラフといえばmatplotlibですが、ここでは、JavaScriptのライブラリSmoothie Chartsを使ってリアルタイムなグラフを描画してみます。
import random
import time
import eel
eel.init("web")
web_app_options = {"chromeFlags": ["--window-size=420,200"]}
eel.start("main.html", options=web_app_options, block=False)
while True:
eel.push_data([
{"time": time.time() * 1000, "value": random.random()}, # JSのgetTime()と同じにするためミリ秒に直す
])
eel.sleep(0.2)
<html>
<head>
<meta charset="UTF-8">
<title>Chart</title>
</head>
<body>
<div id="parent" style="height: 100%;">
<canvas id="mycanvas"></canvas>
</div>
<script type="text/javascript" src="/eel.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/smoothie/1.34.0/smoothie.min.js"></script>
<script type="text/javascript">
// canvasの高さと幅を画面に合わせる
var parent = document.getElementById("parent")
var canvas = document.getElementById("mycanvas")
canvas.width = parent.clientWidth
canvas.height = parent.clientHeight
var smoothie = new SmoothieChart();
smoothie.streamTo(canvas);
var series = new TimeSeries();
smoothie.addTimeSeries(series);
eel.expose(push_data)
function push_data(values) {
for (var i = 0; i < values.length; i++) {
series.append(values[i]["time"], values[i]["value"]);
}
}
</script>
</body>
</html>
バイナリ化
-
pip install pyinstaller
をして、PyInstallerをインストールする -
main.py
のディレクトリでpython -m eel main.py web
と実行すると、必要な引数を加えてPyInstallerが実行されます - すると、
dist
ディレクトリ以下に実行ファイルが出来上がります
- PyInstallerのオプションを引数に加えることもできます
-
--onefile
ひとつのファイルにまとめる -
--noconsole
コンソールを表示させない -
--exclude [モジュール名]
不要に取り込まれてしまったモジュールを取り除く
-