スポンサーサイト
カテゴリ: スポンサー広告
上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。
編集 / --.--.-- / コメント: - / トラックバック: - / PageTop↑
活動報告 No.154 ElectronでD-WARS用のタイム・ポイントカウンタを作る
カテゴリ: 未分類

どうもおはこんばんにちは,もうすぐ無事3年になれる2年のマエダです.


最近は近藤バトルやロボジャパン,ロボワン,D-WARS8と色々ブログ記事になりそうなこといっぱいありましたが,大会報告に関しては他の人に執筆を頼んであるので,その方がアップするまでしばしお待ちを...(あれ,何週間前だろう?)


さて,今回も奇妙なタイトル発進ですが,要はオリジナルのタイムカウンターを作ってみた!てことです.


Electronて何

Electronを使うと,HTML・CSS・JavaScript・+αを使ったWEBサイト用のGUIアプリケーションを,macOS・Windows・Linux用デスクトップアプリケーションにパッケージングすることができます.


いやーデスクトップアプリケーションっていうとOSによってソースコード(というか言語そのもの)がかなり異なるので使用環境の多様性を考えると手をつけ難いもんでしたが,Electronを使えば大半のコードを共有して各OSに合わせたアプリが開発できちゃうんですねー.


1_wOcHbpZ25WbtWWsGI2b1Kw.png 

今回は超シンプルに,HTML・CSS・JavaScriptだけでタイム・ポイントカウンタを作っちゃいます!



諸注意

以下を前提としています.

・HTML,CSS,JS(JavaScript)に対する若干の文法とコードスタイル知識

・コマンドラインでの操作(パッケージインストール等)経験者



Electronをはじめるなら

この記事はコード紹介が主なので,作り方がイチから全部分かるってことはないです.それしたら何記事できてしまうのか...

私はドットインストールから始めました.大体この手の始めはドットインストールかプロゲートですね.


とりあえず手を動かして感覚掴んで,それから書籍をじっくり読み込むのが私の学習スタイルです.


ドットインストール

(・JavaScript入門:https://dotinstall.com/lessons/basic_javascript_v2)

・Electron入門:https://dotinstall.com/lessons/basic_electron


(あ,ステマじゃないですよ)



開発環境構築

※ macOSとUbuntu16.04LTSを想定


1,任意のディレクトリを作る

今回は「time_counter」というディレクトリ名でやっていきます.


2,Node.jsインストール

以下のサイトにアクセスして,Node.jsをインストールします.2つありますが,左側のLTS版を選ぶのが無難かと思われます.


https://nodejs.org/ja/


3,色々インストール

コマンドラインからtime_counterディレクトリまで移動します.

以下のコマンドを叩いていろんなヤツインストールします.


$ npm install electron1.6.1 --save-dev


これで同階層にnode_modulesディレクトリとpackage-lock.jsonファイルが生成されているかと思います.開発バージョンを柔軟にするために引数に「--save-dev」を付けてそのディレクトリのみにelectronを適用してあげます.



アプリつくろー

1,package.json

以下のコードを「package.json」として保存します.


{
"name": "time_counter",
"version": "0.1.0",
"description": "For D-WARS",
"main": "main.js",
"scripts": {
"start": "electron main.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "Shimizu_mizu",
"license": "MIT",
"devDependencies": {
"electron": "^1.6.1"
}
}


パラメータ

"name":アプリケーション名

"version":まぁ何でも良いですが,開発段階は0.X台でいいかなと.

"description":アプリの簡易説明

"main":詳細は後述しますが,アプリ起動時最初に読み込まれるJSファイルです.アプリ全体のエントリーポイント(?)

"scripts" > "start":コマンドからより素早くElectronを起動するために必要です.

"author":作成者名

"license":個人でつくるんで,普段使っているMITでいきます


ちなみに,コマンドラインで


$ npm init


と叩けば対話形式でpackage.jsonをつくれます.


2,main.js

Electron製アプリケーションを起動する際,一番最初に読み込まれるJSファイルです.


// main process
'use stript';

const electron = require('electron');
const app = electron.app;
const BrowserWindow = electron.BrowserWindow;
const Menu = electron.Menu;
const dialog = electron.dialog;

let mainWindow;

let menuTemplate = [{
label: 'D-WARS',
submenu: [
{ label: 'About', accelerator: 'CmdOrCtrl+Shift+A', click: function() { showAboutDialog(); } },
{ type: 'separator' },
{ label: 'Quit', accelerator: 'CmdOrCtrl+Q', click: function() { app.quit(); } }
]
}];
let menu = Menu.buildFromTemplate(menuTemplate);

function showAboutDialog() {
dialog.showMessageBox({
type: 'info',
buttons: ['OK'],
message: 'このアプリケーションについて',
detail: '作成日 : 2018年3月5日\n作成者 : 東京電機大学理工学部学術文化部会ヒューマノイド研究部所属 16RT126 前田泰希\nバージョン : 0.1.0\n説明 : 本アプリケーションは,ヒュー研主催の二足歩行ロボット競技大会「D-WARS」向けに作られたタイム・ポイントカウンターです.'
});
}

function createMainWindow() {
Menu.setApplicationMenu(menu);
mainWindow = new BrowserWindow({
width: 600,
height: 400
});
mainWindow.loadURL('file://' + __dirname + '/index.html');
mainWindow.webContents.openDevTools();
mainWindow.on('closed', function() {
mainWindow = null;
});
}

app.on('ready', function() {
createMainWindow();
});

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

app.on('activate', function() {
if (mainWindow === null) {
createMainWindow();
}
});


menuTemplate ではアプリ画面の左上によくあるメニューバーの表示名・ショートカット・クリック時の動作を記述しています.

{ type: 'separator' }, は区切りの横線です.

function showAboutDialog() では menuTemplate 'About'で表示する内容を記述しています.


mainWindow.loadURL('file://' + __dirname + '/index.html')でこのmain.jsとリンクするhtmlファイルを指定します.メインなので,WEBサーバで最初に読み込まれるindex.htmlという名前にするのがわかりやすくて良いと思います.__dirnameはこのmain.jsの同階層パスです.なので,このJSファイルを,例えば「time_counter/src/renderer/main.js」と「time_counter/index.html」というファイル構造なら,index.htmlはmain.jsから見て2階層上にあるので,記述すべきロードパスは'file://' + __dirname + '/../../index.html'となります.


mainWindow.webContents.openDevTools(); は開発時のみ使います.エラーや注意,デバッグが可能になります.アプリケーション配布時はこの行をコメントアウトするか,削除してからパッケージングします.


3,index.html

以下が中身です.


<!DOCTYPE html>
<html lang="ja">

<head>
<meta charset="UTF-8" />
<title>time_counter</title>

<link rel="stylesheet" type="text/css" href="./css/all.css">
<link rel="stylesheet" type="text/css" href="./css/index.css">
</head>

<body>
<h2 style="text-align: center;">タイムカウンタ</h2>
<ul id="access">
<li>
<a href="./contents/obstacle_race.html">> 障害物競争</a>
</li>
<li>
<a href="./contents/dice_battle.html">> さいころバトル</a>
</li>
<li>
<a href="./contents/robot_league.html">> ロボットリーグ</a>
</li>
<li>
<a href="./contents/battle.html">> バトル</a>
</li>
<li>
<a href="./contents/check.html">> 音量確認</a>
</li>
</ul>
</body>

</html>


複数競技があるので,トップでまず競技選択します.あとはHTMLが読めれば単純な話ですね.


4,カウンタコード(バトル用のみ)

タイム・ポイント両方をカウントしているバトルを例にとって解説です.


とりあえず「contents/check.html」と「js/all.js」を最初にお見せすると.


<!DOCTYPE html>
<html lang="ja">

<head>
<meta charset="UTF-8" />
<title>time_counter</title>

<link rel="stylesheet" type="text/css" href="../css/all.css">
<link rel="stylesheet" type="text/css" href="../css/battle.css">
</head>

<body>
<p id="displayArea">残り<span id="displayTimes"></span></p>
<p id="pointDisplayArea"><span id="displayBluePoints"></span> - <span id="displayRedPoints"></span></p>

<div class="buttonBlock">
<a href="../index.html" id="return">トップへ戻る</a>
<input type="button" value="初期設定" id="settingButton" onclick="setting(180, 0, 0);">
<input type="button" value="開始" id="startButton" class="timeButton" onclick="startCount();">
<input type="button" value="一時停止" id="stopButton" class="timeButton" onclick="stopCount();">
<input type="button" value="再開" id="restartButton" class="timeButton" onclick="restartCount();">
<input type="button" value="リセット" id="resetButton" class="timeButton" onclick="resetCount();">
</div>
<div class="buttonBlock">
<input type="button" value="blue-" id="dePointBlueButton" class="pointButton" onclick="addPoints('blue', -1);">
<input type="button" value="blue+" id="plusPointBlueButton" class="pointButton" onclick="addPoints('blue', 1);">
<input type="button" value="red+" id="plusPointRedButton" class="pointButton" onclick="addPoints('red', 1);">
<input type="button" value="red-" id="dePointRedButton" class="pointButton" onclick="addPoints('red', -1);">
</div>

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

</html>



'use stript';


var passSec;
var firstTime = 0;
var bluePoints = 0;
var redPoints = 0;

var startButton = document.getElementById("startButton");
var stopButton = document.getElementById("stopButton");
var restartButton = document.getElementById("restartButton");
var resetButton = document.getElementById("resetButton");

var audio = new Audio('../audio/Opening_Buzzer02-1.mp3');


function setting(sec, bNum, rNum) {
firstTime = sec;
var msg = firstTime + "";
displayTimes.innerHTML = msg; // 表示更新

bluePoints = bNum;
redPoints = rNum;
msg = bluePoints + "";
displayBluePoints.innerHTML = msg;
msg = redPoints + "";
displayRedPoints.innerHTML = msg;
}


function blockButton(state) {
startButton.disabled = state[0];
stopButton.disabled = state[1];
restartButton.disabled = state[2];
resetButton.disabled = state[3];
(state[0]) ? setOpacity(startButton, 0.2) : setOpacity(startButton, 1.0);
(state[1]) ? setOpacity(stopButton, 0.2) : setOpacity(stopButton, 1.0);
(state[2]) ? setOpacity(restartButton, 0.2) : setOpacity(restartButton, 1.0);
(state[3]) ? setOpacity(resetButton, 0.2) : setOpacity(resetButton, 1.0);
}


// 要素の不透明度を操作
function setOpacity(elem, op) {
// IE6.0, IE7.0
elem.style.filter = 'alpha(opacity=' + (op * 100) + ')';
// Firefox, Netscape
elem.style.MozOpacity = op;
// Chrome, Safari, Opera
elem.style.opacity = op;
}


// 開始ボタン以外無効化
var buttonState = [false, true, true, true];
blockButton(buttonState);


// カウントが最後まで行ったら終了ブザーを鳴らす
function endCount() {
passSec = 0;
var msg = passSec + "";
displayTimes.innerHTML = msg; // 表示更新
audio.play();
}


// 表示内容
function showPassage() {
passSec--; // カウントダウン
if (passSec <= 0) {
stopCount();
endCount();
}
var msg = passSec + "";
displayTimes.innerHTML = msg; // 表示更新
}


// 開始
function startCount() {
passSec = firstTime; // カウンタのリセット
PassageID = setInterval('showPassage()', 1000); // タイマーをセット(1000ms間隔)
buttonState = [true, false, true, false];
blockButton(buttonState);
}


// 一時停止
function stopCount() {
clearInterval(PassageID); // タイマーの停止
buttonState = [true, true, false, false];
blockButton(buttonState);
}


// リスタート
function restartCount() {
PassageID = setInterval('showPassage()', 1000); // タイマーをセット(1000ms間隔)
buttonState = [true, false, true, false];
blockButton(buttonState);
}


// リセット
function resetCount() {
passSec = firstTime;
var msg = passSec + "";
displayTimes.innerHTML = msg; // 表示更新
clearInterval(PassageID); // タイマーの停止
buttonState = [false, true, true, true];
blockButton(buttonState);
}


// 秒数の加減
function addSec(num) {
passSec += num;
var msg = passSec + "";
displayTimes.innerHTML = msg; // 表示更新
}


// 得点の加減
function addPoints(type, num) {
if (type === 'blue') bluePoints += num;
if (type === 'red') redPoints += num;
msg = bluePoints + "";
displayBluePoints.innerHTML = msg;
msg = redPoints + "";
displayRedPoints.innerHTML = msg;
}


HTML内で記載されているinputタグは,

<input type="button" value="表示文字" onclick="起動するJSの関数名(引数)">

のルールにしたがって記述しています.


「js/all.js」はHTMLのボタンで呼び出す関数をつらつら書いているだけです.JSの暗黙の型変換やコメント,関数を細かく記述しているので,JS読めると多分わかるかと..


タイムカウントが最後までいったらvar audio = new Audio('../audio/Opening_Buzzer02-1.mp3');が呼び出されます.ちなみにブザー音です.


ここまでくれば何となく分かると思いますが,各ファイルは以下の役割を果たしています.

package.json:Electronの設定

main.js:ElectronのためのJSファイル

index.html:メイン

battle.html:タイム・ポイントカウント画面

all.js:動作関数群


WEBサイトで同様のアプリケーションを公開するなら,最低限

index.html

battle.html

all.js

が必要です.package.jsonとmain.jsが消えているだけです.

逆に,もしもHTML・CSS・JSで作ったWEBアプリケーションがあるなら,それをElectronを使ってデスクトップアプリケーションをつくるためには,Electronをインストールしてpackage.jsonとmain.jsを付け加えるだけでソースコードは出来上がってしまうのです.



パッケージング

macOS用の手順としては,まずアプリケーションのディレクトリ全てをasarというツールでアーカイブにします.んで,そのasarアーカイブをelectron-packagerというツールでアプリケーションにします.


↓実際のディレクトリ

sk.png 


1,asar

この子はグローバルにインストールして良いかと.


$ npm install -g asar


アーカイブ化するには,以下のルールに従います.


$ asar pack {アプリのディレクトリ} {出力名}.asar


例:

$ asar pack ./time_counter t_count.asar


2,electron-packager

こいつもグローバルでインストール.


$ npm i electron-packager -g


macOS向けパッケージング例

$ electron-packager ./time_counter time_counter --platform=darwin --arch=x64 -- electronVersion=1.4.13

Windows向けパッケージング例

$ electron-packager ./time_counter time_counter --platform=win32 --arch=x64 -- electronVersion=1.4.13



結果

time_counter.gif 

GIFにするとちょいとせっかちですが,ちゃんと動いています.状況によって触れられるボタンとそうでないボタンに分かれるので,誤操作を防ぐことができます.



しめ

最初にいった通り,WEB系の知識だけでいろんなOSに対応したデスクトップアプリが作れちゃう現代なので,誰でもお手軽にフリーソフト的なものを配布することもできちゃいます.

昔は「こういうフリーソフトないかなー」て思って窓の杜に行って見たりしてきましたが,今は比較的簡単に配布者側にまわれるのは感慨深いです.

Electronの元はWEBアプリなので,セキュリティにも注意を払わなくてはなりません.脆弱性をついて任意のコードを実行される恐れがありますから.責任を伴う配布は誰だって厳しいかもですね..






スポンサーサイト
編集 / 2018.03.26 / コメント: 0 / トラックバック: 0 / PageTop↑
コメント
 
Title
 
 
 
 
 
 
Secret 


Pagetop↑
トラックバック
Pagetop↑
プロフィール

ヒュー研の中の人

Author:ヒュー研の人
このブログは東京電機大学理工学部ヒューマノイド研究部の公式ブログです。2012年から部に昇格しました!
その日の活動や大会の記録をできるだけ更新していきたいです!!

☆だいたい金曜日前後に更新します☆

FC2カウンター
カレンダー
06 | 2018/07 | 08
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31 - - - -
リンク
ブロとも申請フォーム
携帯でみるには↓
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。