概要

最初に作ったプログラムは、ポケモンの努力値をサポートする GUI アプリだ。

gels

久々にプログラミングで遊ぼうと思って、これのリメイクを行ったぜ。

 

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))

以下の記事からリンクされています