Blockly 2019 Q4 Release

Blockly도 그동안 계속 버전 패치를 해왔는데, 이번 2019 Q4 릴리즈에선 약간 더 큰 변화를 가지고 릴리즈 되었습니다.

https://groups.google.com/forum/#!searchin/blockly/npm%7Csort:date/blockly/cOxLdS7vX-c/bq-KIyBXAAAJ

Zelos라는 렌더링 엔진을 사용하는 것인데, 기존 렌더링 엔진인 Geras를 보완하여 개발하였습니다. 블럭들의 모습이 Scratch의 그것들과 상당히 유사해졌고, 실시간으로 블럭을 그려서 사용하다보니 좀더 예쁜? 모습을 보여줍니다.

각 렌더링 엔진에 대한 설명은 다음의 슬라이드에서 참고.

https://docs.google.com/presentation/d/1nNk0DYT_aeMxKS7BVfAZxpQSOmwBu9XuG96oezrp_Ss/edit#slide=id.g40d1aecc4e_0_470

여하튼 기존 개발 환경 구축과 마찬가지로 소스를 가지고와 옵션을 살짝 건드려주면? 다음과 같은 모습을 보여줍니다.

렌더링 엔진을 선택하는 부분은 Blockly를 인젝션하는 부분에 옵션을 다음과 같이 추가해주면 됩니다.

    var workspacePlayground = Blockly.inject('blocklyDiv', {

      toolbox: BLOCKLY_TOOLBOX_XML['standard'],
      media: '/blockly/media/',
      trashcan: true,
      zoom: {
        controls: true,
        wheel: true,
        startScale: 0.7,
        maxScale: 3,
        minScale: 0.3,
        scaleSpeed: 1.2
      },
      renderer : "zelos"
    });

끝.

Change the category title color and thickness of scrollbar on Blockly

Blockly의 처음 인상은 굉장히 단순해 보인다. 몇가지만 수정하면 좀더 이쁘게 보일수 있을 것 같은데, 일단은 코드 상에서 해결해야 하는 제목에서 보이는 두 내용을 적용하도록 수정해본다.

소스 파일의 내용 기준은 현재 가장 최신버전인 blockly-3.20191014.4를 기준으로 한다. 추후 업데이트 되더라도 큰 차이는 없을테니 잘 찾아서 적용하면 될듯.

Scrollbar Thickness

blockly-3.20191014.4/core/scrollbar.js [301:305]

Blockly.Scrollbar.scrollbarThickness = 15;
if (Blockly.Touch.TOUCH_ENABLED) {
  Blockly.Scrollbar.scrollbarThickness = 25;
}

위 두 값 (15, 25)를 원하는 값으로 수정. 대략 4, 8 정도면 적당한 듯.

Category Title Color

blockly-3.20191014.4/core/toolbox.js [259:275]

Blockly.Toolbox.prototype.handleBeforeTreeSelected_ = function(node) {
  if (node == this.tree_) {
    return false;
  }
  if (this.lastCategory_) {
    this.lastCategory_.getRowElement().style.backgroundColor = '';
    this.addColour_(); // add
  }
  if (node) {
    var hexColour = node.hexColour || '#57e';
    node.getRowElement().style.backgroundColor = hexColour;
    node.getRowElement().getElementsByTagName('span')[1].style.color = '#fff'; // add
    // Add colours to child nodes which may have been collapsed and thus
    // not rendered.
    this.addColour_(node);
  }
  return true;
};

blockly-3.20191014.4/core/toolbox.js [605:627]

Blockly.Toolbox.prototype.addColour_ = function(opt_tree) {
  var tree = opt_tree || this.tree_;
  var children = tree.getChildren(false);
  for (var i = 0, child; child = children[i]; i++) {
    var element = child.getRowElement();
    if (element) {
      if (this.hasColours_) {
        var border = '8px solid ' + (child.hexColour || '#ddd');
        var colour = child.hexColour || '#000'; // add
      } else {
        var border = 'none';
        var colour = '#000'; // add
      }
      element.getElementsByTagName('span')[1].style.color = colour; // add
      if (this.workspace_.RTL) {
        element.style.borderRight = border;
      } else {
        element.style.borderLeft = border;
      }
    }
    this.addColour_(child);
  }
};

위와 같이 // add 라고 되어 있는 줄을 함수 내에 추가하고, blockly 디렉토리 내에서 빌드를 해보면,

$ ./build.py

압축된 javascript 파일이 생성된다. 이를 이용하여 데모페이지를 보게 되면,

정해진 카테고리의 테마 색상에 따라, 카테고리의 타이틀 색상과 스크롤바의 크기 역시 세련되게 변경 됨을 볼 수 있다.

Blockly 블럭 만들기 #1

Blockly는 사용자가 원하는 기능의 블럭을 자유롭게 만들수 있다. 기본 제공된 블럭 외에 사용자의 로봇이나 기타 기능과 연동하기 위한 블럭을 생성하고, 이를 툴박스에 넣어 사용하면 된다.

Blockly는 이러한 블럭들을 쉽게 만들수 있도록 도구를 제공한다. 재밌게도 블럭을 생성하는 것도 블럭으로 만든다. 물론 나중에 좀 익숙해지만 텍스트로 편집하는게 좀더 쉬울 수 있지만, 제공되는 도구를 이용하면 생성되는 블럭 모양을 확인하면서 수정이 가능하다.

먼저 Blockly Developer Tools를 사용해보자. 사파리나 크롬 등 브라우저를 열고 (https://blockly-demo.appspot.com/static/demos/blockfactory/index.html)으로 이동한다.

위와 같은 화면이 나오는데, 왼쪽은 블럭을 생성하기 위한 블럭 코딩 영역, 오른쪽 상단엔 생성된 블럭 모양을 실시간으로 확인이 가능하고, 오른쪽 중앙엔 추후 코드에 삽입하기 위한 JSON 코드, 오른쪽 하단엔 블럭을 실제 코드로 생성하기 위한 generator 코드가 생성된다.

일단 흐름을 보기 위해 아주 간단한 블럭을 생성해보도록 한다. 사용자의 문자열 입력을 받아 console에 출력하는 블럭을 만들어 보록 한다. 먼저 블럭의 name을 수정한다. 여기선 console_print로 한다.

다음으로 이 블럭은 다른 블럭들과 상하 연결하여 사용 가능해야 하므로, connection을 top+bottom connections로 변경한다. 이렇게 되면 미리보기 창의 블럭 모양이 변경됨을 볼 수 있다.

다음으로 사용자의 문자열을 입력 받아야 한다. 왼쪽 툴박스 창에서 Input 카테고리내에 value input 블럭을 inputs에 추가한다. 이 입력을 받아서 내부에서 처리하기 위한 변수명은 value_input 옆에 있는 NAME이다. 이를 필요에 따라 적절하게 수정한다.

이제 블럭에 기능을 설명하기 위한 텍스트 블럭을 추가한다. (Field 카테고리 Text 블럭)

입력은 Text만 받아야 하므로, 입력 type에 String 블럭을 추가한다.

마지막으로 블럭의 색상과, 이 블럭을 사용하는데 필요한 tooltip, help url 등을 입력한다.

이제 생성된 블럭의 모양을 확인해보면,

와 같이 간단한 모양의 블럭이 생성되었다. 생성된 JSON 코드를 확인해보면,

{
  "type": "console_print",
  "message0": "console print %1",
  "args0": [
    {
      "type": "input_value",
      "name": "NAME",
      "check": "String"
    }
  ],
  "previousStatement": null,
  "nextStatement": null,
  "colour": 330,
  "tooltip": "Block for printing the user message",
  "helpUrl": "https://ahnbk.com"
}

그리고, 이와 같이 생성된 Generator stub을 보면,

Blockly.JavaScript['console_print'] = function(block) {
  var value_name = Blockly.JavaScript.valueToCode(block, 'NAME', Blockly.JavaScript.ORDER_ATOMIC);
  // TODO: Assemble JavaScript into code variable.
  var code = '...;\n';
  return code;
};

와 같이 되어 있다. 자 이제 블럭이 생성되었으므로, 이를 지난번에 만들었던 예제에 생성된 블럭을 추가해보도록 한다. 예제 디렉토리로 이동하여, my_blocks.js 파일을 생성하고, 다음과 같이 입력한다.

'use strict';

goog.require('Blockly.Blocks');
goog.require('Blockly');

Blockly.defineBlocksWithJsonArray(
[
    // insert blocks here

]);

이제 “insert blocks here 부분에 위에서 생성된 코드를 붙여 넣는다.

'use strict';

goog.require('Blockly.Blocks');
goog.require('Blockly');

Blockly.defineBlocksWithJsonArray(
[
    {
        "type": "console_print",
        "message0": "console print %1",
        "args0": [
          {
            "type": "input_value",
            "name": "NAME",
            "check": "String"
          }
        ],
        "previousStatement": null,
        "nextStatement": null,
        "colour": 330,
        "tooltip": "Block for printing the user message",
        "helpUrl": "https://ahnbk.com"
      },
]);

저장한 다음, index.html 파일을 열고 위 파일을 로딩하도록 추가한다.

<!DOCTYPE html>
<!-- HTML file to host Blockly in a mobile WebView. -->
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <style type="text/css">
    html, body, #blocklyDiv {
      border: 0;
      height: 100%;
      margin: 0;
      padding: 0;
      width: 100%;
    }
  </style>
  <script src="blockly_compressed.js"></script>
  <script src="blocks_compressed.js"></script>
  <!-- TODO: Select msg file based on locale. -->
  <script src="msg/js/en.js"></script>
  <script src="toolbox_standard.js"></script>
  <script src="my_blocks.js"></script>
</head>
<body>
  <div id="blocklyDiv"></div>
  <script type="text/javascript">
    var workspacePlayground = Blockly.inject('blocklyDiv', {
          media: 'media/',
          toolbox: BLOCKLY_TOOLBOX_XML['standard'],
          zoom: {controls: true}
        });
  </script>
</body>
</html>

바로 이전 포스팅에서 만든 Custom 카테고리에 생성된 블럭을 추가한다.

+ '<category name="Custom" colour="100">'
+   '<block type="console_print">'
+   '</block>'
+ '</category>'

자, 이제 예제를 다시 열어보면,

Custom 카테고리에 console print 블럭이 추가되어 있음을 볼 수있다. 만약 문자열 입력에 default 값을 넣어 주고 싶다면, 툴박스에 코드를 다음과 같이 수정한다.

+ '<category name="Custom" colour="100">'
+   '<block type="console_print">'
+     '<value name="NAME">'
+       '<shadow type="text">'
+         '<field name="TEXT">Hello Blockly</field>'
+       '</shadow>'
+     '</value>'
+   '</block>'
+ '</category>'

이제 다시 예제를 확인해보면,

와 같이 되어 있음을 확인할 수 있다. 다음엔 좀더 다양한 모양의 블럭을 만들어보록 한다.

Blockly 툴박스에 카테고리 추가

Blockly를 실행하면 좌측 (혹은 설정에 의해서 하단)에 툴박스가 존재한다. 사용자의 필요에 따라 이 툴박스에 카테고리를 추가하는 등에 대한 작업을 할 수 있다.

툴박스에 설정은 toolbox_standard.js 파일에서 진행한다. 지난 포스팅에서 실행했던 디렉토리에서 toolbox_standard.js 파일을 열어보면…

var BLOCKLY_TOOLBOX_XML = BLOCKLY_TOOLBOX_XML || Object.create(null);

/* BEGINNING BLOCKLY_TOOLBOX_XML ASSIGNMENT. DO NOT EDIT. USE BLOCKLY DEVTOOLS. */
BLOCKLY_TOOLBOX_XML['standard'] =
// From XML string/file, replace ^\s?(\s*)?(<.*>)$ with \+$1'$2'
// Tweak first and last line.
'<xml>'
+ '<category name="Logic" colour="%{BKY_LOGIC_HUE}">'
+   '<block type="controls_if"></block>'
+   '<block type="logic_compare"></block>'
+   '<block type="logic_operation"></block>'
+   '<block type="logic_negate"></block>'
+   '<block type="logic_boolean"></block>'
+   '<block type="logic_null" disabled="true"></block>'
+   '<block type="logic_ternary"></block>'
+ '</category>'
+ '<category name="Loops" colour="%{BKY_LOOPS_HUE}">'
+   '<block type="controls_repeat_ext">'
+     '<value name="TIMES">'
+       '<shadow type="math_number">'
+         '<field name="NUM">10</field>'

...

와 같이 되어 있다. Javascript로 되어 있으며, 자세히 살펴보면 XML을 String 형태로 변환해서 사용함을 볼 수 있다. 주석에 나와 있는 것처럼, Blockly Dev Tools를 이용해서 만들어 줄 수 있지만, 간단한 작업은 본 파일을 수정하면 된다.

Custom이라는 카테고리를 추가해보도록 한다. 블럭은 아직 추가하지 않기로 한다. 카테코리를 추가하고 싶은 지점에 다음과 같이 추가한다.

+ '<category name="Custom" colour="100">'
+ '</category>'

이제 다시 웹페이지를 reload해서 보게 되면,

Custom 카테고리가 추가되어 있는 것을 볼 수 있다. 아직 블럭들이 추가되어 있지 않으므로, 클릭해도 블럭들은 보이지 않는다. 카테고리 사이에 구분자(Seperator)를 추가하려면 원하는 지점에 다음과 같이 추가한다.

여기까지.

Blockly 실행 해보기

지난번 포스팅 (https://ahnbk.com/?p=469)대로 Blockly 소스를 받아 빌드하고, 그 결과물로 실행보도록 한다. 나중에 iOS나 Android에서도 어짜피 webview를 이용해서 실행해야 하므로, 과정은 동일하다.

먼저 작업할 디렉토리를 생성한다. 여기에선 편의상 blockly_demo란 디렉토리를 사용한다.

$ mkdir blockly_demo
$ cd blockly_demo

이제 이 디렉토리에 빌드된 파일을 복사한다.

javascript_compressed.js
blocks_compressed.js
blockly_compressed.js

다음으로 demos/mobile/html/toolbox_standard.js 툴박스 파일도 작업 디렉토리로 복사한다. 또 localization 관련한 디렉토리도 복사한다. 복사 위치는 msg 디렉토리 밑으로 한다. 본래 Blockly 디렉토리의 msg/js 디렉토리를 현재 개발 디렉토리로 복사한다. 완료되었으면 다음과 같이 보여야 한다.

이제 에디터를 이용해서 파일을 하나 생성한다. 파일 이름은 index.html 이다. 파일 내용은 다음과 같다.

<!DOCTYPE html>
<!-- HTML file to host Blockly in a mobile WebView. -->
<html>
<head>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <link rel="stylesheet" href="styles.css">
  <script src="blockly_compressed.js"></script>
  <script src="blocks_compressed.js"></script>
  <!-- TODO: Select msg file based on locale. -->
  <script src="msg/js/en.js"></script>
  <script src="toolbox_standard.js"></script>
</head>
<body>
  <div id="blocklyDiv"></div>
  <script type="text/javascript">
    var workspacePlayground = Blockly.inject('blocklyDiv', {
          media: 'media/',
          toolbox: BLOCKLY_TOOLBOX_XML['standard'],
          zoom: {controls: true}
        });
  </script>
</body>
</html>

또 style.css 파일로 생성하여 다음과 같이 입력한다.

html, body, #blocklyDiv {
    border: 0;
    height: 100%;
    margin: 0;
    padding: 0;
    width: 100%;
}

이제 크롬, 사파리, 인터넷 익스플로러 등 웹브라우저를 이용해 index.html을 열어본다.

Blockly가 잘 로딩되어 실행됨을 볼수 있다.

Blockly 개발환경 구축

Blockly는 오픈소스로 소스가 모두 개방되어 있다. 이를 가져와 개발자의 의도 및 용도에 맞게 수정하여 사용 가능하다. Blockly는 미들웨어 성격으로, 사용자의 어플리케이션에 맞게끔 수정 및 기능 추가가 용이하다.

일단 Blockly 소스를 가져와 빌드해보도록 한다. Javascript의 빌드 결과물은 주로 xxxx_compressed.js의 이름으로 저장되며, 여러개로 분산된 코드를 하나의 파일에 압축(정확히는 함축)하여 저장한다.

먼저 소스를 가져온다. github의 Blockly 레포지토리 (https://github.com/google/blockly/releases) 에서 최신 릴리즈 소스 (https://github.com/google/blockly/archive/1.20190215.0.tar.gz)를 다운로드한다.

받은 압축 파일을 임의의 디렉토리로 이동한 후 압축을 푼다. (본 포스팅에선 ~/Developer 를 사용.)

$ cd ~/Developer
$ tar zxf blockly-1.20190215.0.tar.gz

빌드하기 위해선 일단 python2.7이 설치되어 있어야 한다. (macOS엔 python 2.7이 기본 설치되어 있으므로 괜찮음. Windows에선 따로 설치 필요)

$ cd blockly-1.20190215.0
$ ./build.py

이때 다음과 같은 에러가 발생하면, closure-library가 필요하다.

Error: Closure not found.  Read this:
developers.google.com/blockly/guides/modify/web/closure

위 에러 메시지에서 알려준 링크 (developers.google.com/blockly/guides/modify/web/closure)로 가서 압축 파일을 다운로드 한다.

받은 파일을 ~/Developer에 풀고, 디렉토리 이름을 closure-library로 변경한다.

$ cd ~/Developer
$ tar zxf ~/Downloads/google-closure-library-v20190301-20-gde66a8a.tar.gz 
$ mv google-closure-library-de66a8a closure-library

이제 다시 blockly 디렉토리로 이동하여 빌드한다.

$ cd blockly-1.20190215.0
$ ./build.py

여러 메시지가 쭈욱 나오고, 에러가 없이 마무리되면 다음과 같은 파일이 생성된다. (이미 컴파일된 파일이 있긴 하지만, 업데이트 된다) 만약 java.lang.RuntimeException: INTERNAL COMPILER ERROR와 같은 에러가 발생한다면, 잠시후 다시 실행하면 정상적으로 빌드 된다.

...

SUCCESS: msg/js/tlh.js
SUCCESS: msg/js/ba.js
SUCCESS: blockly_uncompressed.js
SUCCESS: blockly_accessible_uncompressed.js
SUCCESS: blockly_compressed.js
Size changed from 2637 KB to 780 KB (30%).
SUCCESS: blockly_accessible_compressed.js
Size changed from 2739 KB to 826 KB (30%).
SUCCESS: blocks_compressed.js
Size changed from 154 KB to 74 KB (48%).
SUCCESS: javascript_compressed.js
Size changed from 85 KB to 47 KB (56%).
SUCCESS: python_compressed.js
Size changed from 71 KB to 36 KB (51%).
SUCCESS: php_compressed.js
Size changed from 74 KB to 38 KB (52%).
SUCCESS: lua_compressed.js
Size changed from 66 KB to 33 KB (50%).
SUCCESS: dart_compressed.js
Size changed from 78 KB to 41 KB (52%).

빌드 완료.

생성된 파일의 용도를 설명하면…

  • blockly_compressed.js : Blockly 코어 파일
  • blocks_compressed.js : 기본 블럭
  • javascript_compressed : Javascript 코드 제너레이터
  • python_compressed.js : Python 코드 제너레이터
  • php_compressed.js : PHP 코드 제너레이터
  • lua_compressed.js : Lua 코드 제너레이터
  • dart_compressed.js : Dart 코드 제너레이터

끗!

Blockly 소개

Blockly (블럭클리?, 블로키?)는 구글에서 오픈소스로 개발하고 있는  visual programming editors (소위 블럭코딩)을 개발하기 위한 Javascript 라이브러리이다. Scratch 3.0도 블럭을 제어하기 위한 부분은 Blockly 라이브러리를 가져와 수정하여 사용하고 있다.

대다수의 블럭코딩 툴들이 Blockly를 기반으로 개발되고 있다. 라이센스는 Apach 2.0을 사용하며, 소스는 https://github.com/google/blockly에 있다. 상업적 사용이 가능하고, 변경 및 배포에 아무런 제약이 없다.

Scratch 3.0이 그만의 프레임웍에 맞추어 개발해야 했다면, Blockly는 사용자가 자유롭게 변형하여 목적에 맞는 프로그램을 개발 가능하다.

블럭으로 만들어놓은 부분을 각종 언어 (Javascript, Pythom, Lua, Dart 등)로 생성이 가능하다. 또 사용자가 원하는 블럭도 생성이 가능하다. 코어가 Javascript로 되어 있어, 웹은 물론 Android, iOS에서도 WebView를 통해 실행이 가능하다. (동일한 코드로)

https://developers.google.com/blockly/ 이 메인 싸이트이며, Blockly에 대한 설명, 사용방법, 개발 방법 등에 대한 부분이 상세하게 적혀있다.

https://groups.google.com/forum/#!forum/blockly 는 Blockly 사용자들이 모여 있는 포럼으로 다수의 사용자들이 Blockly를 Customizing하고, 다양한 사례에 적용할 때 발생하는 문제점을 제시하고 서로 논의 하는 곳이다.