機械人形の実験室

適当に、なんかやってみる

Electron の tray モジュールを使ってみる

今回は、Electronのtrayモジュールを使って、通知領域で遊んでみようと思います。 trayモジュールは、各プラットフォームの通知領域にアプリのコンテキストメニューなどを表示するモジュールです。

github.com

前準備

Electronアプリを動かすのに、必要なファイルをざっと作っていきます。

package.son

{
    "name":"tray-example",
    "version":"0.1.0",
    "main":"main.js"
}

main.js

var BrowserWindow = require('browser-window');

require('crash-reporter').start();

var mainWindow = null;

app.on('window-all-closed', function(){
    if(process.platform != 'darwin')
        app.quit();
});

app.on('ready', function(){
    mainWindow = new BrowserWindow({width:800, height:600});

    mainWindow.loadUrl('file://' + __dirname + '/index.html');

    mainWindow.on('closed', function(){
        mainWindow = null;
    });
});

ということで、テンプレ的に作成します。

trayモジュールを使う

アプリのコードは、script.jsに書いていきます。 trayモジュールを使いますので、必要なモジュールのロードをします。

var remote = require('remote');
var app = remote.require('app');
var Tray = remote.require('tray');
var Menu = remote.require('menu');

今回、menuモジュールを読み込んでいるのは、通知領域にコンテキストメニューを表示するためです。 Electronの公式のドキュメントに以下のように書かれています。

So if you want to keep exact same behaviors on all platforms, you should not rely on clicked event and always attach a context menu to the tray icon.

簡単に言うと、プラットフォーム間で同じ動作させるために、clickedイベントは使わず、常にコンテキストメニューをつけるように、とのことです。 プラットフォームを限定するのであれば、この説明は無視しちゃっても良いと思いますが、プラットフォームをWin/Macなど考えているのであれば、ドキュメント通りにした方が良いです。

trayを作成する

trayを作成するには、newでアイコン画像を指定して作成します。(今回使用したアイコン画像は、Flat Icon Designさんから FLAT ICON DESIGN -フラットアイコンデザイン- | フラットデザインに最適!WEBサイトやDTPですぐ使える商用利用可能なフラットアイコン素材がフリー(無料)ダウンロードできるサイト『FLAT ICON DESIGN』

var iconPath = __dirname + '/icons/s16_f_business_30_1bg.png';
appIcon = new Tray(iconPath.toString());

イコン画像は、Electronで使用できるNative-Imageとして指定します。(Native-Imageは、以下のURLで解説されています) Native-Imageは、画像形式として、JPEGPNGをサポートしています。

electron/native-image.md at master · atom/electron · GitHub

基本的には、画像ファイルのパスをStringで指定して、Native-Imageを作成します。 サンプルのコードでは、Fileオブジェクトで画像ファイルを指定して、trayを作成する段階で、Stringに変換して、Native-Imageを指定して、trayを作成しています。 ここで、指定した画像を使って、通知領域にアイコンが置かれます。 ちなみに、通知領域のアイコンを押した時の画像も指定できます。

  • setPressedImage(image)

上記のメソッドを使って、画像を指定することで、通知領域のアイコンを押した時の画像を指定できます。

trayにコンテキストメニューをつける

trayにコンテキストメニューをつけるには、以下のメソッドを使います。

  • setContextMenu(menu)

引数で指定したメニューがコンテキストメニューとしてつきます。 メニューの作り方などについては、electron/menu.md at master · atom/electron · GitHubを参考にすると良いかと思います。

trayをカスタマイズする

通知領域のアイコンにカーソルを合わせた時に出てくるツールチップは、以下のメソッドを使って設定します。

  • setToolTip(toolTip)

アイコンの隣に文字列を表示する(Macのみ)

Macのみの機能になりますが、通知領域のアイコンの隣に、文字列を表示することができます。

  • setTitle(title)

titleで指定した文字列をアイコンの隣に表示できます。

クリックした時にハイライトするかを設定する(Macのみ)

こちらも、Macのみの機能です。 通知領域のアイコンをクリックした際に、背景色をハイライト表示させるかどうかを指定します。

  • setHighlightMode(highlight)

highlightがtrueだと、ハイライト表示ONで、falseだとハイライト表示がOFFになります。

サンプルコード

ここまで説明した機能を使ったサンプルです。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"></meta>
    <title>Tray Example</title>
    <script type="text/javascript" src="script.js"></script>
</head>
<body>
<h1 id="header">Tray Example</h1>
    <ul>
        <li><a href="#" onclick="App.addTray()">Add Tray</a></li>
        <li><a href="#" onclick="App.removeTray()">Remove Tray</a></li>
        <li><a href="#" onclick="App.toggleHighlight()">Toggle highlight mode</a></li>
        <li><a href="#" onclick="App.setTitle()">Add Title</a></li>
        <li><a href="#" onclick="App.removeTitle()">Remove Title</a></li>
    </ul>
</body>
</html>

script.js

var remote = require('remote');
var app = remote.require('app');
var Tray = remote.require('tray');
var Menu = remote.require('menu');

var appIcon = null;
var currentHighlight = true;

var App = {
    addTray:function(){
        if(appIcon == null){
            var iconPath = __dirname + '/icons/s16_f_business_30_1bg.png';
            var pressedIconPath = __dirname + '/icons/s16_f_business_30_0bg.png';
            appIcon = new Tray(iconPath.toString());
            appIcon.setPressedImage(pressedIconPath.toString());
            appIcon.setHighlightMode(currentHighlight);
            var contextMenu = Menu.buildFromTemplate([
                { label:'Item1', type:'radio', checked:true },
                { label:'Item2', type:'radio' },
                { label:'Item3', type:'radio' },
                { label:'Item4', type:'radio' }
            ]);
            appIcon.setContextMenu(contextMenu);
        } else {
        }
    },
    removeTray:function(){
        if(appIcon != null){
            appIcon.destroy();
            appIcon = null;
        }
    },
    toggleHighlight:function(){
        if(appIcon != null){
            currentHighlight = !currentHighlight;
            appIcon.setHighlightMode(currentHighlight);
        }
    },
    setTitle:function(){
        if(appIcon != null){
            appIcon.setTitle('Tray Example');
        }
    },
    removeTitle:function(){
        if(appIcon != null){
            appIcon.setTitle('');
        }
    }
};

今回のコード置き場

今回作ったコードは以下の場所にあります。

github.com

Electron でDockを操作してみる

今回は、ElectronでDockを色々と操作してみます。 ということで、Macオンリーな操作です。

github.com

前準備

Electronアプリを動かすのに、必要なファイルをテンプレ的な感じですが、ざっと作ります。

package.son

{
    "name":"dialog-test",
    "version":"0.1.0",
    "main":"main.js"
}

main.js

var BrowserWindow = require('browser-window');

require('crash-reporter').start();

var mainWindow = null;

app.on('window-all-closed', function(){
    if(process.platform != 'darwin')
        app.quit();
});

app.on('ready', function(){
    mainWindow = new BrowserWindow({width:800, height:600});

    mainWindow.loadUrl('file://' + __dirname + '/index.html');

    mainWindow.on('closed', function(){
        mainWindow = null;
    });
});

Dockを操作する

Dockをいじるのには、appモジュールを使います。 appモジュールのAPIについては、以下のリンクに詳細があります。

electron/app.md at master · atom/electron · GitHub

appモジュールがDockで出来る操作は、以下の4種類です。

  • バウンス(アイコンが跳ねる動作)
  • バッジ(アイコンの右上にラベルを表示)
  • 表示・非表示
  • メニュー(今回は扱いません)

バウンス

バウンスは、以下の関数で操作します。

app.dock.bounce([type])
app.dock.cancelBounce(id)

bounceで指定するtypeは、文字列でcriticalinformationalを指定します。 指定を省略した場合は、informationalを指定した場合の動作になります。

  • critical - アプリがアクティブになるかバウンス要求がキャンセルされるまで、バウンスします。
  • informational - 1秒間バウンスします。バウンスの要求は、アプリがアクティブになるかバウンス要求がキャンセルされるまでは、要求されたままです。

bounceを呼び出した時のバウンス要求は、bounceの戻り値で返されたIDです。 バウンスをキャンセルする時は、cancelBounceに引数として、キャンセルしたいボウンス要求のIDを渡します。

バッジ

バッジを設定する時は、app.dock.setBadge()で設定します。 バッジに表示したい文字列をsetBadgeに渡します。 現在設定されているバッジを取得するには、app.dock.getBadge()を使います。

表示・非表示

Dockにアイコンを表示する場合は、app.dock.show()を呼び出します。 Dockのアイコンを非表示にする場合は、app.dock.hide()を呼び出します。

注意点?

バッジを設定後にDockのアイコンを非表示にし、再度表示した後に、バッジを設定してもバッジが表示されません。 ちょっと調べてみましたが、いまいち、これの対処方法がわからなかったです。 このような操作をする場合もそんなにないと思いますが。

サンプルコード

今回の実装としては、以下のような感じです。 index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"></meta>
    <title>Dock Example</title>
    <script type="text/javascript" src="script.js"></script>
</head>
<body>
<h1 id="header">Dock Example</h1>
    <ul>
        <li><a href="#" onclick="App.bounceForCritical()">Bounce (Critical)</a></li>
        <li><a href="#" onclick="App.bounceForInfo()">Bounce (Info)</a></li>
        <li><a href="#" onclick="App.setBadge()">Set Badge</a></li>
        <li><a href="#" onclick="App.clearBadge()">Clear Badge</a></li>
        <li><a href="#" onclick="App.showIcon()">Show</a></li>
        <li><a href="#" onclick="App.hideIcon()">Hide</a></li>
    </ul>
</body>
</html>

script.js

var remote = require('remote');
var app = remote.require('app');

var bounceID = undefined;

var App = {
    bounceForCritical:function(){
        if(bounceID != undefined) this.cancelBounce();
        setTimeout(function(){
            bounceID = app.dock.bounce('critical');
        }, 5000);
    },
    bounceForInfo:function(){
        if(bounceID != undefined) this.cancelBounce();
        setTimeout(function(){
            bounceID = app.dock.bounce('informational');
        }, 5000);
    },
    cancelBounce:function(){
        if(bounceID != undefined){
            app.dock.cancelBounce(bounceID);
            bounceID = undefined;
        }
    },
    setBadge:function(){
        app.dock.setBadge('Test');
    },
    clearBadge:function(){
        app.dock.setBadge('');
    },
    showIcon:function(){
        app.dock.show();
    },
    hideIcon:function(){
        app.dock.hide();
    }
};

サンプルコード実行の注意点としては、バウンス動作を確認する場合は、アプリを非アクティブにしないとバウンスしません。

今回のコード置き場

今回作ったコードは以下の場所にあります。

github.com

Electron の dialog モジュールを使ってみる

今回は、Electronのdialogモジュールを使って、いろいろとやってみます。 dialogモジュールは、各プラットフォームのネイティヴのダイアログを表示するためのモジュールです。

github.com

前準備

Electronアプリを動かすのに、必要なファイルをざっと作っていきます。

package.son

{
    "name":"dialog-test",
    "version":"0.1.0",
    "main":"main.js"
}

main.js

var BrowserWindow = require('browser-window');

require('crash-reporter').start();

var mainWindow = null;

app.on('window-all-closed', function(){
    if(process.platform != 'darwin')
        app.quit();
});

app.on('ready', function(){
    mainWindow = new BrowserWindow({width:800, height:600});

    mainWindow.loadUrl('file://' + __dirname + '/index.html');

    mainWindow.on('closed', function(){
        mainWindow = null;
    });
});

この辺りは、テンプレ的な感じですね。 ここまで作ったら、dialogモジュールを使うための本体を作っていきます。

dialogモジュールを使う

アプリのコードは、script.jsに書いていきます。 dialogモジュールを使いたいので、まずは、dialogモジュールをロードします。

var remote = require('remote');
var app = remote.require('app');
var BrowserWindow = remote.require('browser-window');
var dialog = remote.require('dialog');

appモジュールとbrowser-windowモジュールは読み込まなくてもいいですが、dialogモジュールを使ってダイアログを表示する時の引数指定に使うので読み込んでいます。

dialogモジュールで表示できるダイアログは、以下の4種類です。 (カッコ内は、表示するためのメソッドです) 詳細は、以下のリンクからAPI docをみてください。 electron/dialog.md at master · atom/electron · GitHub

  • 読み込みダイアログ (dialog.showOpenDialog([browserWindow], [options], [callback]))
  • 保存ダイアログ (dialog.showSaveDialog([browserWindow], [options], [callback]))
  • メッセージボックス (dialog.showMessageBox([browserWindow], options, [callback]))
  • エラーボックス (dialog.showErrorBox(title, content))

読み込み、セーブ、メッセージボックスの引数にあるbrowserWindowは、どのウィンドウに対してモーダル表示するか、というのを指定します。 未指定だった場合には、通常のウィンドウとして表示されます。

optionsで、ダイアログの挙動を制御します。

読み込みダイアログの場合は

  • title - 読み込みダイアログのタイトル
  • defaultPath- ダイアログを開いた時に、表示するパス
  • filters- 読み込むファイルの拡張子
  • properties- ダイアログの機能の制御

を指定します。

filtersは、nameextensionsを指定したJSONの配列として書いていきます。 たとえば、下のように記述します。

filters: [
    {name: 'Images', extensions: ['jpg', 'png', 'gif']}, 
    {name: 'Documents', extensions: ['txt', 'html']},
],

propertiesは、以下のものを指定した配列の形で書きます。

  • openFile - ファイルを開く
  • openDirectory - ディレクトリを開く
  • multiSelections - 複数選択できるようにする
  • createDirectory - ディレクトリーをダイアログ内で作れるようにする

複数のファイルを選択可能にする場合は、以下のように書きます。

filters: ['openFile', 'multiSelections']

保存ダイアログの場合に指定するoptionsは以下のものです。

  • title - 保存ダイアログのタイトル
  • defaultPath - ダイアログを開いた時に、表示するパス
  • filters - 読み込むファイルの拡張子

を指定します。 基本的に、指定する項目は、読み込みダイアログと同じです。

メッセージボックスで指定するoptionsは、以下の通りです。

  • type - メッセージボックスの種類 "none"、"info"、"warning"から指定します。
  • buttons - 表示するボタンのラベルの配列
  • title - メッセージボックスのタイトル
  • message - メッセージボックスに表示する本文
  • detail - 追加情報
  • icon

ダイアログの引数に指定するコールバックは

  • 読み込みダイアログ - 読み込んだファイルのパスの配列
  • 保存ダイアログ - 保存したファイルのパス
  • メッセージボックス - 押されたボタンの配列内のインデックス

を受け取ります。

ここで、実際に、dialogを使ってみましょう。 以下のコードで、テストをしてみます。 script.js

var app = remote.require('app');
var BrowserWindow = remote.require('browser-window');
var dialog = remote.require('dialog');

var showMsg = function(message){
    var options = {
        title: 'Message from callback',
        type: 'info',
        buttons: ['OK', 'Cancel'],
        message: 'Callback passs',
        detail: message
    };
    var win = BrowserWindow.getFocusedWindow();
    dialog.showMessageBox(win, options);
};

var App = {
    showSaveDialog: function(flag){
        var options = {
            title: 'Save Dialog Example',
            defaultPath: app.getPath('userDesktop'),
            filters: [
                {name: 'Images', extensions: ['jpg', 'png', 'gif']}, 
                {name: 'Documents', extensions: ['txt', 'html']},
            ]
        };
        if(flag){
            var win = BrowserWindow.getFocusedWindow();
            dialog.showSaveDialog(win, options, function(filename){
                showMsg(filename);
            });
        } else {
            dialog.showSaveDialog(options, function(filename){
                showMsg(filename);
            });
        }
    },
    showOpenDialog: function(flag){
        var options = {
            title: 'Open Dialog Example',
            defaultPath: app.getPath('userDesktop'),
            filters: [
                {name: 'Images', extensions: ['jpg', 'png', 'gif']}, 
                {name: 'Documents', extensions: ['txt', 'html']},
            ],
            properties: ['openFile', 'multiSelections', 'createDirectory']
        };
        if(flag){
            var win = BrowserWindow.getFocusedWindow();
            dialog.showOpenDialog(win, options, function(filenames){
                showMsg(filenames);
            });
        } else {
            dialog.showOpenDialog(options, function(filenames){
                showMsg(filenames);
            });
        }
    },
    showMessageBox: function(flag){
        var options = {
            title: 'Message Box Example',
            type: 'info',
            buttons: ['OK', 'Cancel', 'Info'],
            message: 'Message Box Example',
            detail: 'Extra info'
        };
        if(flag){
            var win = BrowserWindow.getFocusedWindow();
            dialog.showMessageBox(win, options, function(response){
                showMsg(response.toString());
            });
        } else {
            dialog.showMessageBox(options, function(response){
                showMsg(response.toString());
            });
        }
    },
    showErrorBox: function(){
        dialog.showErrorBox('Error Box Example', 'Error Box');
    }
};

実際に動かした画面が以下の通りです。 f:id:nhamada:20150524210049p:plain f:id:nhamada:20150524210032p:plain Macの場合は、browserWindowが指定されていると、シートで表示されるため、titleが表示されないので注意が必要ですね。

今回のコード置き場

今回作ったコードは以下の場所にあります。

github.com

Electron + jQuery UI を使ってみる

昨日の記事では、Electron + jQueryを使ってみたので、今回は、Electron + jQuery UIをやってみようと思います。 nhamada.hatenablog.com

前準備

前回のところで、ElectronでjQueryを扱う、というところまでやっているので、その続きから、となります。 なので、前準備としては、昨日の記事までを済ませておく、というとこから。 今回は、jQuery UIを使ってみようと思うので、まずは、jQuery UIをダウンロードしてきます。

jqueryui.com

適当に、jQuery UIをダウンロードしてきます。 今回は、テスト的に使いたいだけだったので、Quick Downloadsから、Stableを持ってきます。 ダウンロードしたファイルを適当な所に展開しておきます。

jQuery UIをElectronから使う

jQueryは、以下のコードで使えるので、それにいろいろと追加します。

index.html

<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8"></meta>
    <title>jQuery UI Test</title>
    <script type="text/javascript" src="script.js"></script>
    <link rel="stylesheet" href="js/jquery-ui-1.11.4/jquery-ui.min.css"></link>
    <script type="text/javascript" src="js/jquery-ui-1.11.4/jquery-ui.min.js"></script>
</head>
<body>
<h1 id="header">jQuery UI Test</h1>
<div id="accordion">
    <h3>Section 1</h3>
    <div>
        <p>
            なんか適当な文字列
        </p>
    </div>
    <h3>Section 2</h3>
    <div>
        <p>
            なんか適当な文字列
        </p>
    </div>
    <h3>Section 3</h3>
    <div>
        <p>
            なんか適当な文字列
        </p>
    </div>
</div> <!-- #accordion -->
</body>
</html>

主な追加は、ヘッダーの以下の箇所。

<link rel="stylesheet" href="js/jquery-ui-1.11.4/jquery-ui.min.css"></link>
<script type="text/javascript" src="js/jquery-ui-1.11.4/jquery-ui.min.js"></script>

ここら辺は、普段、jQuery UIを使う時と同じですね。

実際に、jQuery UIのWidgetsやらを使う時は、script.jsの方で、書いていきます。(実際は、index.htmlで書いても問題ないですが、気分的に)

script.js

var $ = jQuery = require("./js/jquery-2.1.4.min.js");

$(function(){
    $("#header").css("background-color", "#EEE");
    $("#accordion").accordion();
});

このような感じで、ElectronからjQuery UIを使ってみました。 実行画面としては、下のようになります。 f:id:nhamada:20150522231219p:plain

アコーディオンで指定したところが、無事、アコーディオンをして表示されていますね。 挙動も、とりあえず、問題なさげなので、これでjQuery UIをElectronから使えそうです。

今回のコードの置き場

GitHubの以下の場所に、今回のコードが置いてあります。

github.com

Electron + jQuery を使ってみる

Visual Studio Codeとか、最近、Electronを使ったアプリが多いなぁ、ということで、ちょっと遊んでみた。

最近、jQueryを勉強中なので、Electron + jQueryを使ってみようかと。

 

前準備

まずは、ElectronとjQueryをダウンロードしておきます。

Electronは、releaseの所から、最新版(記事作成時はv0.26.1)をダウンロードして、適当なとこに展開しておきます。

jQueryも、最新版(記事作成時はv.2.1.4)をダウンロードして、作成するパッケージ用のディレクトリに配置します。 

適当な所にElectronアプリ用のディレクトリを作成します。

今回は、jquerytestという名前でディレクトリを作成しました。

作成したディレクトリの中に、以下のファイルを作成します。

  • index.html
  • package.json
  • main.js
  • script.js

あと、jQuery本体を作成したディレクトリの中においておきます。

package.json、main.jsはElectronで実行するアプリの設定やら何やらを記述します。

とりあえず、この辺りは、Electron本家のチュートリアルの内容をそのまま持ってきます。

以下のような感じで、記述しておきます。

package.json

{
	"name":"jquery-test",
	"version":"0.1.0",
	"main":"main.js"
}

main.js

var app = require('app');
var BrowserWindow = require('browser-window');

require('crash-reporter').start();

var mainWindow = null;

app.on('window-all-closed', function(){
	if(process.platform != 'darwin')
		app.quit();
});

app.on('ready', function(){
	mainWindow = new BrowserWindow({width:800, height:600});

	mainWindow.loadUrl('file://' + __dirname + '/index.html');

	mainWindow.on('closed', function(){
		mainWindow = null;
	});
});

jQueryをElectronから使う 

ここまでで、アプリを作成する下準備はできたので、ここから作成していきます。

まず、Electronアプリで表示するindex.htmlを作成していきます。

と言っても、基本的に、今回試したいのは、適当な内容を表示するだけで良いので、以下のような感じで作ります。

<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8"></meta>
<title>jQuery Test</title>
<script type="text/javascript" src="script.js"></script>
</head>
<body>
<h1 id="header">jQuery Test</h1>
</body>
</html>

とても簡単な内容ですね。

script.jsの方には、jQueryを使うコードを記述します。

ここでは、簡単に、jQuery Testと表示してるところの背景色を変えるコードを記述します。

var $ = jQuery = require("./jquery-2.1.4.min.js");

$(function(){
	$("#header").css("background-color", "#EEE");
});

必要なこととしては、一行目の部分です。

var $ = jQuery = require("./jquery-2.1.4.min.js");

io.jsのModulesのAPIを使って、jQueryを読み込んで、jQuery$からアクセスできるようにします。(つまり、ブラウザ上で動作させる時と同じようにアクセスできるようにします)

このようにすると、以下のように、うまく動作していることが確認できます。

f:id:nhamada:20150521230629p:plain

jQuery Testの表示の背景色が変わっていますね。

ということで、このようにやれば、ElectronでjQueryが使えるようになります。

 

追記 (2015/05/22)

GitHubで今回の内容を公開しています。

github.com