ElectronとPython（Flask）を使ったデスクトップアプリの制作からパッケージングまで

## はじめに
ElectronとPython（Flask）を使ったデスクトップアプリのパッケージングにとても苦労したので、同じように困っている人の役に立てばいいなと思っています。

## 完成したアプリ

![アプリ画面完成図](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397359/19984589-1eb5-ffc1-8126-18084a556847.jpeg)

## 開発環境
Windows 10

## 環境構築
インストールに`npm`を使用します。
`npm`にはNode.jsが必要となりますので事前にインストールしてください。
[Node.jsダウンロード画面](https://nodejs.org/ja/)

## Electron
### package.jsonの作成
作業ディレクトリを作成し，そこで`npm init`します。
`-y`オプションをつけることで初期値で package.json が生成されます。

```
> mkdir sample_app
> cd my_electron
> npm init -y
```

```json:package.json
{
  "name": "sample_app",
  "version": "1.0.0",
  "description": "",
  "main": "index.js",
  "scripts": {
    "test": "echo \"Error: no test specified\" && exit 1"
  },
  "keywords": [],
  "author": "",
  "license": "ISC"
}

```

### Electronのインストール
`-D`オプションは`--save-dev`の省略で、モジュールが現在のプロジェクトにインストールされます。
プロジェクト毎に異なるバージョンをインストールすることができます。

```
> npm i -D electron
> npx electron -v
  v6.0.1
```

### Welcomeウィンドウの表示
Electronのインストールが完了したらWelcomeウィンドウを表示してみます。
`npx`は現在のプロジェクトにインストールしたElectronにパスが通ります。

```
> npx electron
```
Welcomeウィンドウが表示されます。
![ElectronのWelcomeウィンドウ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397359/a4b5c77f-b670-51b9-2aa8-5d964a74eb43.jpeg)

## Flask
### Python3のインストール
Flaskを使用するためにPython3をインストールしてください。
[Python3のダウンロード画面](https://www.python.org/downloads/)
[Python3の導入方法](https://www.python.jp/install/windows/install_py3.html)

### Flaskのインストール
今回はPythonの軽量なWebフレームワークであるFlaskを使用します。

```
> pip install Flask
> flask --version
  Flask 1.1.1
```

### 必要なモジュールのインストール
`--save`オプションをつけることで、パッケージにした際にも呼び出すことができます。

```
> npm install --save request
> npm install --save request-promise
> npm install --save python-shell
```

###必要なファイルの作成
以下のディレクトリ・ファイルを作成して、画像のようなディレクトリ構造にしてください。
├ templates
│ └ index.html
├ app.py
└ index.js
![ディレクトリ](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397359/61fb9a3e-f0b6-15d9-ff2c-fae8288510a4.png)

#### index.html
フロントの画面を作成します。

```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
</head>
<body>
<div class="container">
    <h1>Hello, World!</h1>
    <button class="btn btn-primary">button</button>
    <p id="text">text</p>
</div>
</body>
</html>
```

#### app.py

```python:app.py
from __future__ import print_function
import time
from flask import Flask, render_template

app = Flask(__name__)

@app.route("/")

def hello():
    return render_template("index.html")

if __name__ == "__main__":
    print('on hello')
    app.run(host="127.0.0.1", port=5000)
```

#### index.js
```js:index.js
// Electron側の初期設定
const {PythonShell} = require('python-shell');
const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
let mainWindow;

// アプリを閉じた時にquit
app.on('window-all-closed', function() {
  app.quit();
});

// アプリ起動後の処理
app.on('ready', function() {
  PythonShell.run('./app.py', null, function (err, result) {  // exe:'./resources/app/app.py'    edit: './app.py'
    if (err) throw err;
    console.log(result);
  });
  const rq = require('request-promise');
  const mainAddr = 'http://localhost:5000';

  const openWindow = function() {
    mainWindow = new BrowserWindow({width: 400, height: 300 });
    mainWindow.loadURL(mainAddr);

    // 開発ツールを有効化
    // mainWindow.webContents.openDevTools();

    // 終了処理
    mainWindow.on('closed', function() {

      // キャッシュを削除
      electron.session.defaultSession.clearCache(() => {})
      mainWindow = null;
    });
  };

  const startUp = function() {
    rq(mainAddr)
      .then(function(htmlString) {
        console.log('server started');
        openWindow();
      })
      .catch(function(err) {
        startUp();
      });
  };

  startUp();
});
```

### 実際に動かしてみる

```
> npx electron .
```
![アプリ画面_02](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397359/3660e607-6d74-09aa-b6fd-9e72bd77dc48.jpeg)
うまく動作しました。
これでほぼ完成ですが、どうせならCSSやJavaScriptも読み込んでみましょう。

## CSSやJavaScriptの使用
### ディレクトリ・ファイルの追加
以下のディレクトリ・ファイルを作成して、画像のようなディレクトリ構造にしてください。
static
├ css
│ └ style.css
└ js
 　└ script.js
![ディレクトリ_02](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397359/404d6345-9024-f6fb-6887-d489ea33cb8c.png)

#### style.css
 ```css:style.css
h1 {
    color: #B00020;
}
```

#### script.js
```js:script.js
function sample() {
    document.getElementById('text').textContent = 'clicked!';
}
```

### CSSやJavaScriptの読み込み
```html:index.html
<!DOCTYPE html>
<html lang="ja">
<head>
    <meta charset="UTF-8">
    <title>Title</title>
    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.1.0/css/bootstrap.min.css" integrity="sha384-9gVQ4dYFwwWSjIDZnLEWnxCjeSWFphJiwGPXr1jddIhOegiu1FwO5qRGvFXOdJZ4" crossorigin="anonymous">
    <link rel="stylesheet" href="/static/css/style.css"> <!-- ★追加 -->
    <script type="text/javascript" src="/static/js/script.js"></script> <!-- ★追加 -->
</head>
<body>
<div class="container">
    <h1>Hello, World!</h1>
    <button class="btn btn-primary" onclick="sample()">button</button> <!-- ★更新 -->
    <p id="text">text</p>
</div>
</body>
</html>
```

### 実際に動かしてみる

```
> npx electron .
```

![アプリ画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397359/91895227-d598-6b02-3968-7162e4c9a9a7.jpeg)
うまくCSSが読み込まれているため、文字の色が変わっています。
また、JavaScriptも読み込まれているため、ボタンをクリックするとテキストが変わります。

残すはパッケージングだけです！
あと少しで完成です！頑張りましょう！

## パッケージング
### electron-packagerのインストール

```
> npm i -D electron-packager
```

### index.jsの更新
実行する前にデスクトップアプリ用に更新する箇所があります。
パッケージングしたあとのディレクトリ構造に合わせます。

```js:index.js
...
// アプリ起動後の処理
app.on('ready', function() {
  PythonShell.run('./resources/app/app.py', null, function (err, result) {  // ★更新 exe:'./resources/app/app.py'    edit: './app.py'
    if (err) throw err;
    console.log(result);
  });
...
```

### electron-packagerの実行

```
> npx electron-packager . sample_app --platform=win32 --arch=x64 --overwrite
```
sample_appディレクトリの配下にパッケージングされたディレクトリが作成されます。
sample_app.exeでアプリが起動します。

sample_app
└ sample_app-win32-x64
　└ sample_app.exe
![アプリ画面](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/397359/22632ef3-5dfc-d1d6-080b-fd18fd7777c8.jpeg)
うまく動作しました。
これで完成です！

お疲れ様でした！

### 他の人が実行するときの注意点
PCにPython3とFlaskがインストールされていなければエラーとなり起動しません。
他の人が実行するには、Python3とFlaskをインストールしてもらってください。
[Python3のダウンロード画面](https://www.python.org/downloads/)
[Python3の導入方法](https://www.python.jp/install/windows/install_py3.html)

Flaskのインストール

```
> pip install Flask
```

## おわりに
初めてWebに記事を投稿しました。
パッケージングするのにとても苦労したので、この記事が他の人の役に立てば嬉しいです。

以下の記事を参考にさせていただきました。ありがとうございます。
[日頃お世話になっているElectronのアプリ開発に入門してみる](https://qiita.com/y-tsutsu/items/179717ecbdcc27509e5a#%E3%83%91%E3%83%83%E3%82%B1%E3%83%BC%E3%82%B8%E3%83%B3%E3%82%B0)
[Pythonでデスクトップアプリを作る with Electron](https://qiita.com/yoshizaki_kkgk/items/da9711c26e71522ad289)