概要
最初に作ったプログラムは、ポケモンの努力値をサポートする GUI アプリだ。
久々にプログラミングで遊ぼうと思って、これのリメイクを行ったぜ。
effort-leveling-support-eel
こんなやつ。
以前は Python の GUI ライブラリ tkinter を使って作ったけれど、今回は、 GUI のデザインを html と css で行えるライブラリ eel で GUI を実現したぜ。
このライブラリ、かなりよくね? tkinter では、 GUI を実現するために GUI ライブラリのウィジェット配置を覚えたけれど、面倒だったもんそんなん。「めっちゃ苦労して覚えたけれどこの知識ほかでは使えんなあ」っていうのはコストに感じる。その点 html, css, js をそのまま使えるなんてサイコーだ。ウェブサイト作りの知識をそのまま持ってこれるし、逆にこっちの知識をあっちでそのまま使えるからね。
今回のポイント
- 画面デザインはマジでそのまんま html, css で出来る。
- bootstrap を使えるからオサレな画面がカンタンに作れる。 cdn も使える!
- GUI でボタンを押したときの動作は JavaScript が担当する。
- JavaScript から Python の関数を呼び出すときは
await
で行う。 - Python 関数からの返り値は callback 関数だから、関数呼び出しにさらにカッコをつけて受け取る。
// NOTE: Python 関数の返り値は await で受け取ります。
// NOTE: Python 関数の返り値は callback 関数みたいです。
const calculationResultText = await eel.get_calculation_result_text(evInt, pokemonNumInt)();
全コード
てかリメイク元では全コード公開してたから今回も公開しよう。
./web/index.html
<!DOCTYPE html>
<html>
<head>
<title>Effort Leveling Support Eel</title>
<!-- NOTE: Bootstrap4.5公式サイトより。 -->
<link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/css/bootstrap.min.css">
<!-- Custom -->
<style>
.emphasize {
font-weight: bold;
}
</style>
</head>
<body>
<div class="m-3 card">
<div class="card-header">
<ul class="nav nav-tabs card-header-tabs">
<li class="nav-item">
<a id="nav-1-tab" href="#nav-1" class="nav-link active" data-toggle="tab" role="tab" aria-selected="true"
aria-controls="nav-1">
Effort Leveling Support
</a>
</li>
<li class="nav-item">
<a id="nav-2-tab" href="#nav-2" class="nav-link" data-toggle="tab" role="tab" aria-selected="false"
aria-controls="nav-2">
About app
</a>
</li>
</ul>
</div>
<div class="tab-content card-body">
<div id="nav-1" class="tab-pane fade show active" role="tabpanel" aria-labelledby="nav-1-tab">
<!-- 結果表示欄です。 -->
<div id="div_alert" class="alert alert-info text-center" role="alert">
The result's gonna be written here.
</div>
<!-- EV を書くフォームです。 -->
<div class="form-group">
<label>How much effort value do you want to pass to each pokemon?</label>
<div class="input-group mb-3">
<input id="input_ev" type="number" class="form-control" value="252">
<div class="input-group-append">
<span class="input-group-text">effort values</span>
</div>
</div>
</div>
<!-- 割り振るポケモン数を書くフォームです。 -->
<div class="form-group">
<label>How many pokemons do you want to pass effort value to?</label>
<div class="input-group mb-3">
<input id="input_pokemon_num" type="number" class="form-control" value="1">
<div class="input-group-append">
<span class="input-group-text">pokemon(s)</span>
</div>
</div>
</div>
<!-- 「計算する」ボタンです。 -->
<div class="text-center">
<button id="b" type="button" class="btn btn-outline-info">Calculate!</button>
</div>
</div>
<div id="nav-2" class="tab-pane fade" role="tabpanel" aria-labelledby="nav-2-tab">
<p class="card-text">
This app is what was remade Effort Leveling Support of tkinter,
that was my first Python app, using Eel library.
</p>
</div>
</div>
</div>
<!-- NOTE: Bootstrap4.5公式サイトより。 -->
<script src="https://code.jquery.com/jquery-3.5.1.slim.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/popper.js@1.16.0/dist/umd/popper.min.js"></script>
<script src="https://stackpath.bootstrapcdn.com/bootstrap/4.5.0/js/bootstrap.min.js"></script>
<!-- eel を JavaScript から利用します。 -->
<script src="/eel.js"></script>
<script>
$(function() {
// ボタン押下時に計算を行います。
$('#b').on('click', async function() {
await calculate($('#input_ev').val(), $('#input_pokemon_num').val());
});
});
// 入力値をもとに計算を行い、結果のテキストを表示します。
const calculate = async function(ev, pokemonNum) {
// int 化します。
const evInt = Number(ev);
const pokemonNumInt = Number(pokemonNum)
// NOTE: Python 関数の返り値は await で受け取ります。
// NOTE: Python 関数の返り値は callback 関数みたいです。
const calculationResultText = await eel.get_calculation_result_text(evInt, pokemonNumInt)();
$('#div_alert').html(calculationResultText);
}
</script>
</body>
</html>
./effort_leveling_support_eel.py
import eel
# JavaScript から関数を呼び出すデコレータです。
@eel.expose
def get_calculation_result_text(ev: int, pokemon_num: int) -> str:
"""計算を行い、結果のテキストを返します。"""
try:
x, y = calculate(ev, pokemon_num)
return ' '.join([
f'Give power-item each of them <span class="emphasize">{x} times</span> in flock battle.', # noqa: E501
f'Then kick <span class="emphasize">{y} pokemons\'</span> ass.'
])
except ValueError as e:
print(e)
return 'Invalid values were input.'
except Exception as e:
print(e)
return 'Sorry! Something went wrong!'
def calculate(ev: int, num: int) -> tuple:
"""
振りたい努力値と、同時に処理したいポケモン数を指定すると、
パワーアイテムを各ポケモンに順番に持たせてそれぞれ群れバトルを x 回させ
その後パワーアイテムを持たせずに単体バトルを y 回すればいいと教えてくれます。
Parameters
----------
ev : int
振りたい努力値数
num : int
同時に処理したいポケモン数
Returns
-------
x : int
上の説明参照
y : int
上の説明参照
"""
if ev <= 0 or num <= 0:
raise ValueError(f'Invalid value, ev:{ev}, num:{num}')
for x in range(100):
_ = (25 * x) + (x * 5 * (num - 1))
if _ > ev:
x -= 1
y = ev - ((25 * x) + (x * 5 * (num - 1)))
return (x, y)
# ウェブコンテンツの含まれるディレクトリを指定します。
eel.init('web')
# 最初に表示する html です。
eel.start('index.html', size=(650, 500))