基于Blockly的可视化编程

平台

  Windows 10
  Web前端框架:Vue.js+Element UI
  可视化编程:Blockly
  JavaScript解释器:eval5

简介

  Blockly是谷歌开源的基于Web的可视化编程工具。具有以下特性:

  1. 完全是JavaScript,可离线使用
  2. 可生成6门语言:JavaScript、Python、PHP、Dart、Lua和XML
  3. 支持主流的浏览器,如:Chrome, Firefox, Safari, Opera, IE
  4. 支持移动设备
  5. 支持许多编程的基本元素,如:变量、函数、数组
  6. 易于扩展自定义的块
  7. 支持多国语言(40+)

  但是Blockly好像只是一个编辑器,可以通过拖拽的方式实现可视化编程,并生成相应的可执行代码,但是好像并不能运行代码,所以我们还需要一个代码解释器。
  由于是运行在Web端,因此需要一个JavaScript解释器。
  eval5是基于TypeScript编写的JavaScript解释器,支持完整ES5语法。

Blockly配置

安装Blockly

1
npm install blockly

引入Blockly

  在vue文件中的<script>部分引入Blockly和其相关组件:

1
2
3
4
5
6
7
8
// 引入Blockly
import Blockly from 'blockly'
// 引入想要转换的语言,语言有php python dart lua javascript
import 'blockly/javascript'
import 'blockly/python'
// 引入语言包并使用
import * as hans from 'blockly/msg/zh-hans'
Blockly.setLocale(hans)

  在main.js文件中,注册Blockly相关组件:

1
2
3
4
5
6
7
8
Vue.config.ignoredElements.push('xml')
Vue.config.ignoredElements.push('block')
Vue.config.ignoredElements.push('field')
Vue.config.ignoredElements.push('category')
Vue.config.ignoredElements.push('sep')
Vue.config.ignoredElements.push('value')
Vue.config.ignoredElements.push('statement')
Vue.config.ignoredElements.push('mutation')

自定义Blockly块

  Blockly块主要由三个部分组成:

  1. 块定义对象:定义块的外观和行为,包括文本,颜色,字段和连接。
  2. 工具箱参考:工具箱XML中对块类型的引用,因此用户可以将其添加到工作区。
  3. 生成器函数:生成此块的代码字符串。

块定义对象

  这里可以使用官方提供的开发工具:
  下载Blockly源码,打开demos->index.html,找到Blockly Developer Tools工具。
  一个自定义块主要包括以下几个部分:

  1. 块名字
  2. 输入
      value input 值输入
      statement input 块输入
      dummy input 无输入

  3. 输入类型
      external 外接
      inline 内接

  4. 连接方式
      left output左连接(输出)
      top + bottm connections 上下连接
      top connection上连接
      bottom connection 下连接

  5. 工具提示
  6. 帮助提示
  7. 颜色 0-360
    自定义块
      如图,定义了一个open_grasp块,具有一个inline输入,输入变量名称为张开夹爪,变量类型为Boolean,并设置为上下可连接。
      依次点击Sava "block_type"Block Exporter即可查看样式。
      在右侧的Block Definition中选择javaScript,即可查看生成的js代码。

生成器函数

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
// 引入Blockly
import Blockly from 'blockly'

export const initBlockly = (div, toolbox) => {
return Blockly.inject(div,
{
// 工具栏
toolbox: document.getElementById(toolbox),
// 网格效果
grid: { spacing: 20, length: 3, colour: '#ccc', snap: true },
// 媒体资源 (该框架下需要将资源文件放在public文件夹下)
media: 'media/',
// 垃圾桶
trashcan: true,
zoom: {
controls: true,
wheel: true,
startScale: 1.0,
maxScale: 3,
minScale: 0.3,
scaleSpeed: 1.2
}
}
)
}

export const initMyBlockly = () => {
Blockly.Blocks['open_grasp'] = {
init: function() {
this.appendValueInput('VALUE')
.setCheck('Boolean')
.appendField('张开夹爪')
this.setInputsInline(false)
this.setPreviousStatement(true, null)
this.setNextStatement(true, null)
this.setColour(230)
this.setTooltip('')
this.setHelpUrl('')
}
}

Blockly.JavaScript['open_grasp'] = (block) => {
// 后面的 || '\'\'' 部分表示当输入为空时 返回的值
var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE',
Blockly.JavaScript.ORDER_FUNCTION_CALL) || 'false'
// 汇总代码
var code = 'openGrasp' + '(' + argument0 + ');' + '\n'
// 第二个参数为当前使用的操作符对应的优先级 https://www.wenjiangs.com/doc/wkeldh8c
// return [code, Blockly.JavaScript.ORDER_FUNCTION_CALL]
return code
}
}

  新建一个js文件,将块定义对象生成的代码直接复制过来。生成器函数部分,首先需要获取到块定义对象中的变量值,然后组织代码形式并作为返回值返回。

eval5环境搭建

安装eval5

1
npm install --save eval5

引入eval5

  在vue文件中的<script>部分引入eval5

1
import { Interpreter } from 'eval5'

简单使用

1
2
3
4
5
6
7
8
9
10
11
12
import { Interpreter } from 'eval5'

runJavascriptCode() {
const interpreter = new Interpreter(window)
var jsCode = "console.log('Hello')"

try {
interpreter.evaluate(jsCode)
} catch (e) {
console.info('执行代码错误', e)
}
}

  首先实例化Interpreter类,然后调用evaluate()函数即可运行脚本类型的字符串。
  注:这里只能运行JavaScript内部的全局函数,如果是自定义的函数,需要声明为全局函数(适用于Vue):

1
window.myFunName = this.myFunName

简单使用

页面布局

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
<template>
<div id="visualProgram" class="visualProgram">
<el-row type="flex" :gutter="10">
<el-col :span="12">
<!-- blockly工作区 -->
<div id="blocklyDiv" class="div-blocklyDiv">
<!-- blockly工具栏 -->
<xml id="toolbox" style="display: none">
<category name="移动" colour="%{BKY_LOGIC_HUE}">
<block type="open_grasp" />
<block type="logic_boolean" />
<block type="text" />
</category>
</xml>

<div class="div-run-code">
<el-button type="primary" @click="runJavascriptCode">运行javascript code</el-button>
</div>
</div>
</el-col>

<el-col :span="6">
<div class="div-blockly-code">
<!-- blockly代码区 -->
<el-input
v-model="blocklyCodeMessage"
:disabled="true"
type="textarea"
:rows="39"
class="el-input-blockly-code"
/>
</div>
</el-col>

<el-col :span="6">
<div class="div-blockly-console">
<!-- blockly代码控制台 -->
<el-input
v-model="blocklyConsoleMessage"
:disabled="true"
type="textarea"
:rows="39"
class="el-input-blockly-console"
/>
</div>
</el-col>
</el-row>
</div>
</template>

  样式美化:

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style scoped>
#visualProgram .el-input-blockly-code /deep/ .el-textarea__inner{
color: black;
background-color: white
}
#visualProgram .el-input-blockly-console /deep/ .el-textarea__inner{
color: black;
background-color: white
}
</style>

<style>
.div-blocklyDiv{
width:800px;
height:850px;
margin-top: 20px;
margin-left: 20px;
position: relative;
border: 2px solid #008B93;
}
.div-run-code{
position: absolute;
right: 300px;
bottom: 800px;
z-index: 2;
}
.div-blockly-code{
margin-top: 20px;
height:850px;
color: #008B93;
border: 2px solid #008B93;
}
.div-blockly-console{
margin-top: 20px;
margin-right: 10px;
height:850px;
border: 2px solid #008B93;
}
.el-input-blockly-code{
width: 99%;
margin: 2px;
border: 1px solid #008B93;
}
.el-input-blockly-console{
width: 99%;
margin: 2px;
border: 1px solid #008B93;
}
</style>

  页面主要分为三部分,即可视化编程区,代码显示区和代码控制台区。

逻辑功能

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
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
<script>
// https://github.com/bplok20010/eval5
import { Interpreter } from 'eval5'

import * as myblock from '@/utils/blocktools/myblocks'

// 引入Blockly
import Blockly from 'blockly'
// 引入想要转换的语言,语言有php python dart lua javascript
import 'blockly/javascript'
import 'blockly/python'
// 引入语言包并使用
import * as hans from 'blockly/msg/zh-hans'
Blockly.setLocale(hans)

export default {
name: 'HelloWorld',
data() {
return {
workspace: null,
blocklyCodeMessage: '',
blocklyConsoleMessage: ''
}
},
mounted() {
this.initBlockly()
myblock.initMyBlockly()

window.openGrasp = this.openGrasp
},
created() {
},
beforeDestroy() {
},
methods: {
initBlockly() {
this.workspace = myblock.initBlockly('blocklyDiv', 'toolbox')
// 工作区监听代码生成器
this.workspace.addChangeListener(this.myUpdateFunction)
var toolbox = Blockly.getMainWorkspace().getToolbox()
},
// 代码生成器
myUpdateFunction(event) {
var codeJs = Blockly.JavaScript.workspaceToCode(this.workspace)
this.blocklyCodeMessage = codeJs
},
runJavascriptCode() {
// const interpreter = new Interpreter(window, {
// timeout: 1000
// })
const interpreter = new Interpreter(window)
var jsCode = Blockly.JavaScript.workspaceToCode(this.workspace)
// console.log(jsCode)

try {
interpreter.evaluate(jsCode)
} catch (e) {
this.blocklyConsoleMessage += e + '\n'
// console.info('执行代码错误', e)
}
},
openGrasp(value) {
// this.$message('openGrasp: ' + value)
window.alert(value)
}
}
}
</script>

  首先初始化Blockly,然后设置监听代码生成函数,当需要执行代码时,调用eval5evaluate()进行解释运行。

谢谢老板!
-------------本文结束感谢您的阅读给个五星好评吧~~-------------