【Windows10とAndroid】Pythonとbottleで自作するJupyter風ナイスなポンコツWebアプリ
PythonとWebフレームワークbottleを使って、JupyterNotebook風Webアプリを自作した。その記録。
前々回に作ったものを改造。
前回
前々回
なんと、ブラウザ上でPythonコードが実行できます!
htmlを書き換えれば独自のJupyterが作れる。
実行風景
起動画面

Pythonコードを入力中

実行ボタンを押すと…

カレンダー表示コマンドをPythonから…

カレンダーが表示された

グラフをファイル出力するコードを…

実行しても画面上に表示なし

ちゃんと画像ファイル保存されていた

エラーが出た場合はこんな感じ


実行環境
Androidスマホ
termux
Python3.7
ブラウザGoogleChrome
ブラウザを起動させる部分でtermuxがインストールされた前提のAndroidとWindows用に作っています。前々回のちょっと改造しただけなのでWindows10でも動くと思うが未確認。
目次
ライブラリ
- bottle
- os
- webbrowser
- subprocess
bottleはpip install等でインストールが必要です。
作るもの
- プロジェクトフォルダ(作るファイルの入れ物)
- index.html(入力・出力)
- main.py(実行用Pythonファイル)
(1)を適当な場所に作ったら、中に(2)(3)を入れておきます。
bottleがno module の場合は、bottleの公式サイトから bottle.py をダウンロードするかコードをその名前で保存し、(1)の中に一緒に入れておけば大丈夫です。
実行方法と動作
実行方法
プロジェクトフォルダをカレントディレクトリにしてターミナルから
python main.py
動作の流れ
- 実行
- 入力保存用フォルダ自動作成
- ブラウザ自動起動しindex.html表示
- 入力したら実行ボタンを押す
- フォーム下に標準出力を表示される
- 入力内容は一旦ファイルに上書きで書き出される
- subprocess.PopenでPythonにそのファイルを実行させる
- stdout,stderrをbottleのテンプレートに渡して
- 実行結果をブラウザ表示
機能
いうなれば、Pythonしか実行できない単一のcodeセルです。
(1)プロジェクトフォルダ作成
適当にフォルダを作ります。
(2)index.html作成
フォルダ(1)に保存
<!DOCTYPE html>
<html lang="ja">
<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>Python実行アプリ</title>
</head>
<body>
<form action="/hello" method="post" accept-charset="UTF-8">
<p>【python code】<br><p><input type="reset" value="Reset"></p>
<textarea name="example1" id="example" cols="40" rows="8" wrap=off style="overflow:auto;"></textarea>
<p><input type="submit" value="実行"></p>
<pre><p>{{text0}}</p></pre>
<pre><p>{{text1}}</p></pre>
</form>
</body>
</html>
(3)main.py作成
これも同じフォルダに保存
from bottle import route,template,run,request
import os
import webbrowser
import subprocess
# 投稿保存用フォルダ作成
def make_log_folder():
os.makedirs("log_folder", exist_ok=True)
# ブラウザ起動
def open_browser():
#url
url = "http://localhost:8080/hello"
# osの種類を取得
which_os_name = os.name
# osブラウザ起動
if which_os_name == "nt": # windows
webbrowser.open(url)
elif which_os_name == "posix": # androidでtermuxをインストール済
cmd = "termux-open-url " + url
subprocess.run(cmd.split())
# bottle API
@route('/hello',method=["GET", "POST"])
def index():
# textを取得
text = request.forms.example1
with open("log_folder/log.py","w") as f:
f.writelines(text)
# コマンド実行
python_file = "log_folder/log.py"
cmd = "python " + python_file
proc = subprocess.Popen(cmd.split(), stdout=subprocess.PIPE, stderr=subprocess.PIPE, universal_newlines=True)
# 正常とエラーの標準出力取り出し
result = proc.communicate()
res0 = result[0] # ok
res1 = result[1] # error
if isinstance(res0,bytes):
res0 = res0.decode("utf-8")
return template('index.html',text0=res0,text1=res1)
# 実行
make_log_folder()
open_browser()
run(host='localhost',port=8080,reloader=False,debug=True)
備考
index.html
前回、前々回と基本的に変えていない。textareaの入力フォームとinputボタン。今回はそこにリセットボタンを付けた。
<input type="reset" value="Reset"></p>を書いておけば、ボタンを押したときに勝手にtextareaの入力をクリアしてくれる。
{{text0}}は正常に実行された時の標準出力が、{{text1}}はエラー時の出力が渡されてレンダリング、置き換わります。
main.py
カレントディレクトリにフォルダ(./log_folder)を作成し、フォーム入力内容を上書き保存で書き込んだファイル(log.py)をそこへ保存する。
subprocess.Popen()でpython ファイル名を実行させるが、そのファイルパスが参照される。
result = proc.communicate()で正常とエラーの標準出力がタプルで取り出せる。
あとはreturnでテンプレートに渡してやる。
ブラクラ注意!
ブラウザ起動とreloader=Trueの組み合わせ
コードをいじっている最中、ブラウザを大量に起動させてコンピュータをクラッシュさせる通称ブラクラが発生した。
main.pyにブラウザを起動させるコードをつけているが、もしreloader をTrueにしておくと何等かの理由でbottleのサーバがストップしたりするたびにrun()関数が何度も呼ばれ、その都度ブラウザが新しいタブを開く。ブラウザクラッシャー発生w
クラッシュしない仕組みを組み込んでおかないといけませんですね。プロはどうしてる( -_・)?だろう?
reloader=Falseで!
以上です。