ラベル electron の投稿を表示しています。 すべての投稿を表示
ラベル electron の投稿を表示しています。 すべての投稿を表示

electron12 でdialogを呼び出す(ContextBridge)

electron12対応へ、前回は

右クリックメニュー(ContextMenu)カスタム処理をmain-processへ移動した(electron 12対応) 

 まで。

 nodeIntegrationはともかくmoduleは将来廃止になるとのこと。

npmパッケージで逃げることもできるが、まとめて移行することに。

つまりいろいろなものをrender-processからmain-processへ移動することになり、ContextBridgeの導入を避けることができない。

 

 # electron11まで使えたdialog呼び出しコード

これまではrender-process側の適当なjsファイル内で完結する下記2行程度の記述で ダイアログを表示することができたのだが、ようはこれが使えなくなる。

 ``` js/mypage.js

const {dialog} = require("electron").remote;

dialog.showMessageBoxSync(null)

```

 

# contextIsolationを有効化

 エラーメッセージによると、ContextBridgeを使うには、contextIsolationを有効化しなければならない

(electron12からデフォルトでtrue)

# ページ上のjsでdialogは呼べない(renderer-process)

ContextBridgeでmain-processへ飛ばしてそこからrequest('electron').dialogを呼ばなければならない。

```

MN@daisy-bell:lina_dicto/$ git diff lina_dicto/js/index.js         }catch(err){
-               message_dialog('error', 'user preference error', "user preference load error:\n" + err.message);
+               window.myApi.message_dialog('error', 'user preference error', "user preference load error:\n" + err.message);
        }
 
 }, false)
```


 

# preload.js内でdialogは呼べない

最初はpreload.js内でdialogを呼ぼうとしたのだが、なぜかrequire('electron')からcontextBridge, ipcRendererのようにdialogを取り出すことができない。

(preload.js内でcontextBridge, ipcRenderer以外が使えないということかもしれない。

後のContextBridgeのサンプルコードなども、見る限りpreload.js自体はrender-process側ということのように見えるし。)

 

# ContextBridgeでdialogを呼び出す

 


 

諸々の非同期を経由で呼び出しているが、 dialog.showMessageBoxSync()はウィンドウを渡せばウィンドウはモーダルに動作する(意図どおりブロックする)ように見える。

(まあとりあえずエラー通知にしか使ってないのでそこはどちらでもよしとする。 )

ページ上のjs -> preload.js上のcontextBridge -> main-processのjs と飛ばす。


 ``` js/index.js (renderer-process)

 window.myApi.message_dialog('error', 'HELLO', 'hello!');

 ```

```preload.js

 const { contextBridge, ipcRenderer, } = require("electron");
contextBridge.exposeInMainWorld(
    "myApi", {

        message_dialog: (strtype, strtitle, strmessage) => {

            ipcRenderer.invoke('tomain-message-dialog', strtype, strtitle, strmessage);

```

``` index.js(main-process)

const { ipcMain } = require('electron');
ipcMain.handle('tomain-message-dialog', async (event, strtype, strtitle, strmessage) => {
    const { dialog } = require('electron');
    dialog.showMessageBoxSync(

            // win, // ここmain-processで生成したwindowを指定できる
            {
                type: strtype,
                buttons: ['OK'],
                title: strtitle,
                message: strmessage,
            });
});
```

 

TODO: 確認ダイアログを出してOK,NGを同期で返す。

(現行の互換動作。あるいはこれは諦めてもいい。今は不要なので)

TODO: ファイル読み出し結果を戻す

 TODO: ところでcontextIsolationが有効な時に nodeIntegration: trueが 無効になってない?

 

 

右クリックメニュー(ContextMenu)カスタム処理をmain-processへ移動した(electron 12対応)

右クリックメニュー(ContextMenu)カスタム処理をmain-processへ移動した。

electron 12へ移行するための対応の一部。

(移行作業としてはelectron11で移行作業をしてから12にアップデートするつもり)。


``` エラーメッセージ

 Uncaught ReferenceError: Menu is not defined
```


 



 

Menuはmein-processでいじるものになっていた模様。

ApplicationMenuカスタム処理をmain-processへ移動した(electron 12対応)

を参照 。

 

main-processのindex.jsの先頭付近に移動した。

```

  7 require(path.join(__dirname, 'main-process/application-menu.js'))¬
  8 require(path.join(__dirname, 'main-process/context-menu.js'))

```

 electron-api-demosでは`  Menu.setApplicationMenu()`はコールバックに仕込んでいたが、コンテキストメニューではしていないので、踏襲してそのまま呼び出しで済ませることにした。

```

 10 const {Menu, MenuItem, app, ipcMain} = require('electron')¬
 11 let menu = new Menu();¬
 12 ¬
 13 menu.append(new MenuItem({¬
 14 >-------label: 'Copy',¬
 15 >-------accelerator: 'CmdOrCtrl+C',¬
 16 >-------role: 'copy'¬
 17 }));¬

 ```

 

またコンテキストメニュー表示のコールバックも、 renderer-processでしていたのをelectron-api-demosの通りにmain-processへ移した。

rendererとmain-processでイベント が違う。

Before:

``` (renderer-process)

 11 >------->-------window.addEventListener('contextmenu', function (e) {¬
 12 >------->------->-------e.preventDefault();¬
 13 >------->------->-------menu.popup(remote.getCurrentWindow());¬
 14 >------->-------}, false);¬

```

After: 'main-process/application-menu.js'(main-process)
 ```

app.on('browser-window-created', (event, win) => {
  win.webContents.on('context-menu', (e, params) => {
    menu.popup(win, params.x, params.y)
  })
})

ipcMain.on('show-context-menu', (event) => {
  const win = BrowserWindow.fromWebContents(event.sender)
  menu.popup(win)
})

```

 

 

 

ApplicationMenuのときと同じく、Rendererに持たせたmoduleを呼び出す機能は動作しない。

 

そちらは後でpreload.js, ipcRendererなどを使う方式へ移行する。

ApplicationMenuカスタム処理をmain-processへ移動した(electron 12対応)

ApplicationMenu(画面上部のメニューバー)カスタム処理をmain-processへ移動した。

electron 12へ移行するための対応の一部。

(移行作業としてはelectron11で移行作業をしてから12にアップデートするつもり)。


``` エラーメッセージ

 Uncaught ReferenceError: require is not defined
    at menu.js:3

```

```menu.js:3

const remote = require('electron').remote;

```

 


 

だが根本的にはMenuはmein-processでいじるものになっていた模様。

electron-api-demos ではそうしている。

```electron-api-demos main.js

function loadDemos () {
  const files = glob.sync(path.join(__dirname, 'main-process/**/*.js'))
  files.forEach((file) => { require(file) })
}

``` 

ただしこの時点でelectron-api-demosはelectron9を使っていた。

(いろいろ言いたいことはあるが略)


Before:

``` index.html

<script type="text/javascript" src="./js/menu.js"></script>

```

After:

```

 require(path.join(__dirname, 'js/menu.js'))

```

これだけでもMenuカスタムが動作するようになってしまう。

ようは`  Menu.setApplicationMenu()`を呼んでしまえばOKのようだが、electron-api-demosではコールバックに仕込むことをオススメしている模様。

```

app.on('ready', () => {
  const menu = Menu.buildFromTemplate(template) // テンプレートを変数に格納
  Menu.setApplicationMenu(menu)
})

```


これをすることで、Menuカスタムは動作するし、Quit(App.quit()呼び出し)などは動作するが、Rendererに持たせたmoduleを呼び出す機能は動作しない。

 

そちらは後でpreload.js, ipcRendererなどを使う方式へ移行するとして、とりあえず次は右クリックのContextMenu。

electronの右クリックメニューから選択テキストを使う

lina_dictoにて、選択テキストを右クリックメニューでGoogle検索とGoogle翻訳にかける機能を実装しました。



右クリックメニューはcontextmenuで設定できます。

プロセス間通信がんばってねという記述もありますが、必要ありません。

window.getSelection();の戻り値で取れます。
返り値はオブジェクトなのでtoString()を忘れないよう注意。

Google検索のURLは
`'https://www.google.com/search?q=' + keyword.replace(/\s+/, '+');`
といった感じで生成。

Google翻訳のURL生成
```
    static get_google_translate_url(keyword, src_lang, dst_lang)
    {
        // ex. eo to ja, `https://translate.google.co.jp/#eo/ja/bona`
        const link = 'https://translate.google.co.jp'
            + '/#' + src_lang
            + '/' + dst_lang
            + '/' + encodeURIComponent(keyword);
        return link;
    }

```

ブラウザはOpenにURLを渡すとデフォルトブラウザが開きます。
`require('electron').shell.openExternal(link)`



結果、lina_dictoで使っている右クリックメニューのコードは以下。

```
    static init()
    {

        //! context menu (light click menu)
        var menu = new Menu();

        menu.append(new MenuItem({
            label: 'Copy',
            accelerator: 'CmdOrCtrl+C',
            role: 'copy'
        }));
        menu.append(new MenuItem({
            label: 'Paste',
            accelerator: 'CmdOrCtrl+V',
            role: 'paste'
        }));
        menu.append(new MenuItem({
            label: 'goto google search(external browser)',
            click: function(){
                const keyword = window.getSelection().toString();
                if(0 == keyword){
                    alert("text no selected");
                    return;
                }
                const link = 'https://www.google.com/search?q=' + keyword.replace(/\s+/, '+');
                //const src_lang =;
                //const dst_lang = '';
                //const link = ExternalBrowser.get_google_translate_url(
                //        keyword, src_lang, dst_lang);
                ExternalBrowser.open(link);
            }
        }));
        menu.append(new MenuItem({
            label: 'goto google translate(external browser)',
            click: function(){
                const keyword = window.getSelection().toString();
                if(0 == keyword){
                    alert("text no selected");
                    return;
                }
                const isEsp = Esperanto.is_esperanto_string(keyword);
                const src_lang = (isEsp ? Language.get_code_by_google() : 'ja');
                const dst_lang = ((! isEsp) ? Language.get_code_by_google() : 'ja');
                const link = ExternalBrowser.get_google_translate_url(
                        keyword, src_lang, dst_lang);
                ExternalBrowser.open(link);
            }
        }));

        window.addEventListener('contextmenu', function (e) {
            e.preventDefault();
            menu.popup(remote.getCurrentWindow());
        }, false);

        return true;
    }

```

eletctronでコマンドライン引数を受け取る (とmacosxの注意点)

eletctronでコマンドライン引数を受け取る場合に、macosx固有の注意点として"-psn_0_xxxxxx"のような引数に対処する必要がある。

daisy-sequenceをデバッグしていて発見。



# よく紹介されるelectronでの引数の受け取り方法

electronで引数にファイルを受け取る時、以下の対応が必要となる。

## electronはデバッグとリリースでコマンドライン引数の数が異なる

デバッグではElectron実行ファイルのパスだけでなく、アプリのmainを入れたjsのパスも引数として渡される。
```
let argv = remote.process.argv;

     if(argv[0].endsWith('lectron')){
        argv.shift();
    }

```
ここで'lectron'とendsWithでマッチしているのは、プラットフォームにより'Electron'/'electron'で先頭大文字小文字の2パターンがあるため。

## electronは OSXでは'open-file' イベントを受ける


electronアプリにGUI上からファイルを渡して開く時、(ファイルをドロップして開く等)macosxではwin/linuxのように通常引数では受け取れないため、以下のコールバックで受け取る。

``` : electronベース(nodeプロセス側)でコールバック受け取り
// MacOSX
global.sharedObject = {'osx_open_file': null};
let openFileHandler = function(event, path) {
    event.preventDefault();
    global.sharedObject.osx_open_file = path;
};
app.on('open-file', openFileHandler);

```
```: electronアプリ(レンダラプロセス側)で上で保存した値を受け取り
     if(typeof remote.getGlobal('sharedObject').osx_open_file === 'string'){
        if(null === arg.open_filepath){
            arg.open_filepath = remote.getGlobal('sharedObject').osx_open_file;
        }
    }

```

# macosxのelectronで引数から"-psn_0_xxxxxx"を取り除く

macosxでリリースしたアプリをダウンロードして起動すると、初回起動時に、
"-psn_0_xxxxxx"のような引数を受け取る場合がある。





これを適切に取り除く必要がある。
```
         if(/^-psn_0_[0-9]+$/.test(arg.open_filepath)){
            // macosxでブラウザからダウンロードしたバイナリを初回起動した際に、
            // 渡される場合のあるコマンドライン引数(ex. '-psn_0_2958034')を除外する。
            console.debug("through for macosx:", arg.open_filepath);
            return true;
        }

```

ただし、初回起動時から引数でさらにファイルパスを渡された場合に、うまく行かないかもしれない。(その場合の引数の順序などの動作は確認していない。)

不明な引数について今回の話に近い報告がされているissue
https://github.com/electron/electron/issues/4403

## テスト方法

なお、macosxでテストする際、「ネットワークからダウンロードしたファイル」のフラグをONにする必要がある。
これには実行ファイル(の配布zip)をchrome にドロップすると良い。ドロップしたファイルがダウンロード扱いされる。

CSSでグラデーションの上にPNG画像を一緒に使う

ページ背景で、透過png画像とbackground / linar-gradientを同時に使うトリック。

electron 8で使えたのでchrome 78で有効なはず。




```
 13 html{¬
 14 >-------background: linear-gradient(-30deg, rgba(190, 255, 190, 1), rgba(215, 255, 215, 1));¬
 15 }¬
 16 body{¬
 17 >-------background: url("image/sky.png") center center / cover no-repeat fixed¬

 18 }¬

```

htmlタグに背景色、bodyに透過PNG画像。
最初はグラデーションと画像でcssファイルを分けていたのが有効化のトリックかとも思ったが、そうではなかった。

注意:
image/sky.pngはcssファイルからの相対パスになっていた。
```
index.html
css + style.css
    + image/sky.png
```


stackoverflowのコメントによると画像とグラデーションの同時利用は回答時点では仕様策定中と書いてあるのだと思う。
https://stackoverflow.com/a/10881460

electron-packagerがフリーズした場合の対処

electron-packagerがフリーズした場合の対処。


```
> daisy_sequence@201812.2.0 pack:win /home/nuka/daisy_sequence/daisy_sequence
> npm run clean:win && electron-packager . --out=./release/win --platform=win32 --arch=x64 --icon='./image/icon.ico' --ignore='release' --ignore='object/' --ignore='obj/' --ignore='work/' --ignore='test/'


> daisy_sequence@201812.2.0 clean:win /home/nuka/daisy_sequence/daisy_sequence
> rm -rf ./release/win

```

とかで止まった時、npmキャッシュを除去すると解決する場合がある。
sudo rm -rf ~/.npm


```
> daisy_sequence@201812.2.0 pack:win /home/nuka/daisy_sequence/daisy_sequence
> npm run clean:win && electron-packager . --out=./release/win --platform=win32 --arch=x64 --icon='./image/icon.ico' --ignore='release' --ignore='object/' --ignore='obj/' --ignore='work/' --ignore='test/'


> daisy_sequence@201812.2.0 clean:win /home/nuka/daisy_sequence/daisy_sequence
> rm -rf ./release/win

Packaging app for platform win32 x64 using electron v6.0.12
Wrote new app to release/win/daisy_sequence-win32-x64
~/daisy_sequence
~/daisy_sequence/daisy_sequence/release/win/daisy_sequence-win32-x64 ~/daisy_sequence
  adding: LICENSE (deflated 41%)
  adding: LICENSES.chromium.html (deflated 83%)
  adding: README.md (deflated 36%)
  adding: chrome_100_percent.pak (deflated 8%)


```


また、各プラットフォーム向けelectonバイナリのダウンロードは進行していてプログレスバーが出ていないだけの場合もある。
(macosx catalinaにて発生)
/tmp下を見るとわかる。electron-*的な名前のファイルが存在してサイズが増えていくはず。
`ls -lh /tmp`


seleniumテストの失敗、デバッグと引数と解決

seleniumテストが失敗する問題のデバッグと解決
====

# TL;DR
関数名のtypoを修正すると、それまで通っていたテストが落ちる。
原因は、テストシステムが生成して渡した不正な引数による異常系終了だった。
対処として、テストケース実行を汚い実装で検出して特別に処理することで問題を回避することとした。


# 問題
## 詳細
electron製アプリのdaisy-sequenceに対して、selenium/mochaでウィンドウの起動テストを行っている。
これはほぼselenium公式サンプルと同じシンプルなテストである。

daisy-sequenceではコマンドライン引数を受け取る。
コマンドライン引数のパースは`js/cli.js` の `class Cli.parse(argv)`にて行っている。
この中で、`sprintf-js`パッケージの`sprintf()`関数をtypoしていた。
```js
        const sprintf = require('sprintf-js').sprintf;
(略)
        if(2 < argv.length){
            if(argv[2] !== '-o'){
                arg.err = {
                    'message': spirntf('invalid argment. `%s`', argv[2])
                };
                return arg;
            }

```

これを修正したところ、ウィンドウが即座に終了し、seleniumテストに失敗する。
```bash
make unit-test
make[1]: ディレクトリ '/home/nuka/daisy_sequence' に入ります
cd daisy_sequence && npm run test test/

> daisy_sequence@201812.2.0 test /home/nuka/daisy_sequence/daisy_sequence
> mocha --require intelli-espower-loader "test/"



  Array
    #indexOf()
      ✓ should return -1 when the value is not present

  Application launch
    1) shows an initial window
    2) "after each" hook for "shows an initial window"


  1 passing (3s)
  2 failing

  1) Application launch
       shows an initial window:
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:240:17

  2) Application launch
       "after each" hook for "shows an initial window":
     chrome not reachable
  Error: Request timed out after the element was still found on the page.
      at windowHandles() - application.js:246:17



^CTerminated
Makefile:20: recipe for target 'unit-test' failed
```

# デバッグ

前提として、typoを修正するとテストに失敗する。
別のtypoを入れる、alart()を挿入する等すると、テストに成功するようになる。 // alart()を入れるのは不正なのか?
 > これまでテスト失敗が起こらなかったのは、不正で止まることが原因だったらしい。
arg.errへの代入(arg.errはエラーフラグを兼ねる)を消すとテストに成功する。
 > arg.errへの代入がおかしい? それとも、parse()がエラーを返すと失敗する?
Cli.parse()エラー時の`process.exit(-1);`を消すと、テストに成功する。
 > parse()がエラーを返すとテストが失敗する!
ところでprocess.exit()するとウィンドウが即座に閉じる。ウィンドウが閉じればテストは失敗する。
つまり不正な引数を受け取っていることが原因の可能性がある。

引数を確認する。
console.error()系, process.write.stderr()等を使用しても出力されない。
// 上記デバッグの際もこれが作業の困難さを増した。
対処として、単にファイルに書き出すこととした。
`fs.writeFileSync("daisy-arg", JSON.stringify(remote.process.argv));`
結果、取得できたselenium/mocha実行時のコマンドライン引数。
```js
["/home/nuka/daisy_sequence/daisy_sequence/node_modules/electron/dist/electron","/home/nuka/daisy_sequence/daisy_sequence","--disable-background-networking","--disable-client-side-phishing-detection","--disable-default-apps","--disable-hang-monitor","--disable-popup-blocking","--disable-prompt-on-repost","--disable-sync","--disable-web-resources","--enable-automation","--enable-logging","--force-fieldtrials=SiteIsolationExtensions/Control","--ignore-certificate-errors","--load-extension=/tmp/.org.chromium.Chromium.oi97WO/internal","--log-level=0","--metrics-recording-only","--no-first-run","--password-store=basic","--remote-debugging-port=12106","--test-type=webdriver","--use-mock-keychain","--user-data-dir=/tmp/.org.chromium.Chromium.h85Kss","data:,"]
```
daisy-sequenceは、第二引数が存在する場合に`-o`でなければ、不正なコマンドライン引数として即座に失敗終了する。
(ただし、先頭がelectronで終了する場合これを引数に数えない。)

原因は不正な引数を受け取っていたことによる不正終了により、ウィンドウが即時終了していたことであると考えられる。

## 現象
大筋は以下。
typo修正前の動作はこのようになっていた。
- selenium/mocha実行はelectronに対してテストに適したコマンドライン引数を渡し、アプリケーションはそれを受け取る。
- アプリケーションはtypoにより「未知の関数を実行しようとした例外」で停止する。
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しない。
- seleniumはウィンドウが起動している・終了できたことに満足してテストをOK判定する。
以上のことからseleniumによるウィンドウ起動テストは成功していた。
なお、「不正な引数を受け取ったことによる不正終了」と「未知の関数を実行しようとした例外」はどちらもアプリケーションの不正終了を起こす。他のテストでは不正な引数をわたしたことによる不正終了が起こりその理由の区別はつかないので、テスト成功となっていた。

typoを修正したことで、以下のように変化した。
- selenium/mocha実行時のコマンドライン引数を受け取ることで、アプリケーションは不正な引数エラーで即座に不正終了する。
- seleniumはウィンドウが起動していないためテストをNG判定する。

// ところで「不正終了」という言葉の使い方が正しくない気がする。まあいいけど。

# 対処
コマンドライン引数からそれらしい引数を検出したらselenium実行と判定してコマンドライン処理をskipすることとした。
キー文字列は"--test-type="にした。

# 今後の課題
- selenium/mocha実行は途中でアプリケーション側に例外が発生しても検知(失敗判定)しないが、検出して失敗判定するようにしたい。
 daisy-sequenceは幸い、正常系で(起動時にwarningが一件出るのと、編集削除操作でエラーログを出力する以外に)例外が発生しない。
- アプリケーション側でパースする際にelectron向けコマンドライン引数を適切に排除する。

electronアプリnode cliアプリでのコマンドライン実行の種類とparseの共用について

必要なelectron,nodeのCLIについての周辺情報の収集・整理を行った。
(CLIというとユーザ入力のプロンプトを含むが、今回はプロンプトを含まないコマンドライン引数によるワンショット実行をCLIと呼びそれを扱う。)

# 要求仕様
daisy-sequenceおよびdaisy-cell-block-diagram(以下daisyシリーズ)は本物のシステム開発アプリケーションを志向している。
本物のシステム開発アプリケーションはCLI、CI連携可能なコマンドラインインタフェースを持つ。
本稿ではdaisyシリーズのCLIについて、以下の通り仕様と実装を検討し、必要なelectron,nodeのCLIについての周辺情報の収集・整理を行った。


## 画面仕様
usage: `app [input_filepath [-o output_filepath]]`
- guiとcli(コマンドライン)を提供する。コマンドラインインタフェースを共通とする。
    - GUI,CLIの区別は別バイナリ提供により実現する。
    - output指定の有無によるGUIとCLIそれぞれの挙動の違いについては未規定とする。
    - (GUIではoutput指定があると不正終了または書き出して終了、CLIではoutputが無ければ不正終了、を想定している。)
- helpとversionはあるに越したことはないが無くていい。
- win/mac/linux対応。
    - win,linuxのランチャに対応するため、GUIのオプション第一引数は渡されたファイルを開く。

## 機能仕様
- electronとnode(cli)で可能な限りコードを共有したい。せめて設定パラメタは共有したい。

# electorn, nodeのCLIの基礎
一般的なコマンドラインでは、先頭がアプリケーション実行ファイルの起動パスで後ろにコマンドライン引数が付く形で実行し、アプリに渡されるコマンドライン引数(argv)も同じである。
しかし、node, electronではそうでない場合がある。

### nodeから起動する場合と、npmパッケージのCLIコマンドとして実行する場合では引数の数が変わる
1. `node ./path/myapp.js myarg1 myarg2`
2. `npm run myapp -- myarg1 myarg2`
3. `node_modules/.bin/myapp`


`node`コマンドによる実行では、頭に`node`が付く。開発中などに用いる。
package.jsonコマンドによる`npm run`実行では、一般的なコマンドライン引数が渡される。
(`--`を付けることで以降の引数が`npm run`から実行アプリケーションに渡る。
 [npm run (npm run-script) コマンドで実行するスクリプトに引数を渡す。2017]( https://qiita.com/tiny-studio/items/ce28bf84c76aba53122f ) )
package.jsonのbin要素による実行ファイル実行では、一般的なコマンドライン引数が渡される。npm配布パッケージとしてインストールした後などに用いる。

```js
 10 >-------// コマンドライン引数を[1]からに調整(node実行の場合に必要)¬
 11 >-------let argv = process.argv;¬
 12 >-------if(argv[0].endsWith('node')){¬
 13 >------->-------argv.shift();¬
 14 >-------}¬
```

### electronで起動する場合と、ビルド後のアプリでは引数の数が変わる
1. `npm run myapp myarg1 myarg2`
2. `./myapp.exe myarg1 myarg2`


package.jsonコマンド起動では、頭に`electron .`と引数2個が付く。electronで起動するためである。開発中などに用いる。
ビルド後のアプリケーションでの実行は、一般的なコマンドライン引数が渡される。

```js
  63 >-------let argv = remote.process.argv;¬
  64 >-------if(argv[0].endsWith('electron') && argv[1] === '.'){¬
  65 >------->-------argv.shift(), argv.shift();¬
  66 >-------}else{¬
  67 >------->-------argv.shift();¬
  68 >-------}¬
```

### (備考) macosxの場合
本編ではないが。
macosxでファイルタイプに対するデフォルト起動アプリケーション登録によるファイルOpen(winのexplorerやLinuxのランチャ)は第一引数によらない。
`open-file`eventで受ける。念の為。
```js
app.on('open-file', openFileHandler);
```

### コマンドラインパーサの呼び出し位置
nodeのコマンドラインパーサパッケージを使う場合、そのうちのいくつかは、electronの'ready'イベントの前にMainProcessで使用する必要があると思われる。
基本的に、electronのRenderProcess側のわりと特殊な環境を想定していない模様。
具体的にはargsをremoteから取る部分、console.log()出力など。

[electronにおけるコマンドライン引数の位置とパーサ]( https://qiita.com/akameco/items/cfbde2f1b241e2babc6c )

command-line-args を使ってエラーで失敗していたのだが、RenderProcess側でやっていたのが問題であった可能性がある。

ただし、後述の通りglobalな変数を使用せずアプリケーションからargvを渡す形のインタフェースであればこれは問題とならないし、daisyシリーズではこれを採用する。


# 再度、要求仕様
- node,electronで共用するにあたり、node,electronおよび呼び出し方法によるCLIの違いを吸収できる必要がある。
  具体的には、これら先頭オプションの違いを取り除く前処理を行ったargvを渡せる必要がある。
  (例えば`argv`パッケージはargv引数を取らないので落選となる。)

minimistを用いるか、規模的に独自実装するのが適切と考える。


# vscodeの場合
vscodeではmain.jsでminimistをめちゃくちゃ単純なオプションで使用している。
後処理でもっと複雑なことをしているような気もするが、調査していない。

```js
/**
 * @returns {ParsedArgs}
 */
function parseCLIArgs() {
    const minimist = require('minimist');

    return minimist(process.argv, {
        string: [
            'user-data-dir',
            'locale',
            'js-flags',
            'max-memory'
        ]
    });
}
```

## 引き渡し
MainProcessからRenderProcessへの引き渡しは、ShareObjectを使えばよさそう。

main.js
```js
  8 global.sharedObject = {'osx_open_file': null};¬
  9 let openFileHandler = function(event, path) {¬
 10 >-------event.preventDefault();¬
 11 >-------global.sharedObject.osx_open_file = path;¬
 12 };¬
```

js/index.js
```js
  85 >-------if(typeof remote.getGlobal('sharedObject').osx_open_file === 'string'){¬
  86 >------->-------if(null === arg.open_filepath){¬
  87 >------->------->-------arg.open_filepath = remote.getGlobal('sharedObject').osx_open_file;¬
  88 >------->-------}¬
  89 >-------}¬
```

## 先頭ファイルパスの実現
minimistは本当にコマンドラインパーサをするだけ。help(usage)は作ってくれない。
よって、先頭ファイルパスを除いてそれより後ろのargvを渡せばよい。`argv.shift()`よりも`argv.slice(2)`がよくサンプルコードに書かれる。


[クロス本草稿]ロギング


本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGTK/Qt/Electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。



# ロギング

本物のデスクトップアプリケーションは、バグレポートに添付して原因を解析可能な、本物のデバッグログ(動作ログ)が出力されるべきである。
デバッグログは、アプリケーションの開発中・ユーザ環境での実運用中の両方で、常に出力され続けなければならない。
本章では、デスクトップアプリケーションからのデバッグログのファイル書き出しについて扱う。
開発中のデバッグ手法は基本的には開発環境の章で扱うが、本章で扱う動作ログも無関係ではない。



GTKにはデバッグログのための機構はない。
QtにはデバッグログのためのqDebug()関数群が用意されている。
Electronではconsole.log()関数群を用いる。
筆者は、Electronでは動作ログのファイル書き出しを行っていないため、それは扱わない。
本章は、GTK,Qt,Electron固有のテクニックと言うより、C,C++での文字列操作テクニックや、筆者の方針が多い内容になっている。

デスクトップアプリケーションでのロギングは下記が参考になる。
[Joel on Software やさしいバグトラッキング]( http://local.joelonsoftware.com/wiki/やさしいバグトラッキング )
[Joel on Software ユーザからクラッシュレポートを自動的に取得する方法]( https://www.amazon.co.jp/Joel-Software-Spolsky/dp/4274066304 )
ただ、筆者は上記に書いてあるようなクラッシュレポートの収集はまだ実施できていない。

マルチプラットフォームを考えると、syslog()が使えない。

筆者は、以下の通りにしている。
- デバッグログはファイルに書き出す
- 標準出力,標準エラー出力からリダイレクトしてテキストファイルに書き出すようにする場合もある
- 基本的に、ログにASCII文字以外は使わない
- 上記を前提に、マルチプラットフォーム(Windows)で日本語出力した場合に起こるかもしれない文字化けは基本的に最初から諦める
- フォーマッティングに難があるため、"+"演算子やstreamによる文字列連結を避ける
- 必要があれば独自実装の簡易なログローテーション(削除や圧縮)を行う
- 起動時にアプリケーションのversion情報をdumpする(手動でインクリメントされるリリース番号だけでなく、gitのコミットハッシュ等をバイナリに織り込んで用いる)

streamについて。
フォーマッティングの文法が長くコードの読み書きが難しいため、極力使用を避けている。

ログローテーションについて。
ログファイル名に起動時刻を使用し、アプリケーション起動時に一週間より古い名前の付いたログファイルを検出して、それらをzipするコマンドを叩く、という実装を行った。
なお、zip圧縮はアーカイブに後からファイルを追加していくことができる。
必要なければzip圧縮でなく単に削除するようにすればよい。
Windowsでは標準コマンドが機能不足であったため、[7-Zip コマンドラインバージョン(7za.exe)]( https://sevenzip.osdn.jp/download.html )をバイナリに添付して用いた。
Linuxでは標準のzipコマンドを呼び出せば用が足りる。
MacOSXでは行っていないが、たぶんLinuxと同じ方法で行ける。

アプリケーションのversion情報について。
過去にはビルド日時も入れていたが、これはビルド毎にバイナリハッシュが変わるだけで意味はないため、最近は行っていない。
gitコミットハッシュを付与する際、未commitな変更がないか確認して存在すればその旨("-develop"など)を付与するようにしている。
[vecterion/version.sh]( https://github.com/MichinariNukazawa/vecterion_vge/blob/master/version.sh )
一部抜粋
```bash
# repository
GIT_HASH=$(git log --pretty=format:'%h' -n 1)
GIT_BRANCH=$(git branch | awk '/^\*/ { printf $2 }')
GIT_DATETIME=$(git log --date=iso --pretty=format:"%ad" -n 1)
## @todo can't check to unstaging new file.
GIT_STATUS_SHORT=$(git diff --stat | tail -1)
```
が、gitで未コミットファイルの十分な判定が未解決の問題となっている。
(未ステージングな新しいファイルが存在する場合にそれを検出する良い方法が見つかっていない。)
また、この方法はQt(QtCreator)では、qmakeファイル(.pro)に組み込むことが難しい。
(QtCreatorから外部のMakefileを呼び出すようにしたら、Makefileを呼び出すだけでヌルビルドに4秒余分にかかるようになってしまった)

## GTK

GTKには、GTKライブラリ側のデバッグオプションがあるが、筆者はこれを使ったことがない。
[Running and debugging GTK+ Applications]( https://developer.gnome.org/gtk3/stable/gtk-running.html )

内容が完全にC言語マクロテクニックになってしまうが、筆者は下記の関数マクロを使用している。
[vecterion/include/pv_error.h]( https://github.com/MichinariNukazawa/vecterion_vge/blob/master/include/pv_error.h )
一部抜粋
```c
#define pv_error(fmt, ...)  \
    fprintf(stderr, "error: %s()[%d]: "fmt"\n", __func__, __LINE__, ## __VA_ARGS__)
```
呼び出すマクロでレベルを指定する。
コード上の関数名と行番号を自動付与する。
vecterionでは、関数名があればファイル名が無くてもログの出力箇所が一意に特定できるため、ファイル名は付けていなかった。
(pc_critical()でexit()を呼んでいないのは、筆者が修行不足であったが故。ここはサパっと死ぬべきだった。)

なお、gtkにはg_strdup_new()というユーティリティ関数がある。
これはprintフォーマットで生成した文字列を必要なメモリを確保して入れて返す。確保するので開放する必要もある。

日本語出力した場合どうなっていたか覚えていないが、多分コマンドプロンプトは文字化けする。

## Qt

QtにはqDebug()関数群がある。
呼び出す関数でレベルを指定する。
環境により出力先を切り分けてくれるようなのだが、常にトラップしてファイル出力していたため、筆者は詳細を知らない。
トラップは[qInstallMessageHandler()]( http://doc.qt.io/qt-5/qtglobal.html#qInstallMessageHandler )で行う。
[QDebug Class]( http://doc.qt.io/qt-5/qdebug.html )
[What's New in Qt 5.6.0: Logging to syslog and journald]( https://www.ics.com/blog/whats-new-qt-560-logging-syslog-and-journald )
ドキュメントに使用例でQRectをそのままストリームに渡しているところを見ると、QObjectを渡すだけで適切にデバッグ出力してくれるのかもしれない。

qDebug()はprintフォーマットでないため、16進数やゼロ埋めを使ったデバッグ出力が欲しい場合に苦労した。
(筆者はc++開発では一般にprintフォーマットが存在しないため苦労する。)
QStringにformatという似た機能があったため、なんとかやっていた。
下記使用例はうろ覚え。
```c++
qDebug(QString::format("%1,%2")
    .arg(hex_value, 16)
    .arg(space_fill_value, 3, 10, QChar(' ')))))
    // hex_valueは、続く第二引数が16進数表記を表す
    // space_fill_valueは、続く第二引数が桁数で第三引数が10進数表記を表す(!)、第四引数が埋めに使う文字
```

str.sprint()というメソッドがあり、筆者はこれも使っていた。
staticメンバではないため、QStringのオブジェクトを用意する必要がある。
下記使用例はうろ覚えだったため[stackoverflow]( https://stackoverflow.com/questions/4784155/how-to-format-a-qstring )より改変引用。
```c++
QString str;
qDebug(str.sprintf("%s %d", "string", 213));
```
2018年現在のQt5.11のドキュメントには無いため廃止された模様。

公式ドキュメントによると、古いversionのQtではqDebug()でprintフォーマットが使えた模様。
[Qt3.3 qDebug()]( http://doc.qt.io/archives/3.3/qapplication.html#qDebug )
2018年現在のQt5.11のドキュメントには無い使用法なので、廃止されたものと思われる。

アプリケーションversion番号のgit hasuについては、よく覚えていない。
確か最終的に.proファイルにコマンド出力をマクロ変数(?)に格納する記法があり、それを用いて「.proのマクロから、c++プリプロセッサマクロ置換」、で実装したような気がする。

Qtと関係ない、純粋なC++言語マクロテクニックの話になってしまうが、MicrosoftのMSDNマガジンで書かれたstd::stringをprintフォーマットに渡せるTempleteテクニックがあり、とても気に入っている。
[最新の C++ で Printf を使用する]( https://msdn.microsoft.com/ja-jp/magazine/dn913181.aspx )

日本語出力がどうなるかは覚えていないが、多分Qt(qDebug())側でハンドリングしているのではないかと思う。

## Electron

console.log()でする。
呼び出すメソッドでレベルを指定する。
デバッグコンソールでログレベルをverboseにしておかないとconsole.debug()が出力に表示されないので注意。

chromium由来のconsole.log()には、いくつかの拡張機能が用意されている。
(どうやら他ブラウザに無い場合があるようで、たぶん標準でない...といっても厳密にはECMAScript仕様にはconsole.log()関数は存在しないのではないかと思うが)
- オブジェクトだけを引数に入れるとコンソールでオブジェクトを展開できる
- 簡単なprintフォーマットが使える

lina_dictoではファイル出力等はしていない。
今後本格的なアプリを書いた場合の課題になっている、未解決問題。
たぶんnpmに選ぶのに困るほど多くのロギングパッケージがあるだろうと考えている。

[クロス本草稿]デプロイ







本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGTK/Qt/Electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。


# デプロイ

開発というコストの存在を忘れる開発者はいない。
が、空ウィンドウ表示までに環境構築という見えないハードルがあることはついわすれがちになる。
同じように、開発完了からアプリケーションをリリースするまでにも、デプロイという大きな見えないハードルが存在する。



gtk-rs(GTK/Rust)やPyQt(Qt/Python)を使用していないのは、デプロイ方法の十分な情報が期待できないことが大きい。
特に、実用的なアプリケーションは外部ライブラリを使う可能性が高く、アイコン画像などのその他のリソースを使う場合が多い。
それら必要な依存ファイルをインストーラに積載する方法が必要である。

筆者は、デプロイはバイナリと必要な依存ファイルをzipした配布パッケージを作成して行っている。
インストーラの作成はQtアプリケーションのWindows版でしかしたことがない。
また、MacOSXアプリケーションはGTK,Qtでの開発経験はなく、Electronでしかデプロイ・リリースをしたことがない。

MacOSX版でアプリケーション・アイコンを使う場合は別途アイコンの章を参照。

## GTK

GTKにはQtのような独自のインストーラ構築システムは無い。
GTKによる補助としては、インストーラ(配布パッケージ)には添付する必要のある設定ファイルがあり、その設定ファイルを構築するためのプログラムがGTKにより用意されている。

GTKアプリケーションのデプロイには、Windows版バイナリが公式から配布されていないという大きな問題がある。

GTKライブラリのビルドは、依存ライブラリ自体の数が多く、バージョン含む依存関係が複雑である。
集めるだけでも難しい。
GTKアプリケーションを作りたいだけのライト開発者(?)が挑戦すると挫折する危険性が高い。

GTK2の途中までは公式から配布されていた。
...のだが、2018年現在、確認したら`all-in-one bundle`のページがリンク切れしてる。
ただし直リンクからファイルはダウンロードできる。
GTK2ライブラリ公式配布[gtk+-bundle_2.24.10-20120208_win32.zip]( http://ftp.gnome.org/pub/gnome/binaries/win32/gtk+/2.24/gtk+-bundle_2.24.10-20120208_win32.zip )

筆者は、Windows版バイナリは[@niloufarjp 氏]( https://twitter.com/niloufarjp )の配布している野良ライブラリを使用している。

公式による[Download for Windows]( https://www.gtk.org/download/windows.php )
によると、他にmsysのpacmanにwindowsクロスビルドのバイナリがあるため、パッケージマネージャから取得するのが推奨の模様。
パッケージが展開された先からバイナリをコピーして集める方法でもWindows版が得られると思われる。

氏の野良ライブラリから配布パッケージを作るにあたって、
- 付属の設定ファイルを再構築する必要がある。
- フルパスになっている箇所を相対パスに置換する必要がある。
- ディレクトリ内のライブラリのバイナリから必要なものだけ抜き出す作業は基本的にトライアンドエラー。
と、わりと泥臭い。
`gschemas.compiled`,`loaders.cache`に対して、sedしたり、`bin\glib-compile-schemas.exe`,`gtk-update-icon-cache.exe`などを使ったり(詳細は忘れてしまった)。
アイコンキャッシュ周りだったと思うが、使い方の解説がなかったため、GTKのMLで質問したり、野良ライブラリを配布している[@niloufarjp 氏]( https://twitter.com/niloufarjp )に直接尋ねることになった。

Linux版は基本的にはリポジトリから取得すれば開発には足りる。
Ubuntuの場合、`sudo apt install libgtk-3-dev -y`

Windows:
GTK3アプリケーションのWindows版ビルドのデプロイは、圧縮ファイルにまとめるまではクロスコンパイラ等を用いることでWindowsを使用せずすべてLinux上で完結できる。
詳細は[vecterion_vge/deploy/packaging_win64.sh](  )を参考にするとよい。

Linux:
Linux版は作成しなかったのでやり方はわからない。
(Linux版は.debパッケージを作ろうとしていた記憶があったのだが勘違いだった模様。)

MacOSX:
MacOSX版は作成しなかったのでやり方はわからない。

## Qt

Qtには独自のインストーラ構築システムがある。
[Qt Installer Framework Manual]( http://doc.qt.io/qtinstallerframework/index.html )

筆者の経験では、これには、
- 依存ライブラリを収集するプログラム
- アプリケーションを含むディレクトリをインストーラにパッケージするプログラム
が用意されており、Windowsで使用してインストーラを構築したことがある。

Windows:
やり方は同人誌が出ている。
[理ろぐ インストーラを作ろう!]( https://booth.pm/ja/items/122098 )
日本語で貴重な情報が整理されているので必要になったら迷わず買うべき。
筆者は、この同人誌の前の版を購入した同僚が作ったインストーラ作成システムの、説明書を読みながらコマンドラインを叩いただけなので詳細は把握していない。
ただ当時のシステムは依存ライブラリを収集しきれていないようで、2つくらい手作業でコピーする必要があった。

Linux:
Windows版のQtアプリケーションではインストーラを作ったが、同じアプリケーションの、Linux版の配布パッケージをインストーラでない圧縮ファイルとして作成した。
当時のQt Installer FrameworkのLinux版がどうなっていたのかは記憶にない。筆者はlddを使って依存関係を見ながら、手作業でシステムから依存ライブラリ等を集めた。

MacOSX:
MacOSX版は作成しなかったのでやり方はわからない。

## Electron

Electron向けのパッケージングシステム、npmに4種類以上ある模様。
どれを使うのか悩むレベル。

公式ドキュメントでパッケージングを扱っているページ。
[Application Distribution]( https://electronjs.org/docs/tutorial/application-distribution )
[Application Packaging]( https://electronjs.org/docs/tutorial/application-packaging )

筆者は、
- Windows,MacOSXにelectron-packager
- Linuxにelectron-installer-debian
を使っているのだけれど、ドキュメントに記載のパッケージを見ていると[electron-builder]( https://github.com/electron-userland/electron-builder )良さそう。

Windows:
筆者は試していないが、Wineの存在もあってか、インストーラ作成まで作業をLinux上で完結できるらしい。

Linux:
debなどが作れる。
Linux以外の環境で作ったことはないけれど、Linuxのインストーラパッケージは大抵はただの圧縮フォルダなので、多分作れるのではないかと思う。

MacOSX:
きちんと案内していないパッケージシステムも多いけれど、どのパッケージシステムでも、MacOSX向けのインストーラはMacOSX以外では作成できない模様。
[electron-builder]( https://github.com/electron-userland/electron-builder/issues/3210 )もissueにそのような回答がある。
むしろ、MacOSXアプリケーションがLinux上でビルドできるのは衝撃的だった。
実際はダウンロードしてきたビルド済み実行ファイルからアプリケーション固有のファイルを読んでいるだけと思うが。

[クロス本草稿]未調査の項目


# 未調査の項目

本章は、クロスプラットホーム・デスクトップアプリケーションについて、わたしが使ったことのないテクノロジーについて書いている。
なので、他の章よりさらに現実乖離度が高いと思われる。
個人的な印象であることに留意しつつ、短く留めるよう努力する。


## その他のクロスプラットホームFW

クロスプラットホーム・アプリケーションFWはGTK/Qt/Electronだけではない。
ぱっと思いつくものとして、Java,C#,Unity,SPAがある。(これらがFWであるかはともかく)

### Java
JavaはGUIなデスクトップアプリケーションだけのものではないが。
joel氏が「宇宙人が見様見真似で作ったため味を付け忘れたUI、後に修正されたが」と語っていた奴。
<<joelonsoftware本へのリンク>>
Javaの機能は、クロスプラットホームGUIだけでない。

Javaの悪評について。
Javaが使われている環境ではプログラマの扱いが悪いという話が目につくし、Java自体も酷評されている印象がある。
特に恨みもないので擁護するようなことを書いておくことにする。
COBOLを置き換えて長期利用する安定したシステムにJavaが採用されるのは、エコシステムを含めたJava全体で見ればそのようなメリットがあるということだと思う。
Javaが持つ機能は、GC・VM・中間表現その他、当時から見たらどれも革新的な新テクノロジだったと思う。
Javaはそれらの新テクノロジを一度に商用システムに持ち込み一般化したすごいパイオニアだった。
そしてJavaは、商用化についてパイオニアになった。
同時に、パイオニアが犯すことを避けられないあらゆる失敗を抱え込んだのではないかと思う。
悪いことに、Javaは成功した本物の商用言語製品だった。後方互換性を捨てることはできないことは失敗を修正する機会がなかったことも意味する。
長生きすればするほど、Javaは10年前のよく知られた言語設計の失敗を今もそのまま抱えた言語として、悪評も固めていったのではないかと思う。

と、個人的にJava開発環境を悪く思ってはいないが、前述の通りプログラマには劣悪環境であるという話しか聞かない。
個人開発で採用する理由はないし、Java案件に関わらないで済ますためにも、Javaは使いたくない。

### C#
フォームデザイナとかあってすごく使いやすいJavaのようなものらしい。
Linuxでやるのが面倒くさそうなので採用したことはない。

### Unity
ゲーム用。
Qtと同じくモバイルアプリケーションを守備範囲に含む。
ユリアーモの文字割り当て変換(※)のWebサービスを作っていた時、競合がWebサービスをUnityでラップして、デスクトップアプリケーション版をリリースした。
[自家のWebサービス sisto_converter]( https://github.com/MichinariNukazawa/sisto_converter )
[競合のWebサービス EsperantoToJuliamoWithHTML5]( https://sugurunatsuno.github.io/EsperantoToJuliamoWithHTML5/ )
[競合のアプリケーション EsperantoToJuliamo]( https://twitter.com/fuyunosuguru/status/903929591122636800 )
モバイルやゲーム以外にも、Electronと同じ使い方で同じ範囲をカバーできるらしい。
Unityは言語として、C#とJavaScriptが使える。他にもあった気がする。

個人的には、Qtと同じくモバイルアプリケーションに傾注するであろうという予想とか、プロプライエタリであり無償版は起動時ロゴが出る、3D性能が必要なことはしない等の理由で、マルチプラットフォーム・デスクトップアプリケーションFW選択の際に真っ先に脱落する。


ユリアーモは「ことのはアムリラート」劇中に登場する架空の自然言語。
独自のアルファベットを用いる特徴があり、公式からフォントがリリースされている。
[『ことのはアムリラート』ユリアーモフォント配布 公式URL]( http://sukerasparo.com/sp004/font.html )
このフォントのアルファベットの割り当てが特殊であるため、一般に使われるラテン・アルファベットの割り当てを、公式の独自割り当てに変換するサービスが必要となる。

### Webアプリケーション(SPA等)
デスクトップアプリケーションから主役の座を奪い、現在はモバイルアプリケーションと覇権を争っている奴。
滅茶苦茶雑に言えば、ブラウザがあれば動くので、マルチプラットフォーム性が最強。
現実にはいろいろ面倒なところや弱点があり、決定打とはいえない。(このへん列挙は面倒だし、読者を含め私より詳しい人はいくらでもいるので書かない)

ぱっと作れてぱっと使う、家の外やモバイルで使う、軽いツールを作る場合はWebアプリとして実装することが多い。
前述の、ユリアーモの文字割り当て変換は軽いツールの例。

## 使用していないFWの機能

GTKは機能ヘルパ兼依存ライブラリの数が多く、Qtは分厚く範囲の広いクロスプラットホームレイヤであるためこちらも機能が多い。
Electronはわりと機能の少ない小さなアプリケーションを書いたつもりなので、最低限以上の機能は使わないようにしていた。
そのため、筆者が使用していない機能のほうがずっと多い。
そもそも網羅的な入門書を書くのではなく、本物のアプリケーションを書くためにGTK,Qt,Electronを使っているので、全機能を触っておく必要はない...。
(なので本書はこういう感じです。)

というわけで、使っていない機能の話はしておくべきかと思うので本章を書いた。

### Electron:自動アップデート・クラッシュレポート
githubのリリースページにインストーラを置いておくと勝手に認識して自動アップデートしてくれる、とかだと嬉しかったのだが、そういうわけでもない模様。
そもそもElectronを使って作ったlina_dictoはプラットフォーム毎にインストーラを作るのが面倒でzipでリリースしている。
現在はインストーラ形式での配布はしていないので、自動アップデートの前にインストーラ作成しなければ。

クラッシュレポートはJoel先生のブログ記事を読んでからぜひ実装したい機能で、Electronに搭載されていると聞いたときは狂喜乱舞したのだが、いかんせん作ったアプリケーションのユーザベースが少なすぎてコストに見合う気がしない...。

### Qt:QML
JSでアプリケーションを書くなら普通にブラウザアプリのほうがいいと思う...。

### Qt:Android/iOSのモバイルクロス開発
Androidアプリのリリース経験があるが、[lina_dicto for Android]( https://github.com/MichinariNukazawa/lina_dicto_for_android )はElectronで書いたlina_dictoをWebViewで移植して作ったので、QtのAndroid,iOSクロス開発機能は使用しなかった。
QtのAPIがカバーする範囲はよくわからないが、QtのAPIに無い機能については、プラットフォーム毎にネイティブコードを書くし、そういう書き方ができる、とのこと。
<取り消し線>Qtでモバイル開発していた周囲の人々は、iOS11対応で悲鳴を上げていた。</取り消し線>

### Qt:組み込み
死に体のデスクトップアプリケーションからQtが見つけ出した脱出先。
セットトップボックス等でHTMLが重いような場合を想定しているとのこと。
ターゲットによってはQt自体のクロスビルドから自前でする必要がある。
組み込みQtのサポートは販売ライセンスがデスクトップアプリケーション版と異なるという話が。

### GTK:マークアップ言語によるウィンドウ構造記述
QtはQtCreatorをフォームデザイナとしてポトペタするのが一番簡単であるためそれを使うことになるし、目diffを目patchする羽目にでも陥らない限りはQtのXMLを直接触る必要はない。
ElectronはHTMLを書くしかないので、マークアップを使う使わないという話にそもそもならない。
GTKの場合、QtCreator相当のフォームデザイナがクラッシュするような品質であり、マークアップを直接書くのはドキュメントが整備されていたとしても御免こうむるので、コードで直接UIを構築していた。
今はもう少しマシになっているといいなと思う。Windows版はバイナリ配布が無いかもしれないけれど。

### GTK/Qt:非ネイティブな言語バインディング
バインディング境界でバグが発生したら解決できなさそうだったので、生産性を犠牲にして、GTK,Qtではネイティブ言語なC言語,C++言語をそのまま使う選択をした。
また、バインディングはどうしてもネイティブ言語よりドキュメントの優先度が低く、ユーザが少ないこともあって、情報を集めることが難しい。
既にバインディングを使ってアプリケーションを書いてしまった場合や、公式が本当に一級市民扱いのサポートをしている場合以外には、おすすめしない。


[クロス本草稿]開発環境・IDE








本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
クロス本は、筆者のGtk/Qt/electronの経験と知見について扱います。具体的な解説書というより技術エッセイです。
本文章には、校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。

# 開発環境・IDE

現代的なプログラミング言語やフレームワークには、専用の開発環境が用意されていたり、開発を支援するエディタアドオンが利用されていることが多い。
前者はiOSのXcode、AndroidのAndroidStudio、PythonにはJupitorNotebookがある。後者はVSCodeのc++アドオンなど。
GtkにはAnjuta、QtにはQtCreatorというIDEが用意されており、electronには専用IDEのたぐいはない。

筆者は、Qt以外ではGUIアプリケーション以外でもほぼ全てのコーディングにVimを使っている。
またターミナルによるコマンド操作で開発作業を行うことが多い。

## Gtk

GtkのAnjutaは率直に言って使い物にならない。
Ubuntu14.x-16.xの頃、aptパッケージマネージャから導入したAnjutaが、最初のプロジェクト作成ウィザードを実行中にクラッシュしたことがある。
また、公式によるWindowsバイナリは用意されていない模様。

Anjuta自体は、PyGtk言語バインディングだけでなく、Gtkと関係ない開発も行える汎用IDEを目指しているようだ。
(Java、GnomeShellの拡張、等のプロジェクトが作成できる。eclipseのようなものか。)
Gtk向けの開発支援機能として、GUIによるフォームデザイナ(ポトペタ)機能が用意されている。

ドキュメントの章で書いていると思うが、Gtkユーザの中でもAnjuta使用者は少ないため利用するにあたって参考になる情報もあまりない。
フォームデザイナでWidgetをポトペタした後で、Widgetに求めるプロパティを設定するにはどこを編集すれば良いのかわからない。

2018年現在、今ならさすがに解決されているだろうと思ってUbuntu18.04でAnjutaの動作を確認してみたところ、一番最初のウィザードでgtkのライブラリが足りない旨のエラーが出た。
その後のビルドも `**Error**: You must have `libtool' installed.` などと出て失敗したので、色々と依存関係パッケージが足りていないようである。




デバッガも専用のものはないのでgdbを使用する。
メモリ範囲外アクセスのバグ等でクラッシュする場合など。
ビルド時に-gオプションを忘れないこと。

筆者はAnjutaは使わずに、開発はMakefileを書いてターミナルで実行し、コードはvimで書いている。
Windows向けはUbuntu上でmingwを用いてクロスビルドし、公式がGtk3ライブラリのWindows向けバイナリを提供していないため、非公式なビルドをダウンロードしてGtkアプリケーション開発をしている。
[Windows向けGtk3ライブラリ 非公式配布版]( https://drive.google.com/drive/folders/0BwkefA3JqxdIcGVuNkJfLVFyV2c )
[上記ライブラリを配布している @niloufarjp 氏のtwitter]( https://twitter.com/niloufarjp )
MacOXS版は作ったことがない。

## Qt

GtkのAnjutaが使い物にならないのに対して、QtのQtCreatorは実用的である。

Qt公式によりWindowsおよびLinux版(たしかMacOSXも)のバイナリが提供されている。
QtCreator以外に、VisualStudioでもWindows向けQtアプリケーションが開発可能であるらしい。
ありがちな話として、Ubuntuのパッケージマネージャでインストール可能なQtCreatorは大抵バージョンが古い。

QtCreatorにも汎用IDEを目指す部分はあるようなのだが、基本的には、QtおよびQML、その他のQtの言語バインディングに特化している。
もちろんWidgetのフォームデザイナ機能も用意されている。
GtkよりはWidgetのプロパティ設定も読みやすく設定しやすい。
ある程度ユーザも多いようで、Web上に参考になる情報もある。

QtCreatorのフォームデザイナは、QtCreator向けアドオンで拡張することで、独自定義Widgetを扱えるようになる。
ただし、筆者が以前試した限りでは、このアドオン導入は上手く行かなかった。原因を追求していないので、アドオン側が壊れていたか、単に筆者の手順が悪かっただけかもしれないが。

Ubuntu環境ではQtCreatorを使用してQtC++のプロジェクトをビルドする際に、依存するライブラリの不足で失敗することがあるため、追加パッケージとしてlibglをインストールする必要がある。
(Ubuntuバージョンによってパッケージ名が違うかもしれないので検索でlibgl*-devを探すとよい。)

Ubuntu17付近のIMEがfcitxを使用している環境では、QtCreatorに日本語入力するためにはfcitx-qt5アドオンをソースからビルドしてQtCreatorに導入する必要がある。

QtCreatorをVM上のUbuntuで使用する場合に、OpenGL周りの問題なのかWindowが白塗りとなって内容が表示されない場合がある。
その場合、QtCreatorをターミナルから起動し、起動時オプションでOpenGL周りのオプションを無効にすることで回避できる。

以前はビルドシステムはqmake、自動テストはQTestLibのみだったが、現在はビルドシステムにcmakeや自動テストにgoogletestが選択できるようになった模様。

gdbが組み込まれている。QVectorの範囲外アクセスによる例外などが発生した場合に重宝する。

筆者は公式からWindows向けおよびLinuxのQtCreatorをインストールしてQtアプリケーション開発をしている。
クロスビルドを構築するのは面倒なようなので、配布用バイナリは、Windows/Linuxそれぞれ環境構築してビルドしている。MacOSX版は作ったことがない。
Windows向けはVisualStudio版とMinGW版があり、MinGW版を使用していた。近年、VisualStudio版と統合された模様。

## electron

electronには専用IDEのたぐいは存在しないが、chromiumブラウザ由来の強力な実行時デバッガにより、Gtk/Qtとは段違いに高レベルが高いランタイムな開発支援が受けられる。

electronは専用のフォームデザイナは無い。
electronの画面は普通のHTMLなので、ポトペタがしたければ別途HTMLエディタ的なものを探せばよい。(使ったことがないのでどのようなものがあるかは不明)

だが、chromium由来の機能として、実行中に実行時のウィジェットツリー(DOM構造)と、Widgetプロパティを見ることができる。
DOMツリーに追加したelementが、思った箇所に追加されているか、また思い通りに表示されているかを実際の描画で確認することができる。
Windowサイズ変更した際の、Widgetサイズがどのように変化するかも確認できる。
また、CSSの値も確認することができる。
elementのCSSの値は、親elementからの継承、CSSクラス指定による上書き、style Attributeによる直接指定、jsによる書き換えによって複雑に上書き変化するため、思い通りに表示されない場合、人間が追いかけることは大変むずかしい。
思い通りの表示になっていなければ、動作中にDOMツリーやCSSを書き換えながら、思い通りの表示をする方法を探すことができる。
これらのフォームデザイン補助機能は、圧倒的に便利である。
CSSの章でも書くと思うが、electronの実行時GUIビジュアルデバッガを体験すると、GtkとQtのフォームのデバッガビリティの低さは論外であると理解できるし、特にGtkのCSS採用はあまりに愚かな判断ミスだったとしか思えなくなる。
(※このへんの内容は、もっと詳細にしてCSSの章に移動すると思う)

jsのデバッガが組み込まれている。
gdb,QtCreatorなどと同じく、ブレークポイントや関数呼び出しのトレースなどデバッグに必要な操作が行える。

Gtk,Qtとの違いとして、配布の際もchromiumの全機能が付いたままであるため、デバッガを切り離すことはできない。
ユーザからのデバッガへのアクセスを避けたい場合、デバッガの呼び出しショートカットを無効化すること等で対応する。



筆者はnodejsの最新安定版をnodejs公式からインストールして、そのnpmからelectronパッケージおよび他の依存関係パッケージをインストールしてelectronアプリケーション開発をしている。
開発時のtest等のコマンドはpackage.jsonに書くのが一般的なようなのだが、筆者はパッケージ作成まで含めたMakefileを書いている。
コードはhtml,css,jsまですべてvimで書いている。
開発はLinux上で行い、配布用のWindowsおよびMacOSX向けビルドはLinux上で作成できるためそのようにしている。

[クロス本草稿] アプリケーション・アイコン

クロス本 注意書き


本文章は、技術書典5にて[project daisy bell]( https://twitter.com/MNukazawa )がリリースする予定の、クロスプラットホーム・デスクトップアプリケーション・GUIフレームワーク・クロスレビュー本(仮)(以下、クロス本)の一部の草稿です。
校正前の文章・記憶により記述し未確認の内容・雑なmarkdown風紀法・張ってないURLリンク、などがあります。


# アイコン

アプリケーション・アイコンはデスクトップアプリケーションの顔である。
デスクトップアプリケーション固有のデザインテクニック(主に低解像度対応)があった。
モバイルアプリケーションに技術が引き継がれたかはわからない。
アプリケーション・アイコンはプラットフォームOSにより扱いが異なる。
クロスプラットホーム・アプリケーション・フレームワークは、それを吸収したりしていなかったりする。
というかGtk/Qtはともかく、electronだけはわりと吸収しようとしている。

## アイコンデザイン

アイコンの意匠をデザインする際、サイズ毎に固有のデザインをすることが推奨されている。
ドット絵のテクニックとして有名かもしれない。コーポレートデザインでも用途に合わせて微妙にバリエーション違いを用意するという似た技法がある。
ドット数が大きいアイコンではグラデーションなどを用いて意匠を凝らした美麗イラストを使う。
ドット数の少ないものほど要素を省き影や斜め線の少ないデザインにする。
[Microsoftの Windowsデスクトップ アプリケーション アイコンデザインのガイドライン]( https://msdn.microsoft.com/ja-jp/library/windows/desktop/aa511280.aspx )

MacOSが1024x1024pxの巨大アプリケーションアイコンを使用することが一時期有名だった。
現在では、Windows7-10デスクトップでも大きなアイコンが使用される。
Webページのために16x16pxのfaviconアイコンを用意する時代もあったが、モバイルブラウザでは表示されないため事実上不要となった。
2018年の現代において、小サイズアイコンを専用で用意する意味はほぼ無い。

daisy_bellでは、省力化のために、アイコンはサイズフリーなベクタ画像で1種類のみ作成している。
サイズフリーといってもターゲットサイズが無ければデザインの複雑さレベルの方針が立たないので、WindowsとMacOSXの間を取って、512x512pxサイズを基準にしている。

<<ここにlina_dictoのアイコン>>

lina_dictoでは、辞書アプリケーションでは典型的な開いた本の形をベースに、エスペラント語のテーマカラーから塗りを緑色、エスペラント語のシンボルである緑の星を反転した抜きとして使用した。
緑の星に、たぬきの尻尾が生えているのはもちろんすべての元凶たる「ことのはアムリラート」を意識しているため。
ところで、念の為調べてみたところたぬきの尻尾に縞模様があるとは限らないらしいのだが、まあ深く考えることなく縞模様にした。アライグマになってしまったかもしれない。
リリース時はよく出来たアイコンデザインだと自画自賛していたのだが、後にlina_dicto for englishのアイコンをデザインした際に、lina_dictoのアイコンは複雑過ぎたなと思い直して反省している。
サイズフリーデザインにするための工夫として、緑の星などの意匠は小サイズでは印象から消えて、ぱっと見では「緑色の辞書アイコン」に見えるように、サイズを調整している。

<<ここにlina_dicto for englishのアイコン>>
英語版はあくまでlina_dictoのオマケなので、アイコンデザインも検討の時間すら略する勢いで描いた。
青色は個人的な英語のテーマカラーであるため。端を丸めた線で開いた本の形を示して辞書アプリケーションのアイコンとした。
結果的に、「辞書アプリケーション=本の形」というステレオタイプは強力であったために、本家よりもシンプルさで見れば出来が良いアイコンデザインとなってしまった。
不本意な気持ちを感じなくもない。

<<ここにdaisy sequenceのアイコン>>
業務用を意図しているので黒色単色アイコンで大人っぽく(言い方が子供っぽい)。
シーケンス図のlifelineをテーマにしようと決めて、将来的なPlantUML対応を見据えていたのもあって植物っぽい意匠を取り入れようというところまでは考えたのだけれど、具体的にデザインに落とし込むのが大変だった。
一度は捨てようかと思った。
基本のデザインが決まってからも、重なり部分の抜きとか微妙に調整する必要があり、全体的に難産だった。

## アイコンデータの作成

Windows/MacOSXの場合、複数サイズの画像を1ファイルにまとめたアイコンファイルを用意する必要がある。

Windowsではicoファイルを使用する。
icoファイルはGimpで作成できる。
機械的にサイズ違いのレイヤを作ってicoファイル形式でエクスポートするだけなので、自動生成させたいところだったが、Gimpの自動化は自分にはハードルが高かったため手作業で行っている。
GimpにはPython/Schemeによるスクリプト機能はあるのだが、コマンドラインから叩けるかは不明。

Linuxにはアイコンフォーマットがそもそも存在しないので、画像サイズは1種のみで、普通のPNGとかjpgとかを使う。

MacOSXではiconファイルを使用する。
iconファイルはGimpでは作成できない。プロプライエタリなファイルフォーマットなのか、作成するにはMacOSXが必要である。
フリーな実装として<<実装名>>というコマンドラインツールがあるが、私の環境と用途では正しくアイコンファイルが作成されなかった。
(具体的にどううまく行かなかったのかは覚えていない。)
画像ファイルからiconファイルを作成するWebサービスがある。これは機能する。
[ Convert PNG to ICO and ICNS icons online - iConvert Icons ]( https://iconverticons.com/online/ )
以前一度、数日サービスが止まっていたことがあり、管理者へメールを書いたことがある。後日復活した。
このサービスで作成したアイコンは、Ubuntu18.04付近のNautilusファイルビューアのサムネイルでは砂嵐になり、正しく表示されない。だが正しく機能する。
コマンドラインツールで作成した方は、サムネイルでは正しく表示されていたように思う(が、記憶が定かでない)。

### Windows
Windowsにおいて実行ファイルにリソース情報として作者名などを付与することができる。
実行ファイルのアイコンは、リソースの一種である。
MinGWにWindowsターゲットのリソースエディタがあり、Linux上からでもWindows実行ファイルのリソースを操作できる。
(実行ファイル名: `x86_64-w64-mingw32-windres` あたり。)
それを使って実行ファイルにアイコンをリソースとして連結しているものと思われる。

### Linux
Linux実行形式のELFには、Windowsのようにディストリビューション汎用に使えるアイコンリソースの仕様はない模様。
debやrpmは、アイコンを独立した画像ファイルとして持っている。
インストーラによりインストールする際に、環境によりGnomeやKDEのアプリケーション・ランチャに登録するものと思われる。
インストーラがない場合は、ランチャからはアイコンのないアプリケーションとして扱われる。
(Unityデスクトップではデフォルトのアプリケーションアイコンに、デフォルトのアプリケーションアイコンが使用される。警告のような斜め線の入ったアイコンが使用される)
<<ここにUnityデフォルトのアイコン画像のスクショ>>
Ubuntu18.04でlina_dictoを動かした際に、前のバージョンのUbuntuでは問題なかったアプリケーションアイコンが突然認識されなくなり、デフォルトのアプリケーションアイコンになってしまった。
electronのインストーラ作成パッケージ<<の名前>>のバグに近い何かだろうと思ってissueを立てたところ、後日、アプリケーション側で起動時にアイコン画像を指定することが必要になったことが原因だという返答をもらった。
[electron-installer-debian
/application icon broken in ubuntu 18.04 alpha dash area #135]( https://github.com/unindented/electron-installer-debian/issues/135 )
アイコン画像のフォーマットは普通のPNGを使っている。icoファイルなどが使えるのかは必要なかったため確認していない。

### MacOSX
electronのdeployシステムによるMacOSXバイナリへのアイコン付与が、Linux上でも実行できていることから、MacOSXも実行ファイルのどこかにアイコン画像のデータを埋め込むとそれをOSが読んで表示してくれるものらしい。
(他に書くこととくになさそうなので以上)

## アイコンデータの実行ファイルとの結合

### electron
electronではdeplyシステムがWindows/MacOSXの実行ファイルにアイコンの結合を行う。
[パッケージzip作成 electron-packager]( https://github.com/electron-userland/electron-packager )
前述の通り、Linuxのみdeployとは別に、アプリケーション起動時に画像ファイルの指定が必要となる。
[deb作成 electron-installer-debian]( https://github.com/unindented/electron-installer-debian )

[Linux向けのアプリケーションアイコン登録コード例]( https://github.com/MichinariNukazawa/lina_dicto/blob/master/lina_dicto/index.js )
```js
 13 >-------// Create the browser window.¬
 14 >-------win = new BrowserWindow({¬
 15 >------->-------width: 800,¬
 16 >------->-------height: 600,¬
 17 >------->-------icon: 'image/icon.png'¬
 18 >-------})¬
```

### Gtk
アイコンリソース共通化レイヤのような高度なものはない。
ビルドシステムと同じで、Gtk側で独自なものは用意していないので、WindowsではMinGWに頼るなど、プラットフォーム毎になんとかする。

### Qt
Windowsのrc形式リソースファイルを書いてqmakeビルド設定ファイルに登録しておくことで、ビルド時に勝手に結合される。
Qtはアプリケーションのアイコンを扱うつもりはないらしく、この方法も、どちらかというとWindowsリソース情報のオマケ扱いに思える。
Linux、MacOSXではリソースファイルは単に無視される(とドキュメントに書いてあった気がするし、Linuxでは実際そうだった)。

[qmakeファイルでのRCファイル登録マクロの記述例]( http://d.hatena.ne.jp/QtCoder/20110910/1315838284 )
```
win32:RC_FILE = icon_win32.rc
```
[qmake RC_FILE 参照URL]( http://doc.qt.io/archives/qt-5.5/qmake-variable-reference.html#rc-file )

エスペラント辞書アプリ lina_dicto for androidをリリースしました

lina_dicto for androidをリリースしました。
エスペラントと日本語で、単語を翻訳できるAndroidアプリです。
AndoidのPlayストアからインストールできます。


エスペラント単語の入力補完機能があり、入力中の文字から単語を予測します。
また、スペルミスによるミスヒットに対して、正しい単語の候補を示します。

エスペラント文では、単語検索だけでなく文章検索にも対応しています。
文章を入ると、自動で単語分割して結果表示してくれます。
Web上のエスペラント文を読みたい場合などに役立つ機能です。
 
ソースコードを公開しています。githubはこちら

技術的な話は少し下の日記に書きました。
https://srad.jp/~m_nukazawa/journal/616311/

lina_dictoはそもそもPC向けのエスペラント辞書アプリケーション(Win/Mac/Linux対応)です。これの裏側がelectronだったため、そのままWebViewを使ってAndroidアプリケーションにできるなと思って作りました。


エスペラント辞書アプリをElectronで作って公開しました

エスペラント辞書アプリをElectronで作成し、リリースしました。
エスペラントto日本語の単語変換をします。とりあえずエスペラントの単語を引くことができます。
名前はlina_dictoです。
https://github.com/MichinariNukazawa/lina_dicto

なおエスペラント辞書データですが、すばらしい辞書データ が公開されており、これを使っています。
感謝。


ダウンロードはこちら。
https://github.com/MichinariNukazawa/lina_dicto/releases


本記事では辞書アプリ固有の話はしません。なぜなら今は2017年であり、辞書データをすべてメモリに置き、かつ動的言語を使って辞書データを頭から尻尾まで舐めるという超富豪コーディングをしても、ユーザ体験をまったく損ねず遅延もまったく体感しない辞書アプリケーションが作れるからです。IntelとJavaScriptエンジンのエンジニアには足を向けて寝られません。データサイズが小さかったのもあるかな

事前に検索インデクスくらいは作る必要があるだろうと想像していたので、正直、拍子抜けの気持ちが無いといえば嘘になります。

# エスペラント固有の話

先にエスペラント固有の話をします。固有の話は2つあります。

## 語根分かち書き

語根とは、語尾の変化による現在形への変換などをされる前の単語です。詳しくはWikipedia。
特にエスペラントは、語根を組み合わせて単語を作ることで、話者の記憶リソースへの負担を軽減する設計になっています。エスペラントは語根がはっきり定義されており、なぜなら人工言語であり、そういう設計だからなのですが、単語が複数の語根から成る場合がかなりあります。詳しくはWikって略すな。
今回使用した辞書データには、単語が語根分かち書き形式で収録されています。この分割部データもいずれは有効に使いたい所存ですが、検索時は、普通のユーザがタイプするであろう分かち書きされていない単語文字列と、分かち書き済み単語データをマッチングしなければなりません。
なので、分かち書きは単純に取り除きます。
上で、動的言語で文字列のマッチング・ループなんて...って書いてありますが、実際にはもっとひどいことをしています。具体的には、分かち書き状態の単語文字列を、正規表現置換によって分かち書き除去を行っています。
(さらに踏み込むなら、大文字/小文字マッチのためのlowarCase()等、CPUリソースを暴虐無人に使っています。)
せめて起動時の、辞書データファイル読み込み・オンメモリ辞書データ生成の際にキャッシュを作っておけよという感じですが、キャッシュ管理はコードが複雑化するので避けたいというプログラマの怠惰な方針を今のところ支持しています。だって実際に問題ない速度が出てしまっていますし...。

## 代用表記

エスペラントが使用する文字には、ASCIIアルファベット以外の文字が含まれています。
具体的には下記のやつです。
``` Ĉ ĉ Ĝ ĝ Ĥ ĥ Ĵ ĵ Ŝ ŝ Ŭ ŭ Ŭ ŭ ```
現代的ブラウザなら、UTF-8かつ対応フォントで上記文字列が問題なく表示されます。

代用表記には4種類くらいあり、ぶっちゃけ初学者には"^-sistemo"(typoではない)以外はピンと来ません。ついでに言うと、他の代用表記は実装が難しいのでやりたくありません。
特に元祖本家のH-sistemoの変換ルールが最悪です。これってつまり代用表記か否かの判定に辞書が必要じゃねえかと。エスペラントはunicodeどころかコンピュータが影もカタチも存在しない時代の設計だから仕方がないねというところです。

 で、^-sistemoの実装ですが、 下記のコード。
```
str = str.replace(/C\^/g,"\u0108");
```
こういうのが12個くらい、ハードコートされて並んでいます。組み込みエンジニアは卒倒して良いと思う。
もし複数のsistemoj(?)に対応することになったら、その時ついでに書き直します。

# electronアプリの話

エスペラントに興味があって読んでくださった方は、ここから先はelectronという退屈な実装技術の話しかしないのでこの下は読まなくてよいです。実装技術に対する初心者の泥臭いトライアンドエラーで脳内物質が出てくる系の方はこんにちわ。エスペラント富豪コーディングの話は楽しんでくれましたか。

なおインストールからサンプルアプリ動作までの前半戦はこちら。
https://srad.jp/~m_nukazawa/journal/614745/

## リリースパッケージディレクトリの展開先について

リリースに使っている設定JSONファイルに書いた書き出し先パスがひとつ上になっているだけでした。そして、実はもっと深刻な問題を持っています。
設定しないと、electron-releasesはリリースディレクトリを、プロジェクトのroot的なソースパスに展開します。
そして、このディレクトリに置かれたファイルは、基本的にすべてがリリースに含まれます。
なので何も考えずに
```
node_modules/.bin/electron-packager . lina_dicto --platform=all --arch=x64
```
とかやると、初回はともかく、2回目以降はすべてのプラットフォームのリリースディレクトリ(バイナリとelectron付属物)がすべてのプラットフォームのリリースに含まれるというマトリョーシカを作ってしまいます。(私は途中で圧縮が長すぎるのに気づいてkillした)


ここらへん、本当はjavascriptのトランスパイラとかMakefileオルタネイティヴスが呼び出して使う想定で単純な作りになっていて、直接使うことはあまり考えてないとかじゃないかなぁと妄想をたくましくする次第ですが、ともかくelectron初心者なので複雑なことはしたくない一心、jQueryすら使っていないこの単純プロジェクトにそんなものを入れたくはないので、成果物はとりあえずwin版だけzipできればいいやの精神で、vecterion_vgeの成果を転用したbashスクリプトをビルド&パッケージングスクリプトとして採用した次第です。

## MacOSXビルドについて

electronのバイナリ配布物、ようは『electron共通ブラウザコア(プラットフォーム固有) + Webページ的なもの(プラットフォーム非依存)』のセットでしかないので、インターネットから降ってきたプリコンパイル済みコアさえあればxcodeが無くともLinuxであってもOSXのバイナリ配布物が用意できるという理屈は納得できるのですが、それはそれとして起動するかどうかくらいは確認しないとリリースはしたくないので、動作検証してくださる方を募集しております。

## (ブラウザの)開発者モードON/OFFについて

一応、開発者モードをユーザがONにできてしまうことについては悩んではいます。 ユーザはOFFにできないからアプリを落としてもらうしか無いなー、という程度ですが。
オープンソースだから中身を見られてもOKということで、私はデバッグモードが開ける状態でリリースしています。リリース候補のデバッグも楽です。問題無ければデバッグモードを殺す手間無しに、そのままリリースできるからです。
デバッグビルド/リリースビルドで開発者モード有効フラグを切り替えると上記の手間が発生してしまいます。
隠れショートカットで開発者モードを出せる、という方法は、万が一にユーザが隠しショーカットを押してしまったらシッチャカメッチャカになるのが目に見えています。

## Windows版インストーラについて

本当はLinux上でWindows版インストーラが作りたいのですが、必須機能ではありませんし、できるかどうか調べることもせず、vecterionで使っているスクリプトを改造して配布用zipを作りました。

# まとめ

エスペラントについて。
すばらしい辞書データ を公開してくださっていることに感謝。

Electronについて。
Webで何かやったことのある人がPCアプリケーションを作る羽目に陥ったなら、electronは良いと思います。なにしろWebKitブラウザなので、ハマった時にWeb上にQAや対処法がたくさんあります。
少なくとも私は3日程度でクロスプラットフォームPC辞書アプリが構築できたので、electronすごく良かったです。


以上です。

ComfyUIでXYZ-PlotするワークフローのサンプルとTips(ComfyUI-XYZ-Stitch)

 ComfyUIでAutomatic1111的なXY-Plotをしたいのだが、ComfyUIデフォルト機能には用意されていない。     カスタムノードが必要となるのだが、Webには古いとか何故か動かないとかなXY-Plotパッケージの使ってみた系記事か、githubに上げられた...