キノコの自省録

テクノロジーとコンテンツの融合を目指して

シャウトアラームver2.1.0をリリース

シャウトアラームver2.1.0をリリースしました。

シャウトアラーム

シャウトアラーム

  • MASAAKI MAKINO
  • ユーティリティ
  • 無料

単体アラームのアラーム音が正常に設定できない不具合の修正と、アラーム音(3個)の追加です。

追加したアラーム音は、以下の通りです。

  • アラーム4: 電話の着信音(プルルルルル)
  • アラーム5: 警告メッセージ感のある音(チャチャチャチャ)
  • アラーム6: ちょっと元気系な女の子の声で「おはようございます!」

一応、海外にもローカライズしているのに「おはようございます」はどうなんだという気もしないでもないですが。個人的にはアラーム6を結構使っています。くどくなり過ぎないよう、発声の間隔は、ちょっと広めにしています。

シャウトアラームver2.0.2が大阪ほんわかテレビにて紹介されました+アプリ更新について

シャウトアラームver2.0.2を、10月27日放送の大阪ほんわかテレビにて紹介していただきました。

大阪ほんわかテレビ|放送内容|読売テレビ

シャウトアラームを App Store で

残念ながら私は関東民のため、読売テレビは見ることができませんでしたが……

なお、シャウトアラームver2.0.1 → ver2.0.2では、スヌーズを声で止めたのに、解除できずに鳴り続けるバグを修正しました。割と致命的なバグなので、ver2.0.1をご使用の方は、ver2.0.2をご利用ください。

デザイン比率計算機 ver2.1.0

デザイン比率計算機も更新しました。iOS11向けのビルドと、規格表にApple製品の縦横サイズを記載しています。以前のApple製品は4:3ばかりでしたが、最近はもう解像度も比率もバリエーションが増え過ぎて、整理しないとよくわからなくなりました。

ウィッチクライド

レビューを拝見しました。実は、封印のメッセージがバグっているのは気づいていましたが、ちょっと放置していました。良くないですね。更新します。iPadのアイコンについては、以前も友人より指摘があったのですが、自分の環境では再現せず、まあいいや大丈夫なんだろうと思ってリリースしました。やっぱり駄目なようですね。アセットを最新にして再確認しようと思います。

機能的な更新も入れたいところですが、時間との相談という感じです。

黒猫の単語帳ver2.5.0

やはり動いていません。そのうち公開停止します。

全国バス経路マップ

iOS11対応予定はありません。残念ながら、更新するだけの余裕が自分にありません。このまま自然公開終了予定です。

Seraph Flight

iOS11向けに更新するかもしれません。実際のところあまり考えていませんが、取り下げる理由もないので。

黒猫の単語帳2nd公開停止のお知らせ

黒猫の単語帳2ndですが、現在動作していません。辞書提供元のiKnowAPIの公開を停止している模様です。元々、iKnow APIは5年以上前に公開停止されていましたが、「保証はしないものの、動いているなら利用しても構わない」とのことで、そのまま黒猫の単語帳も公開を継続していました。

今現在、APIが完全に止まったわけではなく、JSONでエラーが返ってきています。そのため、もしかすると復帰する可能性も0ではないですが、限りなく低いと思われます。

近日中にVectorなどからも公開取り消しを行います。

代替辞書

代替辞書も検討したいところですが、iKnowレベルを求めると、無料では存在しないと言って良いところです。これはiKnow API公開停止通告があってから探してはいましたが、未だに状況は変わっていません。

iKnowに直接掛け合うことも策の1つですが、無料は流石に無理でしょう。月額いくらになると思いますが、黒猫の単語帳を有料化しない限り公開は難しいと思います。

Google TranslateやBing Translateも有料で、さらにアカウント辺りいくらの利用になるため、自分でサーバを立てて一元管理しなければなりません。サーバ代とAPI利用料がかかってくるので、これまた頭の痛いところです。

随分前にどこかで書きましたが、無料のWebAPIを利用するというのは、切られても文句の言えない弱い立場に置かれてしまいます。利益を出す仕組みを考えるというのは、安定的なサービスを提供する上で非常に大事なことで、お金が二の次になりがちなエンジニアであっても、そのことをよく念頭に置かなければならないと、ここ数年、特に強く思っています。

RaspberryPi ZeroでGPIOを使って音声出力するお話

RaspberryPi Zero(W)は3.5φジャックがないので、音声出力はHDMIかGPIOを利用して頑張るしかありません。GPIOを使って音声出力するお話は、既にいくつかの方々がまとめられています。

Raspberry Pi Zero のGPIOを利用した音声出力 – Roguer

Pi Zero PWM Audio | Adding Basic Audio Ouput to Raspberry Pi Zero | Adafruit Learning System

が、外部ソースについて説明がなかったりリンク切れしてたりしたので、ちょっと補足しておきます。

WiringPi

drogon氏のプロジェクトの1つです。Adafruitのページではtar.gzをbootの下に入れようみたいな説明を書いていますが、普通にgit cloneしても構わないと思います。

Drogon Projects | Git

このページのトップに書いてある通り、git clone git://git.drogon.net/ + project name.なので、

git clone git://git.drogon.net/wiringPi

でcloneできます。tar.gzで欲しい場合はsnapshotをクリックするとダウンロードできます。

gpio_alt

konsuktaner氏のプロジェクトの1つです。githubに置いてあります。

GitHub - konsultaner/word-clock-raspberry-pi-zero-neopixels: This is a project to construct and build a word clock using a raspberry pi zero and the adafruid neopixels.

README.mdに書いてある通り、gccでビルドすると、gpio_altのバイナリが生成できます。WiringPiについても書いてあるので、実はここを紹介すれば終わりとも言えなくもない。

gccを利用するので、動かない場合はsudo apt-get install build-essentialを実行しましょう。

RaspberryPiでOLED液晶モジュールに出力する文字のフォントを変えたり日本語を出したり

この続き。

RaspberryPiを使ってHiLetgo 0.91" I2C シリアルLCDモジュールを表示させてみる - キノコの自省録

対象製品はこれ。今売り切れてますね……。

HiLetgo 0.91" IIC I2C シリアルOLED液晶ディスプレイモジュール 128x32 3.3V/5V AVR PIC Arduino UNO MEGA [並行輸入品]

これ使って、時計とか作ろうかなと思ってたりしてます。

フォントとフォントサイズの変更

GitHub - adafruit/Adafruit_Python_SSD1306: Python library to use SSD1306-based 128x64 or 128x32 pixel OLED displays with a Raspberry Pi or Beaglebone Black.

このexplamples/stats.pyをベースに改変してみます。フォントの指定は、98行目のここで行っています。

# Load default font.
font = ImageFont.load_default()

このImageFontというのはPillowのモジュールです。ドキュメントはこちら。

ImageFont Module — Pillow (PIL Fork) 4.2.1 documentation

stats.pyにもコメントとして書かれてあるように、ImageFont.truetype()メソッドを使用すれば、フォントやフォントサイズの変更ができます。

# Alternatively load a TTF font.  Make sure the .ttf font file is in the same directory as the python script!
# Some other nice fonts to try: http://www.dafont.com/bitmap.php
# font = ImageFont.truetype('Minecraftia.ttf', 8)

.ttfフォントがpythonスクリプトと同じディレクトリに入ってるか確認してね、って書いてありますが、そこに配置せず、今回はapt-getでIPAフォントをinstallする方法を試みます。

$ sudo apt-get install fonts-ipafont

インストールされたフォントは、/usr/share/fontsにあります。

$ ls /usr/share/fonts/truetype/
fonts-japanese-gothic.ttf  fonts-japanese-mincho.ttf

このフォントを使って表示させてみます。フォントサイズは倍の16に変更します。

#font = ImageFont.load_default()
font = ImageFont.truetype("fonts-japanese-gothic.ttf", 16)
 :
draw.text((x, top), "hello,", font=font, fill=255)
draw.text((x, top+16), "world!", font=font, fill=255)

システムがフォントのパスを解決しますので、ttfのファイル名だけでOKです。結果はこちら。

f:id:kinokorori:20171001225045p:plain

日本語の表示

IPAフォントは日本語フォントなので、そのまま日本語が表示できます。pythonスクリプト文字コードutf-8にして、ファイル先頭にcoding:utf-8を入れます。

# -*- coding:utf-8 -*-
 :
draw.text((x, top), "こんにちは、", font=font, fill=255)
draw.text((x, top+16), "世界!", font=font, fill=255)

これを実行すると、見事に化けます。draw.textはunicodeを要求しますので、正しくデコードできずに文字化けが発生します。そのため、下記のように修正します。

# -*- coding:utf-8 -*-
 :
draw.text((x, top), u"こんにちは、", font=font, fill=255)
draw.text((x, top+16), u"世界!", font=font, fill=255)

結果はこちら。

f:id:kinokorori:20171001225129p:plain

シャウトアラームver2.0.1を公開しました

シャウトアラーム 叫んで止める!目覚まし時計の最新版ver2.0.1をAppStoreに公開しました。

※この記事を書いている時点ではまだver1.0.0ですが、既に審査通過→公開準備状態となっているため、しばらくすれば切り替わると思います

更新内容としては、

  • iOS11用ビルド
  • アイコン、UI等のデザインを変更
  • アラームのアンロックをもう少し楽に
  • iPad対応

といったところです。この対応で、iOS11以降でも使えると思います。

アラームのアンロックについては、iOS7あたりからゲージ上昇が渋くなっていたので、緩くしました。解除条件を何段階かで切り替えられるようにした方がいいかなあと思いつつ、とりあえず今回はそのままになっています。

アラームの設定にカレンダーを使用していましたが、UIDatePickerに吸収させました。アイコンは、今風なすっきりとした感じに変更しました。元々こういう感じのアイコンにしたかったんですが、2013年当時はデザイン力が足りずに無理でした。4年も経つと、それなりにデザイン力も上がるもんですね。

f:id:kinokorori:20170930213606p:plain

iPad触っている時に気づいたんですが、通知の許可をリクエストするコードを入れるのを忘れてた模様。たぶん、今までは動かない端末がいくつかあったのではないかと思います。。。ちなみに、おやすみモード中はアラームが鳴らないので注意してください。

AngularJS1.xで$scopeを使わず親子間通信

今ホットなライブラリのAngularJSですが、あまりにも更新が速いためか、ちょっと前の情報でもすぐ陳腐化してしまっています。そのせいか、なかなか欲しい情報に辿り着けないという困った状況になっている気がします。

そんなAngularJSですが、1.5以上ではDirectiveの代わりにComponentが導入され、さらに$scopeを使わない書き方が推奨されています。Directiveの代わりにComponentを使用する点はあまり問題がないのですが、$scopeの便利さにべったり依存していると、いざ$scopeが禁止されてしまった場合、どう書いていいかわからない事態に陥ります。特に親子間通信($emit, $broadcast)で困ります。実際困りました。ということで、$scopeの甘い誘惑に耐えて親子通信をするお話がメイン。

解決課題

$scopeを使わず、

  • 子から親のオブジェクトを更新
  • 親から子へのオブジェクト更新通知
  • 子から親へ引数付きメソッド呼び出し

を行います。

なお、対象はタイトルの通り、AngularJS(1.x系)です。Angular2系ではありません。このエントリは2017年9月27日に書かれています。

例題

例として、こんなイメージのページを作るとします。左に毒キノコの一覧が出ていて、キノコを選択すると、右ペインに説明や写真が表示されるといった具合です。

f:id:kinokorori:20170904015407p:plain

Componentおさらい

DirectiveはComponentに置き換えましょう。もうng-controllerとかも使っていませんよね?ng-controllerは早めに消しましょう。オブジェクトの意識が低くなり、再利用が非常にしづらくなります。

下記は、キノコの一覧をサーバから取得して表示するみたいなイメージのコードです。

<!-- kinoko.html -->
<div>
    <!-- キノコ一覧(左ペイン)-->
    <kinoko-list></kinoko-list>
</div>
<div>
    <!-- キノコの説明文(右ペイン) -->
    <kinoko-explain></kinoko-explain>
</div>

//kinokolist.js

class KinokoListController {
    //インジェクションはコンストラクタで指定可能
    constructor($http) {
        this.$http = $http;
    }

    $onInit() {
        this.kinokolist = [];
        //$httpでリストを取得するなど
        var self = this;
        this.$http({
            method: 'get',
            url:'/kinokolist',
        })
        .then(function(res) {
            //メンバ変数kinokolistにデータを入れる
            self.kinokolist = [...];
        }, function(err) {
            //エラー処理
        });
    }

    onclick(kinoko) {
        console.log(kinoko);
        // 親へ通知したい!
    }
}

angular.module()
.Component("kinokoList", {
    template: ['<div id="kinokolist" ng-repeat="kinoko in KinokoCtrl.kinokolist">,
                  '<p ng-click="KinokoCtrl.onclick(kinoko)">{{kinoko}}</p>',
               '</div>].join(""),
    controller: KinokoListController,
    controllerAs: 'KinokoCtrl'
});

※ $httpですが、Angular1.6ではsuccess()が使えなくなっています。上記例のようにthen()に置き換えましょう。

親への通知

左ペインのキノコ一覧のキノコが選択されたとき、選択されたキノコを親に通知したいという場合を考えます。

敗北例

$emitを使用する敗北例です。constructor($http, $scope)にして$scopeをインジェクション、onclickで$emitを使用するとこんな感じになります。

//kinokolist.js

constructor($http, $scope) {
    this.$scope = $scope;
..(略)..

onclick(kinoko) {
    this.$scope.$emit('selectedkinoko', kinoko);
}

これで親に通知されます。しかし$scopeを使用しています。敗北です。

勝利例

bindingsを使用して、親のオブジェクトを透過的に参照する方法を使います。イメージとしてはこんな感じです。

f:id:kinokorori:20170903212614p:plain

コード的には、以下のスキームに従います。

  1. 親のコントローラにオブジェクト実体を定義
  2. コンポーネントのhtmlタグに属性を追加し、valueとして親コントローラのオブジェクトを参照するよう定義
  3. 子のコンポーネント(KinokoList)のbindingsに、オブジェクト参照(つまり'=')で属性を追加
  4. onclickでその属性を書き換え

① 親のコントローラにオブジェクト実体を定義

コンポーネントを司るControllerをKinokoParentControllerとして、次のような感じでオブジェクトを保持しておきます。

//kinokoparent.js
class KinokoParentController {
    constructor() {
    }

    $onInit() {
        this.selected_kinoko = ""
    }
}

app.Component("kinokoParent", {
    templateUrl: "kinoko.html",
    controller: KinokoParentController,
    controllerAs: 'ParentCtrl'
}

コンポーネントのhtmlタグに属性を追加し、valueとして親コントローラのオブジェクトを参照するよう定義

kinoko.htmlのkinoko-listタグに属性を追加します。

<!-- kinoko.html -->
<div>
    <!-- キノコ一覧(左ペイン)-->
    <kinoko-list selectkinoko="ParentCtrl.selected_kinoko"></kinoko-list>
</div>
<div>
    <!-- キノコの説明文(右ペイン) -->
    <kinoko-explain></kinoko-explain>
</div>

③ 子のコンポーネント(KinokoList)のbindingsに、オブジェクト参照(つまり'=')で属性を追加

要するに、②で追加したselectkinokoのbindingsを定義するということです。

//kinokolist.js

app.Component("kinokoList", {
    template: ['<div id="kinokolist" ng-repeat="kinoko in KinokoCtrl.kinokolist">,
                  '<p ng-click="KinokoCtrl.onclick(kinoko)">{{kinoko}}</p>',
               '</div>].join(""),
    controller: KinokoListController,
    controllerAs: 'KinokoCtrl'
    bindings: {
        selectkinoko: '='
    }
});

④ onclickでその属性を書き換え

//kinokolist.js

onclick(kinoko) {
    this.selectkinoko = kinoko;
}

これで、親(KinokoParentController)のselected_kinokoが書き換わります。

子への通知

親のオブジェクトが変わったことを子が知らないと、記事の更新ができません。今回の例ですと、選択されたキノコに応じて、説明文や写真を変更する必要がありますので、なんとかして変更を知る必要があります。

敗北例

$broadcastを使用します。略。

勝利例

bindingsとLifeCycle Hookの$onChangesを使用します。

まず、親コンポーネントのhtmlであるkinoko.htmlに定義したkinokoExplainコンポーネントに対し、次のような形でselectkinoko属性を追加します。

<!-- kinoko.html -->
<div>
    <!-- キノコ一覧(左ペイン)-->
    <kinoko-list selectkinoko="ParentCtrl.selected_kinoko"></kinoko-list>
</div>
<div>
    <!-- キノコの説明文(右ペイン) -->
    <!-- ここを変更 -->
    <kinoko-explain selectkinoko="{{ParentCtrl.selected_kinoko}}"></kinoko-explain>
</div>

これは、kinokoExplainコンポーネントの属性selectkinokoに対して、文字列としてParentCtrl.selected_kinokoの値を設定しています。次に、kinokoExplainコンポーネントのbindingsを設定します。

//kinokoexplain.js

app.Component('kinokoExplain', {
    templateUrl: kinokoexplain.html,
    controller: KinokoExplainController,
    controllerAs: 'KinokoExplainCtrl'
    bindings: {
        selectkinoko: '@'
    }
});

最後に、コントローラクラスにLifeCycle Hookの$onChangesを定義します。

//kinokoexplain.js

class KinokoExplainController {
    constructor($http) {
        this.$http = $http;
    }

    $onChanges(changedobj) {
        if (changedobj.selectkinoko) {
            var obj = changedobj.selectkinoko;
            if (!obj.isFirstChange()) {
                //selectkinokoが更新されたので、記事を取りに行くなど
            }
        }
    }
}

これで、キノコリストコンポーネント→親コンポーネント→キノコ説明文コンポーネントという更新通知の系列が出来上がりました。

親がselected_kinokoの書き換わりを知るには

実は、selected_kinokoが書き換わった時、親コンポーネント自身は、値が書き換わったことがわかりません。親コンポーネント自身がキノコリストの変更を受けて何か処理をしたい場合、これでは困ってしまいます。解決策として、親のメソッドを呼び出してみます。

<!-- kinoko.hml -->
<div>
    <!-- キノコ一覧(左ペイン)-->
    <kinoko-list selectkinoko="ParentCtrl.selected_kinoko" change="ParentCtrl.change();"></kinoko-list>
</div>
<div>
    <!-- キノコの説明文(右ペイン) -->
    <kinoko-explain selectkinoko="{{ParentCtrl.selected_kinoko}}"></kinoko-explain>
</div>
//kinokolist.js

onclick(kinoko) {
    this.selectkinoko = kinoko;
    this.change();
}
//kinokolist.js

app.Component("kinokoList", {
      :
    controllerAs: 'KinokoCtrl'
    bindings: {
        selectkinoko: '=',
        change: '&'       
    }
});

change属性をメソッドでバインディングして、onclick()発生時にParentCtrlのchange()メソッドを呼び出しています。あとは、ParentControllerにchangeメソッドを定義してやるだけなんですが、実はこれ、問題があります。

//kinokoparent.js

class KinokoParentController {
       :

    change() {
        console.log(this.selected_kinoko);
    }
}

console.logで出力すると、書き換わる前のselected_kinokoが出力されます。これでは困ります。なので、引数を付けて呼び出しましょう。その引数ですが、JSONで指定する必要があります。

//kinokolist.js

//kinokolist.js

onclick(kinoko) {
    this.selectkinoko = kinoko;
    //this.change(kinoko)はダメ
    this.change({selectkinoko: kinoko});
}

呼び出されるchangeメソッドは、次のようになります。

//kinokoparent.js

class KinokoParentController {
       :

    change(selectkinoko) {
        console.log(selectkinoko);
    }
}

終わりに

Angular2系の親和性のために、$scopeを投げ捨てようという風潮に従ってみました。

AngularJSは一つの課題に対し、現状、複数のやり方が存在するので、これ以外にも方法はあると思います。どれがObsoleteで、どれがup-to-dateなのかわかりにくいため、参照する記事はくれぐれも慎重に選びましょう。この記事も、実際のところ正しい保証はありません。