PythonアプリのGUIをHTMLで作る方法【Python】

eyecatch プログラミング

Pythonでアプリケーションを作った時、普段使いするなら使いやすいGUIがほしくないですか?
本記事では、Pythonで作ったアプリのGUIをHTMLで作る方法をご紹介します。
PythonアプリのGUIをより柔軟に作りたいという方はぜひ参考にしてください!

目次

概要

GUIを作る方法

今回、PythonでGUIを作るにあたり、どんな方法があるかを軽く調べてみました。
ほかにも色々な方法があるかと思いますが、その一例をご紹介します。
それぞれのドキュメントのリンクを貼ってあるので良ければ参考にしてください。

Tkinter
  • Python標準ライブラリなのでライブラリ等の追加インストールが不要
  • 軽量で用意に実装できる
  • デザインが古く、自由度が低い
  • 公式ドキュメント
Django
  • PythonをWebアプリで使える
  • 機能面での自由度が高い
  • Youtube、Instagram等で使用されている
  • 公式ドキュメント
Electron
  • Web技術でデスクトップアプリを作れる
  • 自由度が高い
  • VisualStudio Code、Slack等に使われている
  • 公式ドキュメント
Eel
  • Web技術でデスクトップアプリが作れる
  • 軽量で小規模アプリに適している
  • Git Hubドキュメント

まだまだあるかと思いますが、導入や学習コストが比較的低そうなものはこのあたりかと思います。
私が少し触った感じですと、小規模の簡単なアプリであれば、TkinterかEelが良いかと思います。
Tkinterは標準ライブラリなので追加インストールが不要です。
また、Eelもライブラリのインストールのみで始められます。
どちらもPythonを動かせる環境ができいればすぐに始められます。
Pythonの導入についてはこちらの記事を参考にしていただければと思います。

Eelについて

Eelのイメージはこんな感じになります。

ユーザーはHTMLをインターフェイスにして情報を入力して、javascriptとPythonで情報を処理するようなイメージです。
似たものにPyscriptという、HTMLに直接Pythonのコードを記述するものもありますが、ブラウザの仕様によりローカルフォルダへのアクセスが制限されるという欠点があります。
より自由度の高いアプリを作るにはEelが適していると思います。
多少Webに関する知識も必要になりますが、小規模のアプリに必要な程度でしたらそこまでハードルは高くないかと思います。

GUIの実装方法

基本動作

Eelを使ったGUIの実装方法をご紹介します。
まずはeelをインストールしましょう。

pip install eel

あとはGoogle ChromeやMicrosoft Edgeなど、HTMLを実行できるブラウザがあれば問題ありません。

次に、アプリを作るフォルダを整えます。
フォルダの構造は画像のように作ってください。

main.pyを実行することで、main.htmlが起動します。
main.pyには次のように記述します。

import eel

eel.init("web")
eel.start("main.html", size = (900, 900)) #sizeで起動するブラウザのウィンドウサイズを決められる。

作成したフォルダ「web」に以下のように記述したmain.htmlを保存してください。

<!DOCTYPE html>
<html lang="ja">
  <head>
  </head>
  <body>
    <h1>
      Hello! World
    </h1>
    <p>Hello! World</p>
  </body>
</html>

次にmain.pyを実行するとブラウザが起動して次の画像のようなページが表示されます。

基本的な使い方は以上です。
必要に応じてcssで装飾や、javascriptを記述してHTMLから情報を受け取ってpythonで処理したり、逆にpythonから情報をjavascriptに渡してHTMLで表示させることができます。

Webサイト側(javascript側)からPythonの関数を呼び出す方法

HTMLからのアクションに応じてPythonの関数を実行させることもできます。
例として、Webページ上のボタンを押すと、Pythonの関数が実行される例を紹介します。
表示されるページはこんな感じです。
pushボタンを押すとPythonのエディタのコンソール上に「Hello! world」と表示されます。


man.pyは以下のように記述してください。

#main.py
import eel

#webページのボタンを押すとpy_function()実行される
##########追記##########
@eel.expose
def py_function():
  print("Hello! world")
########################
  
eel.init("web")
eel.start("main.html", size = (900, 900))

main.pyには、実行したい関数を追記します。
実行したい関数の前には@eel.exposeを書いてください。
ここで実行される関数がHTMLからの入力を引数を受け取る場合、関数はeel.start()よりも下に記述してください。

main.htmlは次のように記述します。

!DOCTYPE html>
<html lang="ja">
  <head>
<!---------------------- 追記 1------------------------>
    <script type="text/javascript" src="/eel.js"></script>
    <script type="text/javascript">
      function js_function() {
        console.log("test")
        eel.py_function();
      }
    </script>
<!----------------------------------------------------->
  </head>
  <body>
    <h1>
      Hello! World
    </h1>
    <p>Hello! World</p>
<!---------------------- 追記2 ------------------------>
    <input id = "button" type="button" value="push" onclick="js_function()">
<!----------------------------------------------------->
  </body>
</html>

追記1ではjavascriptを追記しています。
main.pyの関数を起動させるために<script type=”text/javascript” src=”/eel.js”></script>と、その他必要な関数を追記してください。
また、追記2ではpushボタンの表示と、ボタンが押されたときに実行されるjavascriptの関数を設定しています。
ここで実行される関数も追記1の部分に記述します。

Pythonからjavascriptの関数を呼び出す方法

Pythonからjavascriptの関数を実行することもできます。
以下にPythonから値をjavascriptへ送り、HTMLの値をPythonから受け取った値に書き換える例を示します。

import eel

eel.init("web2")
###############追記###############
eel.js_function("Goodbye!world")
##################################
eel.start("main.html", size = (500, 200))

eel.start()の前にeel.js_function()を記述します。
eel.js_function()の引数が、main.htmlに記述されたjs_function()関数の引数として渡されます。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <!---------------------- 追記 ---------------------------->
    <script type="text/javascript" src="/eel.js"></script>
    <script type="text/javascript">
      eel.expose(js_function)
      function js_function(value) {
        document.getElementById('target').textContent = value;
      }
    </script>
    <!-------------------------------------------------------->
  </head>
  <body>
    <h1>
      Hello! World
    </h1>
    <p id = "target">Hello! World</p> <!-- ここがGoodbye!worldに書き換わる -->
    <input id = "button" type="button" value="push">
  </body>
</html>

Pythonで呼び出す関数js_function()を記述し、その上に、Pythonから呼び出すためにeel.expose(js_function)を記述しておきます。
main.pyを実行すると、画像のようにGoodbye!worldに書き変わります。

実用例

最後に、コピー元とコピー先のパス、ファイル名を入力すると、ファイルがコピーされるアプリの例をご紹介します。
GUIは画像のようになります。

コピー元フォルダの欄とコピー先フォルダの欄にそれぞれパスを入力します。
FILE_NAMEの欄にはファイル名を拡張子なしで記述します。
今回のアプリは拡張子がtxtファルであることを前提としています。
サンプルコードは以下のとおりです。

#main.py
import eel
from py_function import function_list as fl
  
eel.init("web2")
eel.start("main.html", size = (900, 600))

fl.receive_input(input_path, output_path, model_list) 

下のPythonコードはjavascriptから呼び出してファイルをコピーするコードです。
今回ご紹介するアプリケーションはファイル形式がtxtファイルであることを前提としたコードとなっています。
また、今回は関数を別のフォルダに分けています。
関数を分けたファイルは基本動作の項目でご説明しました、webフォルダやmain.pyと同じ階層に関数用のフォルダに入れてください。
今回のサンプルコードでは、py_functionというフォルダを作り、function_list.pyというファイルに以下の関数を記述しています。

#function_list
import eel, os, shutil
import glob as gb

@eel.expose
def receive_input(input_path, output_path, model_list):
  input_list = []
  for i in range(len(model_list)):
            name = model_list[i]
            input_list.append(name)
            
  file_list = gb.glob(input_path + '//*')
  file = []
  
  for i in range(len(file_list)):
    file.append(os.path.basename(file_list[i]))
    file[i] = file[i].split(".")[0]
    if file[i] in input_list:
      shutil.copy2(input_path + "//" + file[i] + ".txt", output_path)

今回のHTMLファイルでは、主に見出しや、テキスト入力欄、ボタンといった要素を実装しています。

<!DOCTYPE html>
<html lang="ja">
  <head>
    <meta charset="utf-8"/>
    <link rel="preconnect" href="https://fonts.googleapis.com">
    <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
    <link href="https://fonts.googleapis.com/css2?family=Balsamiq+Sans:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
    <link href="https://fonts.googleapis.com/css2?family=Balsamiq+Sans:ital,wght@0,400;0,700;1,400;1,700&family=Hachi+Maru+Pop&display=swap" rel="stylesheet">
    <link rel="stylesheet" href="./css/style.css" type="text/css">
    <script type="text/javascript" src="/eel.js"></script>
    <script src="./JS/java.js" type = "text/javascript"></script>
  </head>
  <body>
    <div class = "contents">
      <h1 id = 'title'>
        ファイルコピー
      </h1>
      <div class = "path_label">
        コピー元フォルダ 
        <input type="text" id="input" class = "path" name="name" size="50" />
      </div>
      <div class = "path_label">
        コピー先フォルダ 
        <input type="text" id="output" class = "path" name="name" size="50" />
      </div>
      <div>
        <table>
          <tr>
            <td><input type="text" class="model_num" name="name" size="20" placeholder="例: file_name" /></td>
          </tr>
          <tr>
            <td><input type="text" class="model_num" name="name" size="20" placeholder="例: file_name" /></td>
          </tr>
        </table>
        <input id = "get_button" type="button" value="get files" onclick="send_path()"> 
      </div>   
  </div>
  </body>
</html>

今回の例では、javascriptでそれぞれ入力された値をPythonファイルへ受け渡し、フォルダのコピーを行うPython関数を実行します。

function send_path() {
  const input_path = document.getElementById("input").value;//「コピー元フォルダ」入力された値を変数に格納
  const output_path = document.getElementById("output").value;//「コピー先フォルダ」入力された値を変数に格納
  const model = document.getElementsByClassName("model_num");//FILE_NAMEに入力された値を変数に格納
  const model_list = Array.from(model).map(element => element.value);//FILE_NAMEに入力された値をまとめてリスト型に変換
  eel.receive_input(input_path, output_path, model_list);//Pythonの関数、receive_input()を実行
}

CSSは装飾なので必須ではありません。
ただ、装飾できることがHTMLの良いところだと思いますので、せっかくなのでいろいろ装飾してみてはいかがでしょうか?

@charset "utf-8";
*{
  margin: 0;
  padding: 0;
}
h1{
  font-size: 50pt;
  font-family: "Hachi Maru Pop", cursive;
  font-weight: 900;
}

input:focus {
  outline: solid 1px rgba(105, 105 , 105, 1);
}

.model_num {
  border: solid 1.5px rgba(105, 105 , 105, 0.5);
  border-radius: 0.3em;
  font-size: 20pt;
  text-transform: uppercase; /*入力されたアルファベットを大文字で表示*/
}

.path {
  border: solid 1.5px rgba(105, 105 , 105, 0.5);
  border-radius: 0.3em;
  font-size: 20pt;
}

.path_label{
  font-size: 20pt;
  font-family: "Hachi Maru Pop", cursive;
  font-weight: 900;
}

td{
  padding: 5px 0px;
  font-size: 10pt;
  font-family: "Balsamiq Sans", sans-serif;
  margin:10px;
  height: 30px;
}
html{
  overflow: scroll;
  scrollbar-width: none;
}
body{
  overflow: scroll;
}

html::-webkit-scrollbar{
  display: none;
}

html, body {
  height: 100%;
  background-image:linear-gradient(rgba(255, 255, 255, 0.5), rgba(0, 0, 0, 0.5)), url("../img/wall_paper.svg");
  background-size: cover;
}

.contents {
  height: 100%;
  margin: 20px;
  padding: 20px;
}

#get_button{
  padding: 5px 20px;
  font-size: 15pt;
  border-radius: 0.3em;
  margin: 10px;
  font-family: "Balsamiq Sans", sans-serif;
  font-weight: 200; 
}

おわりに

いかがだったでしょうか?
HTMLを使ったGUIは比較的簡単に導入できますので、より凝ったGUIを作りたい方はぜひ参考にしてください。
分かりづらい点、より詳しく説明してほしい点などありましたらコメントに書いてくださると幸いです!
最後まで読んでいただきありがとうございました!

コメントを残す

メールアドレスが公開されることはありません。 が付いている欄は必須項目です

CAPTCHA


コメント

タイトルとURLをコピーしました