JavaScript
Blockly

ビジュアルプログラミング・エディタのBlocklyをプログラムする

はじめに

世の中には数多のプログラミング言語がありますが、テキストではなくブロックなどのオブジェクトを組みたててプログラミングをする、ビジュアルプログラミング言語というジャンルがあります。

ビジュアルプログラミング言語には、データフローやビジネスプロセスモデリング1に特化したものもありますが、教育目的で利用されるScratchという言語の名前を聞いたことがある人もいらっしゃると思います。または、Hour of Code2という、コンピューターサイエンスが身近で楽しいことを伝える世界的な活動をご存知かもしれません。

Scratchライクなビジュアルプログラミング・エディタが簡単に作れるBlocklyを使うと、ビジュアルプログラミング環境をかんたんに作ったりカスタマイズすることができます。Hour of Codeの教材のcode.orgも、Blocklyを利用しています。

Blocklyの日本語記事がほとんどなかったので、導入からカスタマイズのやりかたまでを書いてみます。3

Screen Shot 2017-12-17 at 16.41.10.png
Google for Education > Blocklyより

セットアップ

Get StartedにBlocklyのライブラリのリンクがあります。

基本的には、公式ドキュメントを読み進めて行けばBlocklyをカスタマイズしていくところまでできます。
とはいえ、環境準備なども含めると手間になるので、テンプレートとなるリポジトリを作りました。

予めnode.jsをインストールしておいてください。

$ git clone -b getting_started git@github.com:taise/blockly_custom.git && cd blockly_custom
$ npm install yarn -g
$ yarn

ここまで終わったら、以下のコマンドを実行するとBlocklyの画面が表示されます。

$ npm run http-server

ブロックを設置する

環境準備が終わったので早速ブロックを配置していきましょう。
左側のグレーのエリアはtoolboxといい、プログラミングで使えるブロックを配置します。

今回はcontrols_repeat,text,text_printのブロックをおいてみます。ブロックを追加するには、id="toolbox"の属性を持つxmlタグに利用したいブロックを定義していきます。

  <xml id="toolbox" style="display: none">
      <block type="controls_repeat"></block>
      <block type="text"></block>
      <block type="text_print"></block>
  </xml>

すると、すると以下のようにブロックが表示されます。
ブロックを右側のworkspaceエリアにドラッグ & ドロップすることで、プログラミングをしていきます。

Dec-17-2017 20-16-07.gif

たくさんのブロックがカスタマイズなく利用できます。全てのブロックは紹介しきれないので、他のブロックも見てみたい場合は
こちらのリンク先のデモが参考になると思います。

ブロックのカテゴライズ

ブロックが増えてきた場合は、ブロックのカテゴライズができます。
toolboxxmlにloop系のブロックを足してカテゴリーを作ります。

  <xml id="toolbox" style="display: none">
    <category name="くりかえし">
      <block type="controls_repeat_ext"></block>
      <block type="controls_repeat"></block>
      <block type="controls_whileUntil"></block>
      <block type="controls_for"></block>
      <block type="controls_forEach"></block>
      <block type="controls_flow_statements"></block>
    </category>
    <category name="文字と出力">
      <block type="text"></block>
      <block type="text_print"></block>
    </category>
  </xml>

Dec-17-2017 22-03-57.gif

カテゴリーは、複数階層作ることもできます。
なお、ここで追加したloop系のブロックは、blockly/blocks/loops.jsのものを羅列しています。

Blocklyの設定

カテゴリーを作ると右下にゴミ箱(trashcan)が出てきましたが、これをいつでも表示させたいなど、Blockly全体に関わる設定はBlockly.injectをする際に行います。

Configurationのところに設定項目のリストがあります。

試しに色々設定してみます。
- 最大ブロック数: 3
- グリッドを表示
- ゴミ箱を表示
- ズームのコントロールパーツ表示

index.js
Blockly.inject(
  'blocklyDiv',
  {
    toolbox: document.getElementById('toolbox'),
    maxBlocks: 3,
    grid: {
      spacing: 18,
      length: 3,
      colour: '#ccc',
      snap: true,
    },
    trashcan: true,
    zoom: {
      controls: true,
      wheel: true,
      startScale: 1.0,
      maxScale: 3,
      minScale: 0.3,
      scaleSpeed: 1.2,
    },
  },
);

Dec-17-2017 23-36-47.gif

実行可能なJavaScriptを出力する

Blocklyはブロックで作ったプログラムをJavaScriptに変換して実行することができます。まずはJavaScriptに変換して表示してみます。

index.htmljavascript/compressed.jsの読み込ませ、JavaScript表示用のボタンと表示エリアを作ります。

index.html
  <button id="showCode">JavaScriptを表示する</button>
  <pre id='jsCode'></pre>

  <script src="lib/google-blockly/blockly_compressed.js"></script>
  <script src="lib/google-blockly/blocks_compressed.js"></script>
  <script src="lib/google-blockly/javascript_compressed.js"></script>

Blockly.JavaScript.workspaceToCode(workspace)のように実行すると、workspaceに置かれたブロックをJavaScriptに変換できます。

index.js
const workspace = Blockly.inject(
  'blocklyDiv',
  {
    toolbox: document.getElementById('toolbox'),
    trashcan: true,
  },
);

function showCode() {
  Blockly.JavaScript.INFINITE_LOOP_TRAP = null;
  const pre = document.getElementById('jsCode');
  pre.innerHTML = Blockly.JavaScript.workspaceToCode(workspace);
}

document.getElementById('showCode').addEventListener('click', showCode, false);

Dec-18-2017 00-10-53.gif

なお、出力できる言語はJavaScriptだけではなく、python, php, lua, dartにも対応しています。4

ブロックをJavaScriptとして実行する

JavaScriptの文字列が取得できたので、eval()を使えばブラウザ上で実行することができます。ただし、htmlに埋め込むために&などが実体参照に変換されていたり、人間の目で追えない速度で実行されてしまったり、セキュリティのリスクがあるなど、そのまま使うにはいくつか課題もあります。
そのため、公式ドキュメントでも紹介されているとおり、JS Interpreter projectを使うのが良さそうです。

まずは、index.htmlに実行用のボタンとインタープリタのスクリプトを読み込ませましょう。

index.html
  <button id="showCode">JavaScriptを表示する</button>
  <button id="runCode">JavaScriptを実行する</button>
  <pre id='jsCode'></pre>

  <script src="lib/js-interpreter/acorn_interpreter.js"></script>
  <script src="lib/google-blockly/blockly_compressed.js"></script>
  <script src="lib/google-blockly/blocks_compressed.js"></script>

つづいてindex.jsにインタープリタの設定を書いていきます。alertが実装されていないため、createNativeFunctionで追加する必要があります。

index.js
function runCode() {
  let maxSteps = 10000;
  const code = Blockly.JavaScript.workspaceToCode(workspace);

  function initialize(interpreter, scope) {
    function alertWrapper(text) {
      const msg = text ? text.toString() : '';
      return interpreter.createPrimitive(window.alert(msg));
    }

    interpreter.setProperty(scope, 'alert', interpreter.createNativeFunction(alertWrapper));
  }

  const jsInterpreter = new Interpreter(code, initialize);
  while (jsInterpreter.step() && maxSteps) {
    maxSteps -= 1;
  }
  if (!maxSteps) {
    throw EvalError('Infinite loop.');
  }
  jsInterpreter.run();
}

document.getElementById('showCode').addEventListener('click', showCode, false);
document.getElementById('runCode').addEventListener('click', runCode, false);

これだけでブロックが実際に実行できるようになりました。

Dec-18-2017 07-24-52.gif

ブロックをカスタマイズする

Blocklyは、ブロックの定義を追加することができます。
今回は文字列をシャッフルするブロックを作ってみます。

これだけだと取っ掛かりがなさすぎるので、Githubのblockly/blocks/text.jsを見てみましょう。すると中身がJSONで定義されていることがわかります。

JSONのなかで参照されているBlockly.Msgは、表示用の文字列で、別ファイルで読み込まれています。Blocklyはi18n対応されており、msg/js/ja.jsを読み込んでいるため日本語表示されています。
表示される日本語に違和感を感じた方や、漢字が多くて小学校低学年向けには厳しいという方は、このファイルを書き換えてあげるとよいでしょう。

すこし話がそれてしまいましたが、カスタムブロック用のjsファイルを作ります。
今回のブロックは、シャッフルしたい文字だけなのでmessageargsが1つずつです。

custom_block.js
Blockly.Blocks.text_shuffle = {
  /**
   * Block for shuffle characters.
   * @this Blockly.Block
   */
  init() {
    this.jsonInit({
        message0: '%1をシャッフルする',
      args0: [
        {
          type: 'input_value',
          name: 'TEXT',
          check: 'String',
        },
      ],
      output: 'String',
      inputsInline: true,
      colour: 160,
      tooltip: '文字の順番をランダムでいれかえる',
    });
  },
};

Blockly.Blocksを参照したいのと、Blockly.injectで環境が初期化される前に読み込みたいので、scriptファイルの読み込み順に気をつけてください。

index.html
  <script src="lib/google-blockly/msg/js/ja.js"></script>
  <script src="custom_block.js"></script>
  <script src="index.js"></script>

これで再度読み込んでみると...でました!

Dec-18-2017 08-59-26.gif

しかし、「JavaScriptを表示/実行する」は、ボタンを押しても反応がありません。
コンソールを開くとエラーが出ています。実装していないので当然ですね。実装しましょう。

Blockly.JavaScriptに対象ブロックのJavaScriptを生成するメソッドを生やします。
見ていただくとわかりますが、そうです、ただの文字列連結です。
当たり前なんですがこうしてみると、改めてプログラミング言語って文字列なんだなと思わされますね。

custom_block.js
Blockly.JavaScript.text_shuffle = function(block) {
  const args0 = Blockly.JavaScript.valueToCode(block, 'TEXT', Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\'';
  const OPERATOR = ".split('').sort(function(){return 0.5-Math.random()}).join('')";
  return [args0 + OPERATOR, Blockly.JavaScript.ORDER_MEMBER];
};

では、これで実行してみます。

Dec-18-2017 09-05-54.gif

カスタムブロックのJavaScriptを実行できました

おわりに

これで自由にBlocklyをカスタムしながら、コンテンツを作る準備が整いました。
さらっとできる感じで書いてますが、なんだかんだ詰まりつつここまでまとめています。

基本的には公式ドキュメントに記載している範囲の内容ですので、もっと詳しく知りたくなった方はBlocklyのドキュメントをご覧ください。

5


  1. 例えば、BPELなどがあります。BPELは、XMLベースのビジュアルプログラミング言語で、SOAが流行していた時期にサービス間のオーケストレーションを目的に使われていましたね。 

  2. 私が所属するリブセンスもCSR活動としてHour of Codeのワークショップを開催しました。 

  3. BlocklyにはWeb / Android / iOS向けのライブラリがありますが、Web版のみです。
    なお、この記事は職業プログラマ向けのレベルで書かれています。 

  4. 多言語への切り替えはgenerating codeを参照ください。 

  5. Livesense - 学 Advent Calendar 2017の記事として書かれたものです。