Hatena::ブログ(Diary)

サイト更新停滞ちうっ このページをアンテナに追加 RSSフィード

2018-10-20

[][] Electronアプリは、まずelectron-localshortcut入れて、Command+R、Command+Wを潰すのがセオリーだと思う 18:02  Electronアプリは、まずelectron-localshortcut入れて、Command+R、Command+Wを潰すのがセオリーだと思うを含むブックマーク  Electronアプリは、まずelectron-localshortcut入れて、Command+R、Command+Wを潰すのがセオリーだと思うのブックマークコメント

おまえ、セオリー語れるほど、Electronやってないだろう。


globalShortcut

Electronにはショートカットキーを制御するglobalShortcutという機能があるんですけど、
これは公式ドキュメントにも書いてあるとおり、グローバルに効いてしまう。

アプリにフォーカスが当たって無くても、効いちゃうんですね。
これはこれで便利だけど、効き過ぎちゃう。

こんなCommand+Sを封じるアプリとか作ったら、あらゆるアプリのCommand+Sを封じちゃって大変ですね。
やってはいけませんよ。

'use strict';
const electron = require('electron');
const app = electron.app;
const globalShortcut = electron.globalShortcut;

app.on('ready', function() {
  globalShortcut.register('Command+S', function() {                                                    
    // アプリが立ち上がっている間、Command+S による保存を封じるぞ
    // 嫌がらせかな?
    console.log('Command+S is pressed')
  })
});

electron-localshortcut

ということで、electron-localshortcutです。
アプリショートカットキーをつけたいならこちらです。

npm install --save electron-localshortcut

BrowserWindowインスタンスを生成したあと、
ウィンドウ毎にショートカットキーを登録する。
昔はウィンドウ指定できなかった。今はショートカットキーを有効にするウィンドウを指定する仕様になった。

var electron = require('electron');
var app = electron.app;
var localShortcut = require("electron-localshortcut");
var mainWindow;

...(中略)...

mainWindow = new BrowserWindow({
  width: 600,
  height: 600,
  acceptFirstMouse: true,
  show: false, // show at did-finish-load event
});
mainWindow.loadURL(`file://${__dirname}/window-main.html`);

// ショートカットキー
// Command+Qでアプリを閉じる
localShortcut.register(mainWindow, 'Command+Q', function() {
  app.quit();
});

// event
mainWindow.webContents.on('did-finish-load', function() {
  mainWindow.show();
  mainWindow.focus();
});

アプリらしく

で、今回のタイトルです。
Electronのアプリを作ると、
Command+R で画面のリロード、
Command+W でウィンドウを閉じる機能が動いてしまう。
ウェブブラウザに元々ついている機能です。

f:id:taku-o:20181020173347p:image

f:id:taku-o:20181020173348p:image


アプリにもよりますけど、自分はこれ、格好悪いと思う。
とてもブラウザブラウザ(?)してる。

なので、electron-localshortcut で、これのショートカットキーを上書きして、
機能にしないようにしておくべきなのではないか。そう思うのです。

localShortcut.register(mainWindow, 'Command+R', function() {
  // do nothing
});
localShortcut.register(mainWindow, 'Command+W', function() {
  // do nothing
});

おわり

おわりです。

なんかMac前提の話してるけど、細かいことは気にしない!!!

2018-10-19

[][][] 画像をElectron側にキャッシュしておく話 00:04  画像をElectron側にキャッシュしておく話を含むブックマーク  画像をElectron側にキャッシュしておく話のブックマークコメント

Electronを業務で使っている会社とかあるらしい。

で、自分も活用してみよう。Electronでどんなアプリを作るかって考えたら、


サーバーのデータを参照するElectronアプリも作れるけれど、
でも、それだったら、Webページでも良いわけで、
Electronを使うなら、やっぱりスタンドアローンアプリにしたいじゃないですか。

f:id:taku-o:20181019234340p:image


しかし、業務で扱うデータって、残念ながら、だいたいサーバー上に置いてある。
そのデータに触れずに業務に役立つアプリを作ることは難しい。

それじゃ、こういう、ローカルとサーバ上、両方にデータがあるElectronアプリにするのが良いのかな、と。

f:id:taku-o:20181019234339p:image


そこで画像キャッシュ

なぜ、画像かはわからないが、今回は画像だ。
毎回データをサーバーに取りに行かずに、(作るのが重い画像とかだ)
Electronのアプリ内部でキャッシュしておこう、という話。


実装

  • まず最初にimage-cacheを入れておいて、
npm install --save image-cache
  • cacheに画像を渡す処理。cacheから画像を取り出す処理を入れる。
# renderer.js
var imageCache = require('image-cache');

...(中略)...

// tweet.user_image は 画像URL
//
// not found image cache
if (! imageCache.isCachedSync(tweet.user_image)) {
  imageCache.setCache(tweet.user_image, function(error) {
    log.error(error);
  });
  tweet.user_image_link = tweet.user_image;

// found image cache
} else {
  const img = imageCache.getCacheSync(tweet.user_image);
  tweet.user_image_link = img.data;
}
  • angular.jsだけど
  • image-cacheから取り出したcacheは、BASE64な文字になっている。そのままそれを表示して良い。
# renderer.html
<li ng-repeat="item in streams track by item.idx">
  <img ng-src="{{item.user_image_link}}" width="32" height="32">
</li>

まとめ

Electronあまり関係ない話になってしまった。

2018-10-18

[][] Electronの画面にHTML落とすと、画面が切り替わっちゃうよね 21:18  Electronの画面にHTML落とすと、画面が切り替わっちゃうよねを含むブックマーク  Electronの画面にHTML落とすと、画面が切り替わっちゃうよねのブックマークコメント

Electronで立ち上げたBrowserWindowの画面に、
HTMLファイルを落とすと、
そのHTMLがウィンドウ内に表示されてしまう。

f:id:taku-o:20181018205922p:image


格好悪い!!!


これをふせぐには

ドロップした時に、画面が差し替わる問題を防ぐには、次のようなコードを入れておくと良い。

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script type="text/javascript">
    document.ondragover = document.ondrop = function(e) {
      e.preventDefault(); return false;
    };
  </script>
</head>

ドロップされたHTML上では、nodeとElectronの機能を使える

この挙動はアプリとして格好悪いが、
それだけはなく、
落としたHTMLではnodeとElectronの機能を利用できてしまう。
(↓のファイルをウィンドウにドロップする。)

# evil.html
<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <script src="js/evil.js"></script>
</head>
<body>
evil
</body>
</html>
# js/evil.js
var fs = require('fs');
var filePath = '/Users/your.name/Desktop/evil.txt';

fs.writeFile(filePath, 'eviiiiiiilll', function(err) {
  if (err) {
    console.log(err);
    return;
  }
  console.log('done.');
});

アプリを操作できるような状況になってるわけだから、
特にセキュリティ的な問題にならないでしょうけどね。


Electron 3.0

結局、この挙動はやっぱり良くないよね、ってことで、
Electron 3.0では、デフォルトで無効にされたのであった。

おわりまる

2018-10-17

[][] Electronのアプリが落ちたり、画面がまっしろになっってしまった時の、問題の追い方 21:20  Electronのアプリが落ちたり、画面がまっしろになっってしまった時の、問題の追い方を含むブックマーク  Electronのアプリが落ちたり、画面がまっしろになっってしまった時の、問題の追い方のブックマークコメント

Electronアプリでのエラーの追い方。


アプリが起動もしないで落ちる時

アプリをクリックして起動したら、そのまま落ちる時。
最初に追うのは main processの初期化まわりで問題が起きていないか。
electron-logを入れて、ログ出力するのが良いと思う。


electron-logを入れる (https://github.com/megahertz/electron-log)

npm install --save electron-log

main processのjavascriptコードの上の方に、エラーを拾うコードを追加する。
便利だから、このコードは入れっぱなしでリリースしても良いと思う。

var log = require('electron-log');

process.on('uncaughtException', function(err) {
  log.error('electron:event:uncaughtException');
  log.error(err);
  log.error(err.stack);
  app.quit();
});

electron-logで出力されたエラーログを見に行く。
↓この場所に出力されます。
アンインストール方法という資料があるなら、このログファイルを消す手順も追加しておきましょう。

view $HOME/Library/Logs/YourAppName/log.log

画面がまっしろになる

アプリの画面がまっしろ。何も表示されない。

  • renderer process側で、読み込めていないリソースがあるか、
  • JavaScriptでエラーが起きているのではないか?

Developer Toolを開いて、エラーが起きていないか確認しましょう。


なんらかの操作をしたら、プチっとアプリが落ちる

なんらかの操作をトリガーとして、突然、アプリが落ちる。
↑で紹介した、electron-logのログに何か出てるのではないか?
例えば、このように。
このログは main process で読み込んでいるnpm moduleが見つからなかったエラー。

[2018-10-17 20:11:26:0174] [error] Error: Cannot find module 'about-window'
    at Module._resolveFilename (module.js:485:15)
    at Function.Module._resolveFilename (/Users/taku-o/Desktop/myukkurivoice/release/myukkurivoice/MYukkuriVoice-darwin-x64/MYukkuriVoice.app/Contents/Resources/electron.asar/common/reset-search-paths.js:35:12)
    at Function.Module._load (module.js:437:25)
    at Module.require (module.js:513:17)
    at require (internal/module.js:11:18)

自分はファンクションのパラメータ名と、
変数のスペル名を間違えていて、アプリが落ちる現象に遭遇したことがある。
エラーログが出なくて苦労した。


ダイナミックライブラリを読み込んで、
誤った操作した時も、もちろんアプリが無言で落ちた。
こちらは実装している人には原因がわかりやすい。


アプリにパッケージングしてない状態では動く。パッケージングすると動かない。

electron .

では正しく動作する。electron-packagerなどでアプリに固めると、動かなくなる現象


どこで動かなくなっているかにもよるけれど、

  • 片方にしか入っていないファイルは無いか?
  • アプリケーションのapp.asarの中に、想定通りのファイルは入っているか?
# asarコマンドで、app.asarの中を覗ける
npm install -g asar
asar e YourAppName.app/Contents/Resources/app.asar dest

  • electron-packagerのコマンドオプションでasar.unpackDirを指定していて、アプリで実行した時にはファイルパスが間違っていたりしない?
  • 実行権限の必要なファイルを、app.asarの中に入れてしまっていないか
  • electron-packagerのコマンドオプションignoreで、実行に必要なファイルを指定してないか

electron-packagerのignoreオプションRegExpのmatchです。
typescriptのファイルを除こうとして、".ts"と指定したら

contents

という文字列にマッチして、アプリが動かなくなったことがある。


まとめ

アプリが落ちたり、エラーが出たりはしないけど、うまく動かない、は自力で追って欲しい。
まとめたら、個人開発だけど、いろんなトラブルを拾ったなぁ。

2018-10-16

[][] electron-json-storage、electron-storeを使ったElectronアプリのテストの話 22:36  electron-json-storage、electron-storeを使ったElectronアプリのテストの話を含むブックマーク  electron-json-storage、electron-storeを使ったElectronアプリのテストの話のブックマークコメント

経緯とか

Electronでよく使われる(?)設定ファイル読込系ライブラリ

がある。


これらのライブラリは"userData"に設定ファイルを置くけれど、
設定ファイルの位置を変更する機能がないので、
単体テストを実行したら、
"userData"に置いていた私が丹精込めて育てた設定ファイルが上書きされちゃったことがある。

// Macならこの位置になる
$HOME/Library/Application Support/$app_name/

どのように対応する?

調べたら、例えば、このように対応したら良いらしい。

https://github.com/electron/spectron/issues/202


Spectronでapp.start()後に、アプリ側でuserDataを変更する。
これで私の設定ファイルは守護られたのだ…

// テストコード(抜粋)
// mainSpec.ts
import {Application} from 'spectron';
import * as assert from 'assert';
import * as temp from 'temp';
temp.track();

describe('window-main', function() {
  this.timeout(10000);

  beforeEach(function() {
    const fsprefix = `_ollfrow_${Date.now().toString(36)}`;
    const dirPath = temp.mkdirSync(fsprefix);
    this.app = new Application({
      path: 'ollfrow-darwin-x64/ollfrow.app/Contents/MacOS/ollfrow',
      env: {NODE_ENV: 'test', userData: dirPath},
    });
    return this.app.start();
  });

(略)
// electron.ts テストされるコード
// テスト時はuserDataを差し替える
if (process.env.NODE_ENV == 'test' && process.env.userData) {                                  
    app.setPath('userData', process.env.userData);
}

// 設定ファイルを使ったテスト

electron-json-storage 4.0

electron-json-storageの4.0では、setDefaultDataPath()が追加され、
設定ファイルの置き場所を変更できるようになった。

electron-storeの方は相変わらず"userData"を読み書きしに行くから、上のコードはそのまま利用中なのですけれど。