Electronでデスクトップアプリを作成するまでの記録を残しておきます。自作の電子カルテが出来上がりました。
作成した電子カルテの紹介はほかの記事でやることにして、ここではelectronの使い方をメインでまとめたいと思います。
動物病院の電子カルテです。日本国内での使用を想定しています。
個人的な研究用途に使用してください。臨床目的では使用しないでください。
免責事項
- 当ファイルをご利用する前に免責事項に同意し、同意後にご利用を開始してください。当ファイルをダウンロードすることによってご利用者は、免責事項に同意したものとみなします。
- 当ファイルのご利用者は、自らの行為に一切の責任を負うものとし、当ファイルによって生じた問題については、ご利用者自らで解決し、製作者は一切免責されるものとします。
- 製作者は、ご利用者のファイルの利用により発生した、ご利用者の損害又は第三者の損害に対し、いかなる責任も負いません。製作者は、当ファイル使用によるコンピューターシステムの破損、データ破損、得られたデータの不正確性、及びそれ以外のいかなる原因に基づき生じた損害について賠償する義務を一切負わないものとします。
- 本プログラムは、医薬品、医療機器等の品質、有効性及び安全性の確保等に関する法律の承認を受けたものではありません。そのため、診療には使用しないでください。個人的な研究又は娯楽の用途でお使いください
ライセンス形式:MIT(LICENSE.mdをご覧ください)
Electron
Electronは、htmlとcssとjavascriptでデスクトップアプリを作る仕組みです。Rやpythonを連携させたデスクトップアプリもできます。Rやpythonと違ってexe化するのは難しくありませんでした。
必要な環境を整える
以下の2つは使えるようにしておく必要があります。
- git
- node.js
まずelectronのテンプレートをフォルダにダウンロードします。ターミナルやコマンドプロンプトから、git cloneしてください。
git clone https://github.com/electron/electron-quick-start .
そのフォルダで以下のコマンドを使って必要なモジュールをインストールします。なお、すでにpackage.jsonはダウンロードされるのでnpm initは不要です。
npm install
さてさて、ここからが本番です。まずは、electronの仕組みから理解する必要があります。
Electronの仕組み
基本的には以下のファイルから成り立ちます。
- main.js
- preload.js
- index.html
- renderer.js
main.jsは、electronのプロセスの起点となるところです。main.jsがそのほかを呼び出します。なお、画面を構成するのはindex.htmlとrenderer.jsです。preload.jsはrenderer.jsにnode.jsの機能を渡す役割です。
preload.jsは最近できた要素らしく、古いチュートリアルはこれを使っていません。preload.jsを使うとより安全なアプリケーションになるそうです。しかしまた、このpreload.jsの使い方が難しすぎてよくわからなかったです。情報も少ないし、苦戦しながら理解したのは以下の概念図です。preload.jsの扱い方は、main.jsの中で設定します。
- デフォルト(contextIsolation: true かつ nodeIntegration: false)
- contextIsolation: false(かつ nodeIntegration: false)
- contextIsolation: false かつ nodeIntegration: true
デフォルトの状態を示したのが以下の図です(私の理解)。一番セキュアです。renderer.jsでnode.jsのパッケージを使う場合には、contextbridgeというのを使う必要があります。ユーザーとのインターフェイスを担うrenderer.jsにすべてのnodejsのパワーを与えるとローカルファイルの書き換えなど様々なことが可能になってしまいます。そのため、インターフェイスからnodeを隔絶するかのような設計になっています。
以下は、contextIsolation: false(かつ nodeIntegration: false)の状態です。preload.jsからwindowオブジェクトに、変数や関数などいろいろ追加できます。また、その変数はrenderer.jsからアクセスできます。
contextIsolation: false かつ nodeIntegration: true
数年前のチュートリアル、公式が紹介しているチュートリアルでもこの設定です。ユーザーがインターフェイスからxss攻撃をしてnodeを使用できるためセキュリティ的に問題があるそうです。renderer.jsでrequireしていたらこの設定が必要です。
今回の取り組みでは、一番安全といわれているデフォルトの状態でアプリを作ります。
IPC通信
ユーザが使うインターフェイスは、htmlファイルで作成します。通常のhtmlを作成するときと同様にcssも使えるし、jqueryも使えます。このhtmlファイル(renderer.js)とmain.jsの間でデータのやり取りをするのがIPC通信です。IPC通信を介してきた情報をつかって、node.jsでファイルの処理や読み込み、書き込みを行います。ユーザーによるhtmlへの書き込み情報からからmain.jsに情報を送るのはrenderer.jsが担います。そのため、renderer.jsにnodeの関数を使わせる必要があります。そのため、preload.jsには以下の設定をします。
修正前
window.addEventListener('DOMContentLoaded', () => {
const replaceText = (selector, text) => {
const element = document.getElementById(selector)
if (element) element.innerText = text
}
for (const type of ['chrome', 'node', 'electron']) {
replaceText(`${type}-version`, process.versions[type])
}
})
修正後
const { contextBridge, ipcRenderer} = require("electron");
contextBridge.exposeInMainWorld(
"preloaded", {
send: (channel, data) => {//rendererからの送信用//
ipcRenderer.send(channel, data);
},
on: (channel, func) => { //rendererでの受信用, funcはコールバック関数//
ipcRenderer.on(channel, (event, arg) => func(event, arg));
}
}
);
この修正により、renderer.jsにてpreload.send()を呼ぶことでipcRenderer.send()の機能を使えます。preload.on()でipcRenderer.on()の機能が使えます。window.preload.send()、window.preload.on()でも同じことです。
以下がipc通信の関数の流れです。
renderer.jsにて ipcRenderer.send(“test-send”, data);
→ main.jsにて ipcMain.on(“test-send”, (event, arg) => {event.reply(‘test-reply’, data) //返事});
→ renderer.jsにて ipcRenderer.on(‘test-reply’, (event, arg) => {//処理})
これらのベースを理解することが必要です。
あとは、htmlとcssの作りこみ → renderer.jsとmain.jsの通信 → ウィンドウズ用にexe化 Mac用にapp化 の流れです。これらの記録はほかの記事で。