ボタン

これまでは、デバイスに何かをさせるコードを作成しました。これは 出力 と呼ばれます。しかし、物事に反応するためのデバイスも必要です。このような事は 入力 と呼ばれます。

これを憶えるの簡単です: 出力はデバイスが世界に出すものであり、入力はデバイスが処理するものです。

micro:bit で入力を行う最も当たり前の手段は、 AB のラベルのついた2個のボタンです。ボタンが押されたことに反応するには MicroPython が必要そうです。

これはとても簡単です:

from microbit import *

sleep(10000)
display.scroll(str(button_a.get_presses()))

このスクリプトの全貌は、1万ミリ秒(すなわち10秒)スリープしている間にボタン A を押した回数をスクロール表示するというものです。それでおしまい!

これはほとんど役に立たないスクリプトですが、面白い新アイデアをいくつか紹介します:

  1. sleep 関数 は micro:bit を指定のミリ秒数だけスリープさせます。プログラムの実行を一時停止させたいなら、この関数を使うとよいです。関数メソッド のようなものですが、 オブジェクト にドットで結びつけられていません。
  2. button_a というオブジェクトがあり、その get_presses メソッド で押した回数を得られます。

get_presses は数値を返し、 display.scroll は文字だけを表示するので、数値を文字列に変換する必要があります。これを行うのが str 関数です("string" の略 ~ 指定されたものを文字列に変換します)。

3行目はちょっと玉ねぎみたいになってます。括弧が玉ねぎ状になっているので、 display.scrollstr を含み、それがまた button_a.get_presses を含んでいることがわかるでしょう。Python は、最初に最も内側の答えを出し、次の外側に向かって実行していこうとします。これは 入れ子 (nesting)といいます。このようなコードは、ロシアのマトリョーシカ人形の作りと同じです。

../_images/matrioshka.jpg

あなたがボタンを10回押したとしましょう。Python が3行目で起こっていることをどう処理しているかは以下のとおりです:

Python は行全体を解釈して、 get_presses の値を求めます:

display.scroll(str(button_a.get_presses()))

Python はボタンが何回押されたかを知ったので、この数値を文字列に変換します:

display.scroll(str(10))

もう、Python は何をスクロール表示するのかが分かりました:

display.scroll("10")

これは大変な作業のように見えるかもしれませんが、MicroPython はこれを非常に高速に行います。

イベントループ

何かが起こるのを待つプログラムが必要になることがよくあります。これを行うには、ボタンを押すなどの特定のイベントにどう反応するかを定義するコードの周りにループを作ります。

Python でループを作るために、 while キーワードを使います。これは何かが True であるかをチェックします。True であるなら、ループの 本文 と呼ばれる コードブロック を実行します。そうでない場合は、ループから抜け出し(本文を無視して)、後のプログラムを続行します。

Pythonでは、コードブロックを簡単に定義できます。紙の上に書かれた to-do リストがあるとします。これはたぶん次のようなものになります:

Shopping
Fix broken gutter
Mow the lawn

to-do リストをもう少し具体的にしたいと思ったら、次のように書くかもしれません:

Shopping:
    Eggs
    Bacon
    Tomatoes
Fix broken gutter:
    Borrow ladder from next door
    Find hammer and nails
    Return ladder
Mow the lawn:
    Check lawn around pond for frogs
    Check mower fuel level

メインタスクがサブタスクに分解されていることが一目瞭然です。メインタスクの下に関連するものがサブタスクとして インデント (字下げ)された構造になっています。つまり Eggs, Bacon, TomatoesShopping に関連づけられていることは明らかです。インデントすることで、タスクが互いにどのように関連しているかを一目で分かりやすくします。

これは 入れ子 (nesting)と呼ばれます。入れ子を使って次のようなコードブロックを定義します:

from microbit import *

while running_time() < 10000:
    display.show(Image.ASLEEP)

display.show(Image.SURPRISED)

running_time 関数は、デバイスが始動してからのミリ秒数を返します。

while running_time() < 10000: の行は動作時間が1万ミリ秒(すなわち10秒)未満であるかをチェックします。このチェックにとおり、 その実行中に見ていたならば 、イメージ Image.ASLEEP を表示します。これが、先の to-do リストのように while 文の下にインデントされていることに注目してください。

もちろん、実行時間が 10000 ミリ秒以上になれば、その後の Image.SURPRISED を表示します。どうしてかって? while 条件は False になるので(running_time はもう < 10000 ではありません)、ループが終了し、 while ループのコードブロックの後のプログラムを続けて実行します。あなたのデバイスは 10 秒間眠ってから、驚いた顔で目を覚まします。

試してみてください!

イベントの処理

MicroPython がボタンを押すイベントに反応するようにしたい場合、ボタンが押されたかをチェックする is_pressed を無限ループの中に入れます。

無限ループは簡単です:

while True:
    # 処理の実行

(while は何かが True であるかをチェックし、そうであればコードブロックを実行するということを思い返してください。つまり、条件に True を指定すると明らかに True であるので、無限ループを実現できるわけです!)

簡単なサイバーペットを作りましょう。あなたが A ボタンを押している場合を除き、常に悲しい顔をします。あなたが B ボタンを押すと死にます。(これがとても楽しいゲームではないことが分かりますので、きっとあなたが楽しいゲームにする方法をあみだしてくれることでしょう):

from microbit import *

while True:
    if button_a.is_pressed():
        display.show(Image.HAPPY)
    elif button_b.is_pressed():
        break
    else:
        display.show(Image.SAD)

display.clear()

どのボタンが押されているかをチェックする方法がわかりますか? if, elif ("else if" の短縮形), else を使っています。これは 条件文 というもので、次のように処理します:

if 何か is True:
    # 何かを行う
elif 他の何か is True:
    # 別の何かを行う
else:
    # さらに別の何かを行う

これは英語に非常に似ています!

is_pressed メソッドは2つの結果だけを返します: TrueFalse です。ボタンを押している場合は True を返し、そうでない場合は False を返します。上記のコードを日本語で表すと「永遠に、ボタン A が押されたら幸せな顔を表示し、ボタン B が押されたらループから抜け、さもなければ悲しい顔を表示します」となります。ループの外に抜ける(永遠に動き続けるプログラムを止める)には break 文を使います。

最後に、サイバーペットが死んで、ディスプレイを clear します。

このゲームをあまり悲惨なものでなくする方法を考えてみませんか? 両方 のボタンが押されたことをチェックするにはどうしますか? (ヒント: Python には、複数の条件(True または False のどちらかを結果とするもの)のチェックを助ける論理演算子 and, or, not があります。