Pywebview + Vite + React でデスクトップアプリを作成する

## はじめに

Pythonでデスクトップアプリを作る選択肢は様々あります（Tkinter, Pyside, Fletなど）。
ただ、Web技術を使うとなると、Electron と Flask をうまく繋ぎこむ必要があります。
と思っていたのですが、偶然にも Pywebview というライブラリを ChatGPT におすすめされ、調べてみると、「これぞ求めていたものだ！」と感動しました。

Pywebview は簡単にいうと、Electron の Python 版みたいなものです（詳しいことはちゃんと理解していません…🙂）。HTML や React などで作ったフロントエンドをブラウザエンジン（Webkitなど）で表示させて、デスクトップアプリとして使うみたいな感じだと思います。

## Vite + React でフロントエンドを組みたい

[公式のボイラープレート](https://pywebview.flowrl.com/examples/)だと、parcel や create-react-app で作った React アプリしかありません。Vite は爆速だし、個人的に最近よく使っているので、[Vite + React で Pywebview アプリを作れるボイラープレート](https://github.com/ShijiMi-Soup/pywebview-with-vite-react)を作りました。

https://github.com/ShijiMi-Soup/pywebview-with-vite-react

今回は、ボイラープレートを使って、アプリを作ってみるところまでご紹介します。

## 環境

- MacBook Air M1 2020 (Sonoma 14.4.1)
- Python 3.10.7
- Node 21.1.0

## セットアップ

まずは、[ボイラープレートのレポジトリ](https://github.com/ShijiMi-Soup/pywebview-with-vite-react)にアクセスし、「Use this template > Create a new repository」します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528997/bf6358c9-a389-4c82-d98a-b1ed34fd5cd9.png)

お好きなレポジトリ名をつけて Create repository します。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528997/dc483d67-4643-ba97-0c47-7a6a6f0bb79e.png)

作成したリポジトリをローカルにクローンします。

```shell
git clone https://github.com/ユーザー名/リポジトリ名.git
```

READMEのとおりに諸々のインストールをします。

```shell:Mac
npm run init
python -m venv venv
source venv/bin/activate
pip install -r requirements.txt
```

```powershell:Windows
npm run init
python -m venv venv
.\venv\Scripts\activate
pip install -r requirements.txt
```

:::note warn
Linux対応はしていませんが、[元のボイラープレート](https://github.com/r0x0r/pywebview-react-boilerplate)の package.json を参考にすると利用できるかもしれません
:::

## 動作確認

### フロントエンドのみ

フロントエンドのみを走らせるには、下記を実行します。

```shell
npm run dev
```

ブラウザで [http://localhost:5173](http://localhost:5173/) にアクセスすると、こんな画面が見えます。
フロントだけなので、Saveボタンは動作しません。

![Screenshot 2024-05-16 at 17.55.15.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528997/b1694996-5454-d823-472c-9a4b10d706c7.png)

### フロントエンドとバックエンド 

フロントエンドをビルドして、バックエンドを走らせるには、下記を実行します。

```shell
npm run start
```

こんな感じのウィンドウが表示されるはずです。
バックエンドが動いているので、カウントが表示されるのと、Saveボタンでテキストを保存することができます。

![Screenshot 2024-05-16 at 17.58.07.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528997/572c7182-ee73-1ad8-7262-e7da3c55afb8.png)

### デスクトップアプリとしてビルド

ビルドするには下記を実行します。

```shell
npm run build
```

dist フォルダ内にアプリが生成されているはずです。

:::note warn
Windowsの場合、Windows Defender などのセキュリティソフトウェアで、アプリの生成がブロックされることがあります。[回避方法はあるみたい](https://www.google.com/url?sa=t&rct=j&q=&esrc=s&source=web&cd=&cad=rja&uact=8&ved=2ahUKEwjRzNGv6JGGAxX5qlYBHdIaAYkQFnoECA0QAQ&url=https%3A%2F%2Fqiita.com%2Ftru-y%2Fitems%2Fcb3cebe9612d367dccb2&usg=AOvVaw2R0kFSxooy8zLRF8FFpcps&opi=89978449)ですが、僕は面倒なので[Windowsの設定を少し変更](https://stackoverflow.com/questions/77346372/pyinstaller-says-i-made-a-virus)しました。
:::

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528997/18390af8-811f-8306-615e-aef20b255c30.png)

ダブルクリックするとアプリが起動します。

![Screenshot 2024-05-16 at 18.11.48.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528997/1de32010-7860-ed51-f4e6-c05d30f20895.png)

## 機能の追加

最後に、「Numpyで生成された乱数配列を表示するボタン」を実装します。

### バックエンドを編集

numpy を pip install します。

```shell
pip install numpy
```

`backend/index.py` の `class Api` を編集します。

```diff_python:backend/index.py
  class Api:
+     def rand_arr(self):
+         return np.random.rand(10).tolist()
+
      def fullscreen(self):
  ...
```

### フロントエンドを編集

`frontend/src/components/Editor/Editor.tsx` で `button` を追加します。

:::note
クリックすると、rand_arrを実行し、生成された乱数配列をテキストボックスに出力するように`onClick`を書き換えています。
:::

```diff_tsx:frontend/src/components/Editor/Editor.tsx
  return (
     <div className="editor-container">
       <textarea
         className="textarea"
         value={content}
         onChange={(e) => {
           saveContent(e.target.value);
         }}
       />
       <br />

+      <button
+        className="button"
+        onClick={() => {
+          // @ts-expect-error: We are using a custom API
+          window.pywebview.api.rand_arr().then((arr: number[]) => {
+            const randArrSttr = arr.join(", ");
+            saveContent(randArrSttr);
+          });
+        }}
+      >
+        Generate Rand Arr
+      </button>
       <button
         className="button"
         onClick={() => {
           window.pywebview.api.save_content(content);
         }}
       >
         Save
       </button>
     </div>
   );
```

### 実行

あとは実行してみるだけです！

```shell
npm run start
```

こんな感じで、ボタンを押すと乱数が生成されるはずです（Saveを押せばそのまま保存できます）。

![Temp.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/528997/320ceb99-8b62-d7c9-4ed6-75fbcaafc950.gif)

## 終わりに

今回は、私が作成したボイラープレートを使って、 Pywebview + Vite + React のデスクトップアプリを作る方法を説明しました。思ったよりも簡単ではなかったでしょうか？

ReactはUIフレームワークやアニメーションフレームワークが豊富なので、ぜひかっこいいアプリを作ってみてください！

また、今回説明に利用したリポジトリはこちらです。必要に応じて参考にしてください。

https://github.com/ShijiMi-Soup/pywebview-vite-react-tutorial