PythonのGUIをHTMLで構築するライブラリ、PyWebViewを使って、既存のHTMLやCSSを読み込み。さらにPyInstallerを使ってEXEとして使えるようにパッケージしてみます。
用意するもの
- Windows 10 1903 Ver
- Python 3.7.5
- PyInstaller 3.7
- PyWebView 3.0.2
- pipenv 2018.10.13
- Bootstrap(今回はBootstrap-honoka 4.1.3を利用)
なお、パッケージングの都合上、pipenvはプロジェクトフォルダに配置されていた方が都合が良いため、予め環境変数にPIPENV_VENV_IN_PROJECT=true
を設定しておきます。
作業
必要なライブラリをインストールする
とりあえず作業を開始します。適当なフォルダを作り、pipenvでPyInstallerとPyWebViewをインストールします。
mkdir pywebtest
cd pywebtest
pipenv install --python C:\Python36\Python.exe
pipenv install pywebview
pipenv install --dev pyinstaller
次に、最低限必要なフォルダを作成します
mkdir html
mkdir src
bootstrapを導入する
次に、Bootstrapを導入します。今回はnpmを使ってライブラリを導入します
npm init
... // パッケージの設定は適当で良いです
npm install --save bootstrap-honoka
GUI用のHTML一式を作成する
htmlフォルダには以下の三つのファイルを作成します。
- html/main.html
- html/main.css
- html/main.js
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test</title>
</head>
<body>
<h1>It works!</h1>
<button class="btn btn-primary">test button</button>
</body>
</html>
body{
padding: 12px;
}
h1::after{
content: " with CSS";
}
document.querySelector("button").addEventListener("click", function(){
alert("It works!");
});
ここまでで大体次のような感じになっていると思います。
pywebtest
│ package-lock.json
│ package.json
│ Pipfile
│ Pipfile.lock
│
├─.venv
│
├─html
│ main.css
│ main.html
│ main.js
│
├─node_modules
│ └─bootstrap-honoka
└─src
ここにソースファイル
GUI呼び出しのためのコードを作成する
それではGUI作成のためのコードを書いてみます。注意点としては以下の二つ
- PyWebViewは、ウィンドウを表示すると、そのスレッドを占有してしまうこと(その後に処理を行いたい場合は、スレッドを作る必要があること
- PyWebViewにはHTMLやCSSのファイルを読み込むメソッドはないので、load_html, load_css, evaluate_jsメソッドにopenしたファイルを読み込ませる必要があること
特に気をつけるのは上です。以下のような感じで読み込んでみます。
import webview
def load_css(path):
with open(path, mode="r", encoding="utf-8") as f:
webview.load_css(f.read())
def load_js(path):
with open(path, mode="r", encoding="utf-8") as f:
webview.evaluate_js(f.read())
def load_thread():
load_html("html/main.html")
load_css("html/main.css")
load_css("node_modules/bootstrap-honoka/dist/css/bootstrap.min.css")
load_js("html/main.js")
with open(path, mode="r", encoding="utf-8") as f:
html = f.read()
window = webview.create_window(title="Test Application", html=html, width=640, height=320)
webview.start(load_thread, window)
ここまでできたら、pipenv run
で実行します。
pipenv run ./src/main.py
すると、とりあえずアプリが起動し、無事にBootstrapが読み込まれた状態で、HTMLが表示されます。
なお、一件load_html
をload_css
のあとに呼び出せば処理が高速化されそうなものですが、これをやるとGUIの表示段階で無限ループに陥ってしまうようなので上手くいきません。必ずHTMLを読み込んでからCSSを読み込みようにします。
PyInstallerで実行ファイルを作る
それではPyInstallerを使ってみようと思います。以前と同様、ビルド用のPowerShellスクリプトを書いてビルドを行います。
# ディレクトリの用意
if (Test-Path dist) {
Remove-Item dist -Recurse | Out-Null
}
New-Item dist -ItemType Directory | Out-Null
# windllを捜索
Write-Host "> Searching WinDLL"
$windll = "c:\windows\WinSxS\x86_microsoft-windows-m..namespace-downlevel_*"
$windllpath = "."
if(Test-Path $windll){
$item = (Get-ChildItem $windll | Sort-Object LastWriteTime)[0]
$windllpath = '"c:\windows\WinSxS\{0}"' -f $item.Name
}
# pyinstaller
Write-Host "> Creation EXE"
pyinstaller `
--name TestAPP `
--windowed `
--path $windllpath `
--path C:/Windows/System32/downlevel `
--specpath ./dist/ `
--distpath ./dist/dist `
--workpath ./dist/build `
--add-data "../html;html" `
--add-binary "../.venv/Lib/site-packages/webview/lib;webview/lib" `
src\main.py
Write-Host "> Complete"
ここで気をつけるのは
- add-binaryオプションでPyWebViewのDLLをパッケージに含めておくこと(パスも合わせておくこと)
- add-dataオプションでhtmlファイルを配置しておくこと
単独のアプリとして実行できました。
同じようなHTMLでGUIを実現するライブラリは、eelというライブラリもあるのですが、そちらは既存のChromeをそのまま使う(拡張機能がインストールされていたら、それらも使える状態)ようになっていることや、そもそもPyInstallerでパッケージするとうまく動かないことから、採用できませんでした。
PyWebViewは EdgeHTMLを使うため 残念ながらTrident(IE11)を使います(いちおうEdgeHTMLへの移行予定自体はあるようですが)。PyWebViewの引数JSAPIで指定したPythonオブジェクトの戻り値などはIE11が対応していないPromiseオブジェクトですので、かなり従来のHTMLコーディングとは異なるアプローチが必要になるでしょう。
ただ、そこまで無理しなければアプリのGUIでは一応良いんじゃないでしょうか。ファイルやフォルダを開くダイアログを表示するなどのメソッドも完備していますし。
2019/10/22追記:CEFPythonを使う
PyWebViewでCEFというものが使えるようになりました。これを使えば、ブラウザエンジンをChromiumにすることができます(いちおうMSHTML(Edge)にも対応しているようですが、どうせ今後はこちらもChromiumになってしまうので…)。
こちらを使う場合は、まずcefpython3
とpythonnet
という二つのモジュールを追加します。
pipenv install cefpython3
pipenv install pythonnet
PyInstallerでEXEを作るとき、cefpython3
ディレクトリに入っているDLLが必要になるため、pyinstallerの呼び出しコードに、次のコマンドを追加します。
--add-binary "../.venv/Lib/site-packages/cefpython3/;." `
で、最後にPyWebViewの公式にも書かれているように、webview.start()
にgui="cef"
という引数を付け加えます。
# …
window = webview.create_window(title="Test Application", width=640, height=320)
webview.start(load_thread, window, gui="cef")
また、webview.start()
メソッドの引数にdebug="true"
をつけると開発者モードのウィンドウまで出せるようになります。これで開発が相当やりやすくなる。
CEFPythonの開発者ウィンドウで、JavaScriptのトレースを行う
CEFPythonを使った場合、HTMLはdata-uriの形式でChromiumに読み込まれています。そのため、この状態でコンソールからJavaScriptを追っていくのは難しい。
そんなときは、JavaScriptの冒頭で、何か適当にconsole.log
しておきます。
すると、右側に行番号が表示されるので、そこからJavaScriptのソースを見ることができます。