Pythonでおしゃれなアプリを作りたい？　ならpywebviewをオススメします！

# 1. Pythonでアプリを作りたい
　先日、「Pythonで画像処理アプリを作りたい」という相談を受けました。画像処理と言っても複雑な物ではなく、「フォルダ内の画像を一括でトリミング・縮小する」くらいのアプリです。
　さて、先方の希望が「画像処理を学びたい」だけならOpenCVやnumpyについて教える流れになると思うのですが、ゴールは画像処理ではなくアプリ開発です。それも、仕様を詳しく理解していないユーザーでも簡単に使いこなせるようなアプリが目標地点のようです。
　ふーむ、これは一筋縄ではいきませんね。

　ここでPythonに詳しい人ならPython・GUIアプリと聞くとtkinterやQtを思い浮かべると思います。ですが、これらを使って「おしゃれで使いやすい」アプリを作るのはなかなかに骨が折れる作業です。

　そこで私は**pywebview**をオススメすることにしました。これはhtmlを使って作ったアプリをPythonで動かすことのできるライブラリです。もっとわかりやすく言うと、ウェブページのデザインをする感覚でPythonアプリを作る事が出来ます。

　という訳で本記事ではpywebviewの使い方をご紹介しようと思います。最後までお付き合いいただければ幸いです。

※なお、本記事は「変数」や「関数」「ライブラリ」と言った語句を知っている前提で書いています。


　また、本記事で紹介するコードは以下のレポジトリにあげています。参考にして下さい。

https://github.com/TBjustice/Tutorial_GUI-with-Python/tree/main

# 2. 基礎知識
## そもそもhtmlって何ぞや？
　HTMLを一言で言うと、ウェブページの**構成要素について記す言語**です。例えばヘッダー(画面上部のタイトル的な物)とメインコンテンツがあるウェブサイトは次のように書きます。

```html
<html>
    <head>
        <meta charset="UTF-8">
        <title>ページタイトル</title>
    </head>
    <body>
        <header>
            ヘッダー
        </header>
        <main>
            メインコンテンツ
        </main>
    </body>
</html>
```

　HTMLでは一部例外を除き構成要素を`<○○>～中身～</○○>`のように書きます。

　例えば最初と最後の行にある`<html>～</html>`はコード全体を囲うまとまりです。
　2行目から5行目の`<head>～</head>`はページの初期設定を含むブロックです。その中に「これはUTF-8です」や「ページタイトルはこれです」みたいなことを書いています。
　6から13行目の`<body>～</body>`はページに表示される内容を含むブロックです。その中に「header」と「main」という二つのブロックが入れられています。

## CSSとJavaScriptについて
　HTMLはそれ単独ではただの要素の羅列です。それでも問題ないと言えば問題ないですが、どうせだったらデザインもこだわりたい。そんな時に使うのが**CSS**です。先ほどのコードの`<head>`内に以下のように書くことでヘッダーの背景を濃い青に、文字を白色にできます。
```html
        <style>
            header{
                background:darkblue;
                color:white;
            }
        </style>
```

　また、**JavaScript**と言うものを使う事で、ウェブページに**動き**を付けることができます。これに関しては、「アプリを作るぞ！」の項目で詳しく話しましょう。

## で、結局HTMLって何が便利なの？
　HTMLを使ってアプリを作るメリットは二つあります。

- 簡単
プログラミングのようにあれこれ頭を悩ませる必要がないので簡単です。例えば「メインコンテンツの中にOKボタンを配置したいな」と思ったら、`<main>～</main>`の中に`<button>OK</button>`と書くだけで出来てしまいます。
もちろん、デザインにこだわり始めたら少し複雑になりますが、それもネット上に情報が散らばっているので、そのコピペで大抵どうにかなります。

- 情報収集がしやすい
という訳で、二つ目のメリットは情報収集のしやすさです。多くのウェブデザイナーがサンプルコードを公開しており、コピペするだけで大抵のことは出来てしまいます。

　これらがHTMLでアプリ開発を行うメリットと筆者は考えています。

:::note info
確認
・HTMLとは要素の列挙です。
・HTMLでは`<○○>～中身～</○○>`という形で要素を定義します。
・CSSを使う事で、多少デザインを修正できます。
・より凄いデザインを作る際は、ネットで調べてコピペします。
:::

# 3. アプリを作るぞ！
　では、さっそくアプリを作っていきましょう！！

## pywebviewことはじめ
### ライブラリのインストール
　まずは**pywebview**をインストールします。Anacondaをお使いの場合は、Anaconda Prompt上で`pip install pywebview`とするとインストールできます。

### ファイル構成
　次にファイル構成。プロジェクトフォルダー（今回はMyFirstAppとしました）の中にrun.pyとwebというフォルダを、そしてwebの中にindex.htmlを作ります。
```
MyFirstApp
├─run.py
└─web
   └─index.html
```

　ファイルの中身は次のようにします。コピペしちゃってOKです。
　まずは、run.py。
```python
import webview
window = webview.create_window("My First App", url="web\\index.html")
webview.start(http_server=True, debug=True)
```
　次にindex.html。コピペしちゃってOKです！
```html
<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>My First App</title>
</head>
<body>
    <h1>My First App</h1>
</body>
</html>
```
　VS Codeをお使いの場合は、「!」と打つだけでhtmlのテンプレートを書いてくれます。

### ざっくりコード解説
　Pythonの方からコードを解説していきます。と言っても、3行しかないですが。これらはコピペでいいので、内容は覚えなくても大丈夫です。
　一行目の`import webview`はwebviewという名前のライブラリを読み込んでいます。今回のメインテーマですね。
　二行目の`window = webview.create_window("My First App", url="web\\index.html")`ではウィンドウを作成していますね。「タイトルは“My First App”で埋め込むhtmlコードは"web\\index.html"に保存しています」という意味です。
　三行目についてです。`webview.start(http_server=True, debug=True)`でアプリを起動しています。`http_server=True`についてはおまじないみたいなものです。`debug=True`は開発者用ツール（後述）を表示するという意味です。開発中は`debug=True`にしておいて、完成時に`debug=False`にすればよいでしょう。

　次にHTMLについてです。こちらは長いですが、VS Codeのテンプレートほぼそのままなので、`<body>`の中身だけ説明します。
　`<h1>My First App</h1>`
　このh1というのは「大見出し」という意味です。要するに大きな文字でMy First Appと表示して下さいという意味です。ちなみに`<h2>`だと見出し、`<h3>`だと小見出し……と言うように、数字を大きくするずつ文字サイズは小さくなります。

### 実行結果
　以下の様に表示されたら成功です。
　お疲れさまでした、これであなたは「PythonでGUIアプリを開発したことがある」という実績・経歴を手にしました！
　なお、右側に移っている"My First App"と書かれているアプリがアプリの本体です。左上の黒いのは**コマンドプロンプト**で、Pythonのprint関数の内容が出力される場所となっています。
　一方左下のウィンドウは**開発者用ツール**と呼ばれるもので、 ウェブサイトにおけるエラーメッセージが表示される場所です。詳しくは後ほど、今は「エラーなんかが表示される場所」と思っておいてください。
![スクリーンショット 2023-06-26 192956.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3039281/d4a13830-7fa9-aecc-842f-0920c1c26482.png)

:::note info
確認
・まずはpywebviewをインストールする必要があります。
・取り合えず上記コードをコピペすれば、アプリが起動します。
・また、アプリを実行すると**開発者用ツール**と言う物も表示されます。
・HTMLで`<h1>大見出し</h1>`と書くと、大見出しを作れます。
:::

## JavaScriptとデバッグウィンドウ
### 取りあえずコードをみる
　先ほどのプロジェクトをコピーして、プロジェクト名をJSとします。
　次に、index.htmlを編集します。**bodyの中**を次のようにします。
```html
    <h1>JavaScript</h1>
    <main>
        <button onclick="onClickOk()">OK</button>
    </main>

    <script>
        let count = 0;
        async function onClickOk(){
            count+=1;
            console.log(count);
        }
    </script>
```

　run.pyは特に変更しなくて大丈夫です。

### コード解説
　まずは`<h1>JavaScript</h1>`。これは見出しでしたね。大きな文字でJavaScriptと表示されるはずです。

　次は`<main>`と`</main>`。これはメインコンテンツの意味でしたね。メインコンテンツの中に`<button onclick="onClickOk()">OK</button>`が入っています。
　`<button>OK</button>`と書くだけでOKボタンを作る事ができるのですが、それだと「ボタンを押しても何も起こらない」という残念なことになってしまいます。そこで、`onclick="onClickOk()"`と書きます。こう書く事で、ボタンが押された時にJavaScriptの関数「onClickOk()」が呼び出されます。という訳で次にonClickOkの中身を見てみましょう。
```html
    <script>
        let count = 0;
        async function onClickOk(){
            count+=1;
            console.log(count);
        }
    </script>
```
 `<script></script>`で囲う事で、その中身に**JavaScript**（≒**ウェブページの動作**）を書くことができます。順番に見ていきましょう。
　`let count = 0;`でcountという変数を作成し、0で初期化しています。
　`async function onClickOk(){……}`にてonClickOkという関数を作成します。ボタンが押されたら、この中身が動く訳ですね。
　関数の中身は`count+=1;`と`console.log(count);`です。前者はcount変数に1加えるという意味。後者はそれを開発者用ツールに表示するという意味です。Pythonの`print()`がJavaScriptでは`console.log()`と思っておけばいいでしょう。

### 実行結果
　以下の様に表示されたら成功です。おめでとうございます、これで「JavaScriptを使った事がある」という実績を獲得しました！
　さて、実行結果を見ると、ボタンが押されるごとに左下の開発者用ツールに数字が表示されます。これが開発者用ツールの真骨頂です。
![スクリーンショット 2023-06-26 193128.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3039281/7bb07917-8178-f95d-8198-5f9c24884fdf.png)


:::note info
確認
・`<button>OK</button>`と書くだけでOKボタンを作る事ができます。
・onclick="JSの関数()"と書くと、JSの関数を呼び出せます。
・`<script></script>`の中に**JavaScript**を書きます。
・JavaScriptでは`async function 関数名(){……}`と書くことで、関数を作れます。
・`console.log()`で開発者用ツールに情報を表示できます。
:::

## PythonとJavaScriptの協働
　ここまでで「ボタンを押した時の動作」をJavaScriptで定義する事ができましたが、本命は別ですよね。そうです、最終目標は画像処理をPythonで行う事です。という事で次はJavaScriptとPythonの通信に関してです！
### 取りあえずコードをみる
　先ほどのプロジェクトをコピーして、プロジェクト名をJS2Pythonとします、
　次に、index.htmlを編集します。**bodyの中**を次のようにします。
```html
    <h1>JS to Python</h1>
    <main>
        <button onclick="onClickOk()">OK</button>
    </main>

    <script>
        async function onClickOk(){
            let res = await pywebview.api.add(10, 15);
            console.log(res);
        }
    </script>
```
　次はrun.py
```python
import webview

class Api:
    def add(self, a, b):
        print("Python")
        return a+b

api=Api()
window = webview.create_window("JS to Python", url="web\\index.html", js_api=api)
webview.start(http_server=True, debug=True)
```
　少し複雑になってきましたね。

### ざっくりコード解説
　まずはhtmlの中（というかJavaScript）に関して。onClickOkの中身を見てみます。
```js
let res = await pywebview.api.add(10, 15);
console.log(res);
```
　`pywebview.api.add()`という関数を呼び出していますが、これが**JavaScriptからPythonの関数を呼ぶ**方法となっています。なお、**await**とは「**結果が返ってくるまで動作が止まる**」という意味です。
　`pywebview.api.add()`の返り値を`console.log(res);`で表示しています。

　次はPythonの方を見てみましょう。
```python
class Api:
    def add(self, a, b):
        print("Python")
        return a+b
api=Api()
```
　classが出てきました。ご存じない方は「え、ナニコレ？」と思うかもしれません。classを一言で言うと「**関数の集まり**」です。JavaScript側に提示する関数の一覧をここに示しているのです。今回はaddという関数だけですので、上のようなコードになっています。
　なお、もしもadd以外にsubという関数もJavaScript側から呼び出したければ次のように書きます。
```python
class Api:
    def add(self, a, b):
        print("Python")
        return a+b
    def sub(self, a, b):
        print("Python")
        return a-b
api=Api()
```
　こんな感じです。こう見ると、「ああ、確かにclassは関数の集まりなんだなあ」というイメージを掴めるのではないでしょうか？

### 実行結果
　以下の様に表示されたら成功です。
　ボタンを押すと、コマンドプロンプト（黒い画面）に`print("Python")`による出力が表示され、左下の開発者用ツールには10＋15の計算結果である25が表示されています。
![スクリーンショット 2023-06-26 193316.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3039281/37ec76f2-2065-32b6-119c-386d7b127704.png)


:::note info
確認
・Pythonのクラスは関数の集まりです。
・クラス内に詰め込んだ関数をJavaScript側から呼び出すことができます。
・その際は`await pywebview.api.関数名(引数);`と言うように書きます。
・ここで、`await`とは、処理が終わるまで待つという意味です。
:::

## Inputと複雑なデータの送信
　さて、画像編集ソフトを作るならば「画像の縮小率」「クロップの範囲」「反転の有無」などをユーザーに入力してもらい、それをPython側に送信しなくてはいけません。
　そこで今回は、ユーザーに数値などを入力してもらうフォームの作り方と、そのデータをPythonに送信するサンプルアプリを作ってみます。

### 取りあえずコードをみる
　index.htmlをのbodyの中を次のようにします。
```html
    <h1>Inputs</h1>
    <main>
        <div>
            Value
            <input type="number" id="number" min="0" max="100" step="25" value="50">
        </div>
        <div>
            Check
            <input type="checkbox" id="check">
        </div>
        <div>
            Select
            <select id="select">
                <option>Apple</option>
                <option>Banana</option>
                <option>Peach</option>
            </select>
        </div>
        <div>
            Slider
            <input type="range" id="slider" min="0" max="100" step="5" value="30">
        </div>
        <button onclick="onClickOk()">OK</button>
    </main>

    <script>
        async function onClickOk(){
            let data = {};
            data["number"] = parseInt(document.getElementById("number").value);
            data["check"] = document.getElementById("check").checked;
            data["select"] = document.getElementById("select").selectedIndex;
            data["slider"] = parseInt(document.getElementById("slider").value);
            await pywebview.api.pyprint(data);
        }
    </script>
```
　次はrun.py
```python
import webview

class Api:
    def pyprint(self, data):
        print(data)

api=Api()
window = webview.create_window("Inputs", url="web\\index.html", js_api=api)
webview.start(http_server=True, debug=True)
```

### 先に実行結果を見る
　実行結果は以下のような感じです。
![スクリーンショット 2023-06-27 134016.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3039281/5ae23f36-a178-d2c2-6144-e80c44d31084.png)

　アプリ本体を見てみると「Inputs」と書かれた見出しの下にValue、Check、Select、Sliderとあって、一番下にOKボタンが設置されています。
　そして、OKボタンをクリックすることで、その時の値がPythonに送信されています。（コマンドプロンプトにデータが表示されているのがわかると思います）

### コード解説
　まずはPythonの方から見てみましょう。変わった事と言えば、Apiクラスの中身だけです。
```python
class Api:
    def pyprint(self, data):
        print(data)
```
　データを受け取って、それを表示するだけです。シンプルですね。

　一方、HTMLとJavaScriptは少々複雑な事になっています。

　まずはdivについてお話します。`<div>～</div>`はその間の物がひとかたまりであることを示すものです。division（分割）が語源とされています。例えば3～6行目に以下のような記述があります。
```html
        <div>
            Value
            <input type="number" id="number" min="0" max="100" step="25" value="50">
        </div>
```
　これで「`Value`という文字と`<input type="number" …略…>`はひとかたまりですよ」という意味になります。

　次はinputについて。inputは珍しくも`<〇〇>～</〇〇>`と言う形にならない要素です。inputはそれ単独では文字の入力欄になりますが、type="〇〇"と書くことで色々な種類の入力欄を作る事が出来ます。
　その一例として、今回は次の三つを紹介します。まず`<input type="number">`、これは数字の入力欄と言う意味になります。次は`<input type="checkbox">`、これはチェックボックスを作ります。最後は`<input type="range">`、これはスライドバーを作る時に使います。
「実行結果」を見てみてください。Value、Check、Sliderの横に、対応した入力装置があるのがわかります。

　さて、`min="0" max="100" step="25" value="50"`の部分は数字入力欄に入力できる数字の最小値、最大値、ステップ（この場合0、25、50、75、100が入力可能）、そしてデフォルトの値を設定しています。

　そしてある意味一番重要な事。それが`id="number"`です。このように書くことで、JavaScript側から要素にアクセスすることができます。
　ボタンの項目で`onclick="onClickOk()"`と書くことでHTMLからJavaScriptを呼び出せると話しましたが、今回はその逆と思っていただければ分かりやすいかと思います。つまり、`id="○○"`と書いておけば、JavaScriptからHTMLを参照できるのです。

　次はselectについてお話ししましょう。selectはプルダウンを作る事が出来ます。
```html
        <div>
            Select
            <select id="select">
                <option>Apple</option>
                <option>Banana</option>
                <option>Peach</option>
            </select>
        </div>
```
　これでApple、Banana、Peachの中から一つを選択できるプルダウンを作る事が出来ます。
　また、selectに対して`id="select"`と言うようにidを付けている事にも注目です。これもJavaScript側から参照したいのでidを割り当てているのです。

　最後にscriptについてみていきましょう。
```javascript
        async function onClickOk(){
            let data = {};
            data["number"] = parseInt(document.getElementById("number").value);
            data["check"] = document.getElementById("check").checked;
            data["select"] = document.getElementById("select").selectedIndex;
            data["slider"] = parseInt(document.getElementById("slider").value);
            await pywebview.api.pyprint(data);
        }
```
　`let data = {};`でデータの入れ物を用意しています。今からここに、Pythonに送りたいデータを詰め込んでいきます。
　まずは"number"という名前で数字を詰めます。その書き方が`data["number"]=○○;`です。○○には中に入れたい数字を記述します。さて、○○の部分が今回は`parseInt(document.getElementById("number").value)`となっています。これはいったい何でしょうか？
　まずはparseInt(△△)について。これは文字列を整数に変換する関数です。`"1234"`は文字列ですが、`parseInt("1234")`とすることで1234という数字に変換されます。
　次に`document.getElementById("number")`と言う部分。これはHTMLの中で"number"というidが割り振られた要素を探しなさいという意味です。探してきた要素のvalue（中身）を整数に変換しているわけですね。

　同様に、`data["check"] = document.getElementById("check").checked;`では`"check"`というidが割り振られた要素にチェックが入っているかどうかを取得し、その結果をdata["check"]に保存しています。他の要素についても同じです。

　最後に`await pywebview.api.pyprint(data);`で用意したデータを送信しています。こうしてJavaScriptから色々なデータを一括で送信することができるようになりました。

　なお、受け取ったPython側でもdata["number"]と書くことで、dataの中のnumberにアクセスできます。


:::note info
確認
・`<div>……<\div>`と書くと、その中身がひとかたまりと言う意味になります。
・input要素にはtypeとidを指定する必要があります。
・select要素はプルダウンを作る時に必要です。これにもidをつけておきましょう。
・document.getElementById("id名")でJavaScriptから要素を検索できます。
・`let data={};`や`data["データ名"]=中身;`のように書くことで、データを用意できます。
※data={}はPythonで言う所のdictionaryに近いですね。
:::

## ダイアログ

　ところで、画像処理を行うなら「ファイルを選択する」機能は必須ですよね。そこで次はファイル選択ダイアログとフォルダ選択ダイアログの表示のさせ方をお伝えできればと思います。

### 取り敢えずコードを
　まずはindex.html。bodyの中身だけです。
```html
    <h1>File Dialog</h1>
    <main>
        <div>
            Open
            <button onclick="onSelectTiff()">Open Files</button>
            <button onclick="onSelectFolder()">Open Folder</button>
        </div>
        <div>
            Save
            <button onclick="onSaveText()">Save File</button>
        </div>
    </main>

    <script>
        async function onSelectTiff(){
            let res = await pywebview.api.selectTiff();
            console.log(res);
        }
        async function onSelectFolder(){
            let res = await pywebview.api.selectFolder();
            console.log(res);
        }
        async function onSaveText(){
            await pywebview.api.saveText("Hello World");
        }
    </script>
```
　三つのボタンと三つの関数がありますね。
　それ以外は特に変わっていません。

　次にrun.pyを
```python
import webview

class Api:
    def selectTiff(self):
        file_types = ('Image Files (*.tif)', 'All files (*.*)')
        result = window.create_file_dialog(webview.OPEN_DIALOG, allow_multiple=True, file_types=file_types)
        print(result)
        return result
    def selectFolder(self):
        result = window.create_file_dialog(webview.FOLDER_DIALOG)
        print(result)
        return result
    def saveText(self, text):
        file_types = ('Text Files (*.txt)', 'All files (*.*)')
        result = window.create_file_dialog(webview.SAVE_DIALOG, file_types=file_types)
        with open(result, mode="w") as f:
            f.write(text)

api=Api()
window = webview.create_window("File Dialog", url="web\\index.html", js_api=api)
webview.start(http_server=True, debug=True)

```
 classの中に三つの関数が入っていますね。`selectTiff`と`selectFolder`と`saveText`です。

### ざっくりコード解説。

　HTMLの方は特に変わったことはしていませんね。三つのボタンがあるので、それぞれに対応した三つの関数を用意しています。そして、それぞれが異なるPythonの関数を読んでいます。

　Pythonの方も複雑そうですがほぼほぼ同じことの繰り返しで、新しく出てきた情報は以下の関数だけです。
```python
window.create_file_dialog()
```
　これはファイルダイアログを作る関数です。名前のままですね。file_dialogをcreateします。
　引数が複雑なので、順番に見ていきましょうか。

　第一引数はダイアログのタイプです。
1. webview.OPEN_DIALOG
ファイルを開きます。基本的に既に存在するファイルしか選ぶことができません。
2. webview.FOLDER_DIALOG
フォルダ（ディレクトリ）を開きます。「このフォルダ内の画像を一括処理したい」みたいな時に使うと良いでしょう。
3. webview.SAVE_DIALOG
ファイルを保存するときのダイアログです。基本的に存在しないファイル（今から新しく作るファイル）を選びます。なお、既にあるファイル名を選ぶと「上書きます、よろいしいですか？」のように聞かれます。

　オプションとして以下のようなものを書くことが出来ます。
- allow_multiple=True
ファイル選択ダイアログに関して、複数選択を許可するかどうかを指定できます。
- file_types=(..., ..., ..., ...)
ファイル選択ダイアログ、およびファイル保存ダイアログに関して、ファイルタイプ（.txtなのか.tifなのか、.bmpなのか、などなど）を指定できます。

　それとPythonに関して以下のコードはファイルを開いてテキストを書き込むコードとなっています。
　今回は本題ではないので、詳しくは述べません。
```python
with open(ファイル名, mode="w") as f:
    f.write(テキスト)
```

:::note info
確認
・window.create_file_dialog()でダイアログを表示できます。
・ダイアログには「ファイルを開く」「フォルダを開く」「ファイルを保存する」の三種類あります。
:::

## 動的にページの内容を更新
　ここまでのウェブサイト(HTML)は静的でした。つまり、一度立ち上げたらウェブサイト自体の見た目は一切変化しません。ということで、次はウェブサイトの見た目を動的に変化させるサンプルコードを紹介します。

### 取りあえずコードを見る
 HTMLのbodyの中身です。
```html
    <h1>ul Element</h1>
    <main>
        <div>
            Open
            <button onclick="onSelectTiff()">Open File</button>
            <button onclick="onSelectFolder()">Open Folder</button>
        </div>
        <ul id="images">

        </ul>
    </main>

    <script>
        async function updateList(files){
            if(files == null)return;
            let text = "";
            for(const file of files){
                text += "<li>" + file + "</li>";
            }
            document.getElementById("images").innerHTML = text;
        }
        async function onSelectTiff(){
            let res = await pywebview.api.selectTiff();
            updateList(res);
        }
        async function onSelectFolder(){
            let res = await pywebview.api.selectFolder();
            updateList(res);
        }
    </script>
```
　新しい部分は二つ。HTMLでは`<ul id="images"></ul>`という新しい要素があります。もう一つはJavaScript、`updateList()`という関数でよく分からないことをしていますね。

　次はPython
```python
import webview
import glob

class Api:
    def selectTiff(self):
        file_types = ('Image Files (*.tif)', 'All files (*.*)')
        result = window.create_file_dialog(webview.OPEN_DIALOG, allow_multiple=True, file_types=file_types)
        return result
    def selectFolder(self):
        result = window.create_file_dialog(webview.FOLDER_DIALOG)
        if(result != None and len(result) >= 1):
            return glob.glob(result[0]+"\\"+"*.tif")
        else:
            return None

api=Api()
window = webview.create_window("ul Element", url="web\\index.html", js_api=api)
webview.start(http_server=True, debug=True)
```
　新しい部分はglobでしょうか。これはフォルダ内のファイル一覧を取得する機能です。ここでは本題ではないので詳細は割愛させて頂きます。

### ざっくりコード解説
　まずは`<ul></ul>`について。これはリストです。下のようなものです。

- バナナ
- パイナップル
- マンゴー

　上記のリストをHTMLで書くと次のようになります。
```html
<ul>
    <li>バナナ</li>
    <li>パイナップル</li>
    <li>マンゴー</li>
</ul>
```
　つまり、ulはリスト全体を囲む物です。そして、各項目はliの中に書きます。

　では次に、以下のJavaScriptを解説します。
```js
async function updateList(files){
    if(files == null)return;
    let text = "";
    for(const file of files){
        text += "<li>" + file + "</li>";
    }
    document.getElementById("images").innerHTML = text;
}
```
　二行目`if(files == null)return;`は「ファイルが選択されていないなら、何もせずに終了」という意味です。具体的には、ファイル選択ダイアログで「キャンセル」を押された時にこれが呼び出されます。
　三行目の`let text="";`はtextという文字列変数を作っています。空の文字列で初期化しています。
　四行目の`for(const file of files){……}`についてです。これはPythonで言う所の「for file in files:」に該当します。JavaScriptではinではなくofを使う点に注意しましょう。なお、filesの中身は選択されたtifファイルのリストです。
　五行目ではtextに`<ul>ファイル名</ul>`という形式の文字列を結合しています。例えば選択されたファイルが「a.tif」「b.tif」「c.tif」ならtextの中身は以下のようになります。
```
<li>a.tif</li>
<li>b.tif</li>
<li>c.tif</li>
```
　いい感じでHTMLを含んだ文字列を作れました！　これをul要素の中に入れる事が出来たら、ファイルリストを表示させることができます。それが以下のコードです。
　`document.getElementById("images").innerHTML = text;`

　`document.getElementById("images")`はimagesというidが割り振られた要素を取得するんでしたよね。これでHTML中の`<ul id="images"></ul>`を取得できます。
　その要素の中身のHTMLにアクセスするには`.innerHTML`を使います。今回ですとul要素の中身のHTMLを`text`にしたいので`document.getElementById("images").innerHTML = text;`となります。

### 実行結果
　実行し、ファイルを選択すると、選択したファイルのリストが画面に表示されます。また、フォルダを選択するとその中に入っているファイルのリストが表示されるはずです。


:::note info
確認
・`<ul></ul>`はリストを表示する要素です。
・`<ul>`の中に入れる各要素は`<li></li>`で囲みます。
・JavaScriptのfor文はinではなくofを使います。
・innerHTMLを使って、要素の中身を書き換える事ができます。
:::