Blockly 的配置

2023-10-29

本文基于Web Blockly,整理一下可视化编程工具-Blockly 的常用配置,包括:工作区配置、添加自定义块、配置工具箱、配置代码生成器等。

  1. 固定尺寸工作区
  2. 可调尺寸工作区
  3. 添加自定义块(Block
  4. 工具箱(Toolbox)配置
  5. 代码生成器
  6. 网格(Grid
  7. 缩放(Zoom
  8. 事件
  9. 云存储

1. 固定尺寸工作区

把 Blockly 放到网页上最简单的方法是将其注入到一个空div中。

1.1 引入 Blockly 脚本

首先,引入 Blockly 脚本及核心“块”:

<script src="blockly_compressed.js"></script>
<script src="blocks_compressed.js"></script>

注意:实际路径可能与上面不同,请按 Blockly 的实际位置调整上面路径


1.2 引入语言文件

引入包含用户语言的消息定义(本例中使用了英语):

<script src="msg/js/en.js"></script>


1.3 确定引入位置

在页面创建一个空div元素,并设置其尺寸:

<div id="blocklyDiv" style="height: 480px; width: 600px;"></div>


1.4 添加工具栏

在页面任意位置定义好“工具栏”结构:

</xml id="toolbox" style="display: none">
  </block type="controls_if"></block>
  </block type="controls_repeat_ext"></block>
  </block type="logic_compare"></block>
  </block type="math_number"></block>
  </block type="math_arithmetic"></block>
  </block type="text"></block>
  </block type="text_print"></block>
</xml>


1.5 初始化

最后,在页面底部(</page>之前)创建调用脚本,完成 Blockly 的初始化:

<script>
  var workspace = Blockly.inject('blocklyDiv',
      {toolbox: document.getElementById('toolbox')});
</script>

workspace变量当前并不会使用,但当需要保存“块”或生成代码时,它会变得非常重要。如果有多个 Blockly 实例注入到同一个页面时,应保存其返回的工作区保存在不同的变更中。


2. 可调尺寸工作区

一个好的Web应用将调整 Blockly 的大小以填充屏幕的可用空间,而不是将其固定成一个尺寸。以下演示了一种创建可调尺寸的 Blockly 的方法,这本示例中只需简单的三步,就可以创建一个非固定尺寸的工作区:


2.1 定义区域

使用HTML的table元素或div及CSS,创建一个空区域,并确保该区域有一个唯一的ID(本例中为blocklyArea

一个在线示例,在页底部使用table进行定义


2.2 注入

与创建固定尺寸工作区一样引入 Blockly,添加脚本、blocklyDiv元素、工具栏、及初始化脚本。


2.3 定位

最后一步是将blocklyDiv元素定位到blocklyArea元素上。这样,就需要移除blocklyDiv元素的heightwidth样式,并添加绝对定位:

<div id="blocklyDiv" style="position: absolute"></div<

然后用一个同样位于blocklyDiv之上的blocklyArea取代注入脚本:

<script>
  var blocklyArea = document.getElementById('blocklyArea');
  var blocklyDiv = document.getElementById('blocklyDiv');
  var workspace = Blockly.inject(blocklyDiv,
      {toolbox: document.getElementById('toolbox')});
  var onresize = function(e) {
    // Compute the absolute coordinates and dimensions of blocklyArea.
    var element = blocklyArea;
    var x = 0;
    var y = 0;
    do {
      x += element.offsetLeft;
      y += element.offsetTop;
      element = element.offsetParent;
    } while (element);
    // Position blocklyDiv over blocklyArea.
    blocklyDiv.style.left = x + 'px';
    blocklyDiv.style.top = y + 'px';
    blocklyDiv.style.width = blocklyArea.offsetWidth + 'px';
    blocklyDiv.style.height = blocklyArea.offsetHeight + 'px';
  };
  window.addEventListener('resize', onresize, false);
  onresize();
  Blockly.svgResize(workspace);
</script>


3. 添加自定义块(Block

虽然 Blockly 已经定义了大量的标准块,但大多数应用仍然需要定义和实现一些自己业务相关的“块”

“块”有三个组件构成:

  • 块(Block)定义对象 - 定义块的外观和行为,包括文本、颜色、字段和连接
  • 工具箱(Toolbox引用 - 工具箱XML中对块类型的引用,这样用户才能将其添加到工作区中
  • 生成器函数 - 用于生成块的代码字符串。该函数总是使用JavaScript编写,虽然目标语言可能不是JavaScrpit,基至运行环境也可能不是Web


3.1 块定义

Blockly 通过页面中脚本文件加载“块”,在blocks/目录下包含了几个标准块示例。如果需要创建一个新的块,就需要创建个包含块定义的脚本文件,并将其添加到引用页的<script ...<标签中。

一个块定义类型如下:

JSON定义格式:

Blockly.Blocks['string_length'] = {
  init: function() {
    this.jsonInit({
      "message0": 'length of %1',
      "args0": [
        {
          "type": "input_value",
          "name": "VALUE",
          "check": "String"
        }
      ],
      "output": "Number",
      "colour": 160,
      "tooltip": "Returns number of letters in the provided text.",
      "helpUrl": "http://www.w3schools.com/jsref/jsref_length_string.asp"
    });
  }
};

JavaScript定义格式:

Blockly.Blocks['string_length'] = {
  init: function() {
    this.appendValueInput('VALUE')
        .setCheck('String')
        .appendField('length of');
    this.setOutput(true, 'Number');
    this.setColour(160);
    this.setTooltip('Returns number of letters in the provided text.');
    this.setHelpUrl('http://www.w3schools.com/jsref/jsref_length_string.asp');
  }
};

以上两咱定义形式是等价的。其中:

  • string_length - 所定义的“块”的名称。由于所有的块会共享一个命名空间,所以可以使用使用一个类别名来区分分类(本例中是string),拉下来是块的功能定义(本例中是length
  • init - 这个函数用于定义块外观、形状等

上面模块完成定义后,效果如下:

更多关于块定义,请参考:块自定


3.2 添加工具箱引用

完成定义后,需要使用类型名,将其添加到工具箱中:

<xml id="toolbox" style="display: none">
  <category name="Text">
    <block type="string_length"></block>
  </category>
  ...
</xml>


3.3 添加生成器函数

最后,要将块解析成代码,需要定义一个与之对应的生成器函数。生成器函数由于生成语言的不同也会有所不同,标准的生成器函数格式如下:

Blockly.JavaScript['text_length'] = function(block) {
  // String or array length.
  var argument0 = Blockly.JavaScript.valueToCode(block, 'VALUE',
      Blockly.JavaScript.ORDER_FUNCTION_CALL) || '\'\'';
  return [argument0 + '.length', Blockly.JavaScript.ORDER_MEMBER];
};

生成器函数会引用块,以对其进行处理。其输入(如上面的VALUE)的是代码字符串,然后会将其链接到一个更大的表达式中。


4. 工具箱(Toolbox)配置

工具箱是用户可以创建新“块”的侧边菜单。工具箱的结构使用XML来指定,它可以一个节点树,也可以是字符串的形式。所定义的XML会在 Blockly 注入到页面中时传递给它。除了手工输入XML外,还可以使用Blockly Developer Tools来自动生成。


4.1 工具箱定义

以下是一个最小化的工具箱定义示例,在示例中使用了节点定义形式:

<xml id="toolbox" style="display: none">
  <block type="controls_if"></block>
  <block type="controls_whileUntil"></block>
</xml>
<script>
  var workspace = Blockly.inject('blocklyDiv',
      {toolbox: document.getElementById('toolbox')});
</script>

下面定义是字符串定义形式,两种方式是等价的:

<script>
  var toolbox = '<xml>';
  toolbox += '  <block type="controls_if"></block>';
  toolbox += '  <block type="controls_whileUntil"></block>';
  toolbox += '</xml>';
  var workspace = Blockly.inject('blocklyDiv', {toolbox: toolbox});
</script>

两种定义方式,都会生成如下工具箱:


4.2 类别

工具箱中的模块可以类别进行组织。以下示例中,包含了'Control''Logic'两个分类:

<xml id="toolbox" style="display: none">
  <category name="Control">
    <block type="controls_if"></block>
    <block type="controls_whileUntil"></block>
    <block type="controls_for">
  </category>
  <category name="Logic">
    <block type="logic_compare"></block>
    <block type="logic_operation"></block>
    <block type="logic_boolean"></block>
  </category>
</xml>

完成定义后,其效果如下:

“分类”表示会改变 Blockly 的UI,以支持更大的应用。而“滚动条”的出现,就提供了一个无限大的工作空间。其它的,如:“垃圾桶”、“上下文菜单”等,都可以通过配置选项来设置。

定义分类时,可以通过一个colour属性来指定该分类的颜色。颜色使用0~360之间的数字表示:

<xml id="toolbox" style="display: none">
  <category name="Logic" colour="210">...</category>
  <category name="Loops" colour="120">...</category>
  <category name="Math" colour="230">...</category>
>
  <category name="Variables" colour="330" custom="VARIABLE"></category>
  <category name="Functions" colour="290" custom="PROCEDURE"></category>
</xml>

设置颜色后,工具栏效果如下:


4.3 动态类别

以下有两个分类'Variables''Functions'用于指定形为,分类里面并没有内容,但定义分类时添加了'custom'属性,值可以是'VARIABLE''PROCEDURE'。这些分类,将使用适合的“块”自动填充:

<category name="Variables" custom="VARIABLE"></category>
<category name="Functions" custom="PROCEDURE"></category>

开发者还可以使用custom属性来创建动态填充弹出类别。例如,要创建一个自定义的弹出颜色块:

  • 创建自定义属性:
    <category name="Colours" custom="COLOUR_PALETTE"></category>
  • 定义提供给分类内容的回调。这个回调应该有一个工作区并返回一个XML块元素数组:
    /**
     * Construct the blocks required by the flyout for the colours category.
     * @param {!Blockly.Workspace} workspace The workspace this flyout is for.
     * @return {!Array.<!Element>} Array of XML block elements.
     */
    myApplication.coloursFlyoutCallback = function(workspace) {
      // Returns an array of hex colours, e.g. ['#4286f4', '#ef0447']
      var colourList = myApplication.getPalette();
      var xmlList = [];
      if (Blockly.Blocks['colour_picker']) {
        for (var i = 0; i < colourList.length; i++) {
          var blockText = '<xml>' +
              '<block type="colour_picker">' +
              '<field name="COLOUR">' + colourList[i] + '</field>' +
              '</block>' +
              '</xml>';
          var block = Blockly.Xml.textToDom(blockText).firstChild;
          xmlList.push(block);
        }
      }
      return xmlList;
    };
  • 在工作区注册回调:
    myWorkspace.registerToolboxCategoryCallback(
      'COLOUR_PALETTE', myApplication.coloursFlyoutCallback);


4.4 类别树

类别可以嵌套在其它类别中。如下所示,有两个顶级类别'Core''Custom',每个类别中又包含了两个子类别:

<xml id="toolbox" style="display: none">
  <category name="Core">
    <category name="Control">
      <block type="controls_if"></block>
      <block type="controls_whileUntil"></block>
    </category>
    <category name="Logic">
      <block type="logic_compare"></block>
      <block type="logic_operation"></block>
      <block type="logic_boolean"></block>
    </category>
  </category>
  <category name="Custom">
    <block type="start"></block>
    <category name="Move">
      <block type="move_forward"></block>
      <block type="move_backward"></block>
    </category>
    <category name="Turn">
      <block type="turn_left"></block>
      <block type="turn_right"></block>
    </category>
  </category>
</xml>


4.5 块分组

XML可以包含自定义块或块分组。现在有以下4个块:

  1. 一个logic_boolean块:

  2. 一个math_number块,用于修改时显示42而不是0:

  3. 一个controls_for块,其中包括了3个math_number块:

  4. 一个math_arithmetic块,其中包括了两个math_number“阴影块”:

以下工具栏中包括了上面4个块:

<xml id="toolbox" style="display: none">
  <block type="logic_boolean"></block>

  <block type="math_number">
    <field name="NUM">42</field>
  </block>

  <block type="controls_for">
    <value name="FROM">
      <block type="math_number">
        <field name="NUM">1</field>
      </block>
    </value>
    <value name="TO">
      <block type="math_number">
        <field name="NUM">10</field>
      </block>
    </value>
    <value name="BY">
      <block type="math_number">
        <field name="NUM">1</field>
      </block>
    </value>
  </block>

  <block type="math_arithmetic">
    <field name="OP">ADD</field>
    <value name="A">
      <shadow type="math_number">
        <field name="NUM">1</field>
      </shadow>
    </value>
    <value name="B">
      <shadow type="math_number">
        <field name="NUM">1</field>
      </shadow>
    </value>
  </block>
</xml>

这些定制块或组的XML与Blockly的XML存储格式相同。因此,为此类块构造XML的最简单方法是使用Code application来构建块,然后切换到XML选项卡并复制结果。

“阴影块”是执行几个函数的占位符块,它们具有以下特征:

  • 它们表示其父块的默认值
  • 允许用户直接输入值,而不需要获取一个数字或字符串块。
  • 与普通块不同,如果用户在其上删除一个块,则它们将被替换
  • 它们会提示用户期望的值类型

阴影块不能直接用Code application构造,但可以通过替换普通块的XML中的<block ...></block><shadow ...></shadow>来构造。


4.6 阴影块

阴影块是占位符块,它们可执行多种功能:

  • 指示其父块的默认值
  • 允许用户直接输入值,而不需要数字或字符串块
  • 它们与常规块不同,如果用户在其上放置块,则会被替换
  • 它们会通知用户预期的值类型

阴影块无法直接使用代码构建。但可以使用常规块,然后将XML中的<block ...>和</block>更改为<shadow ...>和</shadow>。


4.7 分隔器

添加一个<sep></sep>标签到任意两个分类之间,将会创建一个“分隔器”。

默认情况下,两个相邻块之间的分隔间隔为24像素。可以通过修改sep标签的gap属性来调整距离:

<xml id="toolbox" style="display: none">
  <block type="math_number"></block>
  <sep gap="32"></sep>
  <block type="math_arithmetic">
    <field name="OP">ADD</field>
  </block>
  <sep gap="8"></sep>
  <block type="math_arithmetic">
    <field name="OP">MINUS</field>
  </block>
</xml>

通过调整块之间间隙,就可以在工具箱中创建逻辑块组:


4.8 按钮与标签

就像“块”一样,你也可以将一个按钮或标签放在工具栏的任何位置:

<xml id="toolbox" style="display: none">
  <block type="logic_operation"></block>
  <label text="A label" web-class="myLabelStyle"></label>
  <label text="Another label"></label>
  <block type="logic_negate"></block>
  <button text="A button" callbackKey="myFirstButtonPressed"></button>
  <block type="logic_boolean"></block>
</xml>

<style>
.myLabelStyle>.blocklyFlyoutLabelText {
  font-style: italic;
  fill: green;
}
</style>

还可以对按钮和标签使用CSS样式。在上面示例中,第一个label标签使用了自定义样式,而button则使用了默认样式。

按钮需要回调一个函数,但标签不用。设置按钮点击时的回调函数:

yourWorkspace.registerButtonCallback(yourCallbackKey, yourFunction).


4.9 禁用

工具箱中的“块”,可以在XML中通过disabled属性将其禁用:

<xml id="toolbox" style="display: none">
  <block type="math_number"></block>
  <block type="math_arithmetic"></block>
  <block type="math_single" disabled="true"></block>
</xml>

禁用块可用于限制用户的选择。这可以用于用户完成某些成就后解锁块的场景:


4.10 Toolbox的修改

应用可以在任何时候通过调用以下函数来修改工具箱:

workspace.updateToolbox(newTree);

在初始配置下,newTree可能是一个节点树或字符串。唯一的限制就是不能修改模式,也就是说在初始工具箱中定义的类别,那么新工具箱也必须有;同样的,初始工具箱中没有的类别,在新工具箱中也不能有。

别外,更新工具栏可能会导致一些较小的UI重置:

  • 在一个工具箱类别,“弹出”如果当前是开启状态,那么会关闭
  • 在没有类别的工具箱中,用户更改的任何字段(如下拉列表)都将恢复到默认值
  • 任何超长工具箱,如果超出了页面,其滚动条会跳到顶端


5. 代码生成器

大多数 Blockly 应用都需要将用户程序转换为JavaScript、Python、PHP、Lua、Dart或其它语言,这一转换过程是由 Blockly 客户端完成的。

5.1 生成代码

首先,引入所需生成语言的生成器。Blockly 中包含了以下生成器:

生成器类需要在blockly_compressed.js之后引入。如:

<script src="blockly_compressed.js"></script>
<script src="javascript_compressed.js"></script>

应用调用时,用户块可以随时从应用中导出到代码:

var code = Blockly.JavaScript.workspaceToCode(workspace);

将以上代码中的JavaScript替换为PythonPHPLua、或Dart就可以生成相应的代码。


5.2 实时生成

生成的操作非常快,所以频繁调用生成函数也不会有问题。这样就可以通过添加 Blockly 事件监听来实时生成代码:

function myUpdateFunction(event) {
  var code = Blockly.JavaScript.workspaceToCode(workspace);
  document.getElementById('textarea').value = code;
}
workspace.addChangeListener(myUpdateFunction);


6. 网格(Grid

6.1 使用网格

Blockly 的主工作区可以有一个网格。而网格可以对块进行分隔,从而实现更整洁的布局。当工作区较大时,这会非常有用。

注入 Blockly 时,可以在其配置选项中启用网格:

var workspace = Blockly.inject('blocklyDiv',
    {toolbox: document.getElementById('toolbox'),
     grid:
         {spacing: 20,
          length: 3,
          colour: '#ccc',
          snap: true},
     trashcan: true});


6.2 网格配置参数

Spacing

网格最重要的配置项就是spacing,它定义了网格中点的距离。其默认值是0,其结果是不会有网格。以下演示了分别设置为10、20、40时的效果:

Length

length是定义网格端点形状的数字。长度为0的结果是一个看不见的网格,长度为1(默认值)会是点,一个更长的长度会导致交叉,长度等于或大于spacing时将没有间隔。下面是将length分别设置为1、5和20的示例:

Colour

colour属性定义了网格端点的颜色,可以使用任何与CSS兼容的格式,如:#f00#ff0000rgb(255, 0, 0),其默认值是#888

以下为将colour分别设置为#000#ccc#f00的效果:

Snap

snap属性是一个布尔值,用于设置当放置在工作空间时块是否应该锁定到最近的网格点。其默认值为false


7. 缩放(Zoom

7.1 使用缩放

Blockly 的主工作区大小是可调的,其大小可以由用户动态控制,或由开发者设置为静态的。

zoom是 Blocly 的初始化选项之一:

var workspace = Blockly.inject('blocklyDiv',
    {toolbox: document.getElementById('toolbox'),
     zoom:
         {controls: true,
          wheel: true,
          startScale: 1.0,
          maxScale: 3,
          minScale: 0.3,
          scaleSpeed: 1.2},
     trashcan: true});


7.2 缩放配置参数

controls

设置为true时,会显示zoom-centre、zoom-in、and zoom-out三个按钮,默认为false

wheel

设置为true时允许鼠标滚轮缩放,默认为false

startScale

初始放大基数。对于多层应用来说,startScale会在第一层设置一个高级值,这样在子层就可以根据这个值进行更复杂的缩放。默认为1.0

maxScale

最大可放大倍数,默认为3

minScale

最小可缩小倍数,默认为0.3

scaleSpeed

每次放大或缩小时,维放速度比。即:scale = scaleSpeed ^ steps。注意,缩小时使用减,而放大时使用加。默认为1.2


8. 事件

工作区上的每个更改都会触发事件。这些事件完全描述了每次更改前后的状态。


8.1 事件监听

工作区对象(workspace)中有addChangeListenerremoveChangeListener两个方法,可用于监听事件流。

在以下示例中,会检测用户创建的第一条注释,然后发出警报,并停止监听,从而不进一步触发警报:

function onFirstComment(event) {
  if (event.type == Blockly.Events.CHANGE &&
      event.element == 'comment' &&
      !event.oldValue && event.newValue) {
    alert('Congratulations on creating your first comment!')
    workspace.removeChangeListener(onFirstComment);
  }
}
workspace.addChangeListener(onFirstComment);

此外,Blockly 还提供了另一种监听事件流的方法,可以在每个“块”中定义一个'onchange'函数,该函数会在块发生变化时被调用。

8.2 事件类型

所有事件都具有以下共同属性:

  • type - string。Blockly.Events.CREATEBlockly.Events.DELETEBlockly.Events.CHANGEBlockly.Events.MOVEBlockly.Events.UI之一
  • workspaceId - string。工作区UUID。工作区可以通过Blockly.Workspace.getById(event.workspaceId)找到
  • blockId - string。块的UUID。块可以通过workspace.getBlockById(event.blockId)找到
  • group - string。UUID 分组。有些事件是不可分割的组中的一部分。例如,在堆栈中插入语句

Blockly.Events.CREATE

Create事件,其有两个附加属性:

  • xml - object。定义新块及任何连接子块的XML
  • ids - array。包含新块及任何连接子块Id的数组

Blockly.Events.DELETE

Delete事件,其有两个附加属性:

  • oldXml - object。所删除块及任何连接子块的XML
  • ids - array。包含所删除块及任何连接子块Id的数组

Blockly.Events.CHANGE

Change事件,其有4个附加属性:

  • element - string。'field''comment''collapsed''disabled''inline''mutate'之一
  • name - string。所更改字段的名称
  • oldValue - value。原始值
  • newValue - value。修改后的值

Blockly.Events.MOVE

Move事件,其有以下6个附加属性:

  • oldParentId - string。其旧父块的UUID,当为顶级块时为Undefined
  • oldInputName - string。旧输入值,当为顶级块时为Undefined
  • oldCoordinate - object。当为顶级块时,则为X和Y坐标;否则为Undefined
  • newParentId - string。其新父块的UUID,当为顶级块时为Undefined
  • newInputName - string。新输入值,当为顶级块时为Undefined
  • newCoordinate - object。当为顶级块时,则为X和Y坐标;否则为Undefined

Blockly.Events.UI

UI事件,其有以下3个附加属性:

  • element - string。'selected''category''click''commentOpen''mutatorOpen''warningOpen'值之一
  • oldValue - value。原始值
  • newValue - value。修改后的值


9. 云存储

你的应用是托管在云端的,那么你可以使用 Blockly 的云存储功能,并利用这一服务的优势来保存、加载、分享、或发布你的程序。


9.1 设置应用引擎

首先,需要将 Blockly 发布到应用引擎上:

  1. 下载并安装Python SDK
  2. 登录Google App Engine并创建应用
  3. 编辑appengine/app.yaml,并将blockly-demo中的应用Id修改为上一步所创建的Id
  4. 复制(或软链接)以下文件及目录到appengine/static/
    • demos/
    • msg/
    • media/
    • *_compressed.js
  5. 可选:如果你需要在服务器上使用blockly_uncompressed.js ,则同样需要将其复制到appengine/static/。复制coreappengine/static/,并复制closure-library/到上级目录appengine/
  6. 可选:如果需要运行 Blockly Playground,那需要像第5步一样,复制blocksgeneratorstests几个目录到指定位置
  7. 运行 Google App Engine Launcher,并添加你的appengine,然后点击“Deploy”按钮。如果习惯使用命令行,那么执行:appcfg.py --oauth2 update appengine/

上传 Blockly 后,就可以在浏览器中输入步骤2中创建的URL。然后就能到看到Demo列表,包括云存储Demo。


9.2 云端通讯

云存储Demodemos/storage/index.html文件中有以下特点。

首先,有一个加载云存储API的脚本:

<script src="/storage.js"></script>

请注意,该脚本假设只有页面上只有一个 Blockly 工作区。还有下面这些消息定义,应该根据你需要修改这些定义:

BlocklyStorage.HTTPREQUEST_ERROR = 'There was a problem with the request.\n';
BlocklyStorage.LINK_ALERT = 'Share your blocks with this link:\n\n%1';
BlocklyStorage.HASH_ERROR = 'Sorry, "%1" doesn\'t correspond with any saved Blockly file.';
BlocklyStorage.XML_ERROR = 'Could not load your saved file.\n' +
    'Perhaps it was created with a different version of Blockly?';

将这些消息解析成其它语言的示例,可以参考 Blockly Games 中的json目录

保存当前块调用BlocklyStorage.link()即可:

<button οnclick="BlocklyStorage.link()">Save Blocks</button>

在页面加载时重新保存,仅需在 Blockly 注入页面,通过根据URL中的Hash调用BlocklyStorage.retrieveXml

if ('BlocklyStorage' in window && window.location.hash.length > 1) {
  BlocklyStorage.retrieveXml(window.location.hash.substring(1));
}


9.3 本地存储

storage.jsAPI 还有浏览器本地存储能力。这可以用于替代云存储,或二者结合使用。

从本地存储中恢复块,只需要在 Blockly 注入后超时调用BlocklyStorage.restoreBlocks即可:

window.setTimeout(BlocklyStorage.restoreBlocks, 0);

在用户离开时自动备份到本地存储,可调用BlocklyStorage.backupOnUnload实现,且其可以通过监听页面的unload事件来自动调用:

BlocklyStorage.backupOnUnload();
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Blockly 的配置 的相关文章

  • Deeplabcut----(1)新建自己的训练(单只动物)

    Deeplabcut新建自己的训练 本教程最后使用Autodl云平台跑代码 本地电脑上只是进行数据标注 建议直接新建多动物训练 比单动物的项目能干的事多 也能对单动物进行预测 个人使用感觉还是mmpose预测新视频和图片的正确率更高 整个文
  • 阿里云服务器安装及部署canal

    阿里云服务器安装及部署canal 1 环境部署 1 1 mysql开启binlog模式 1 查看当前mysql是否开启binlog模式 SHOW VARIABLES LIKE log bin 如果log bin的值为OFF是未开启 为ON是
  • 多线程学习十九:生产者消费者

    异步模式之生产者 消费者 定义 与前面的保护性暂停中的 GuardObject 不同 不需要产生结果和消费结果的线程一一对应 消费队列可以用来平衡生产和消费的线程资源 生产者仅负责产生结果数据 不关心数据该如何处理 而消费者专心处理结果数据
  • Pandoc 实用教程

    Pandoc 是一种将各类文件互相转换的工具 例如 docx html markdown epub pdf 等 在一些 markdown 写作工具中 它常被用作导出工具的一种 Pandoc 的安装 安装方法有很多种 方案一 直接通过 Pan
  • CentOS6系统中在/etc/rc.local添加开机自启动项启动失败

    CentOS6系统中在 etc rc local添加开机自启动项启动失败 应项目要求需要在开机的时候启动自己的Redis程序 想当然的直接就往 etc rc local当中添加启动命令 结果重启之后发现什么都没有发生 一开始还以为是配置的问
  • SQL增删改查语句学习

    删除语句 语法 DELETE FROM 表名 WHERE 条件 例如 DELETE FROM studentchose WHERE sc id 202046 DELETE FROM studentchose WHERE sc id 2020
  • 基于springboot的校园疫情防控系统【毕业设计,源码,论文】

    想要源码或其他毕设项目 可以私信 摘 要 随着信息技术和网络技术的飞速发展 人类已进入全新信息化时代 传统管理技术已无法高效 便捷地管理信息 为了迎合时代需求 优化管理效率 各种各样的管理系统应运而生 各行各业相继进入信息管理时代 校园疫情
  • Linux下远程git服务器拉取代码发布jar包脚本

    一 yum安装 在Linux上是有yum安装Git 非常简单 只需要一行命令 yum y install git 二 maven安装 参考https www jianshu com p 51e4e84e02cd 三 编写脚本 脚本步骤如下
  • Echarts 地图使用,以及tooltip轮播使用

    一 首先下载Echarts npm install echarts save 二 引入Echarts 全局引入main js中 echarts 4 0 import echarts from echarts Vue prototype ec
  • sql注入-union select

    什么是SQL注入 SQL注入 Sql Injection 是一种将SQL语句插入或添加到应用 用户 的输入参数中的攻击 这些参数传递给后台的SQL数据库服务器加以解析并执行 哪里存在SQL注入 GET POST HTTP头部注入 Cooki
  • 第一次使用Arduino IDE(mac os) 配置合宙ESP32C3(9.9包邮)且烧录代码的历程

    目录 Arduino 配置ESP32 1 Arduino 请更新至最新版 2 科学上网 3 添加开发板管理URL 配置 1 连接开发板 2 Arduino IDE 的配置 3 烧录代码 Arduino 配置ESP32 1 Arduino 请
  • java实现pdf上传、下载、在线预览、删除、修改等功能

    资源下载 pdf上传 下载 在线预览 删除 修改功能源码 最近闲来无事 做了一个pdf的小功能 以供各位大神参考 下面是效果展示图 功能主页 点击上传PDF按钮 上传文件之后 在线预览 开发环境 jdk 1 8 mysql 5 7 开发工具
  • 蓝牙 bluetooth-之一

    蓝牙profile的作用 蓝牙子系统应用程序的交互通过蓝牙profile实现 profile有些文献将其解释为子协议 似乎不是很准确 我依然以profile称呼它 蓝牙profile定义了蓝牙子系统分层结构中的每一层需要具有的功能和特性 G

随机推荐

  • loss.backward() Segmentation fault 段错误

    在运行一个非常简单的深度学习程序的时候 发现运行一段时间会报错 段错误 经过定位发现是执行loss backward 的时候出现的问题 而源码明显是没有什么问题的 具体排查可以这样 gdb args python train py 然后发现
  • CDI(Weld)基础<2> JSF+CDI+JPA项目示例

    2019独角兽企业重金招聘Python工程师标准 gt gt gt CDI可以理解为Spring 但其中的一些功能比spring更强大 灵活 本章是个简单的项目示例 推荐有一定基础的看 1 JPA定义 MVC M Entity public
  • dom型xss ---(waf绕过)

    目录 1 漏洞源码 2 进行绕过 2 1 使用老方法进行绕过 注入失败原因 2 2 分析注入方式 2 3 使用svg进行绕过 方式一 2 3 1 了解什么是dom树 以及dom树的构建 2 3 3 分析img方法失败的原因 可以在页面上添加
  • 二阶段提交java_分布式事务(一)两阶段提交及JTA

    分布式事务 分布式事务简介 分布式事务是指会涉及到操作多个数据库 或者提供事务语义的系统 如JMS 的事务 其实就是将对同一数据库事务的概念扩大到了对多个数据库的事务 目的是为了保证分布式系统中事务操作的原子性 分布式事务处理的关键是必须有
  • 【原生js实现】吃掉病毒,还森林一片祥和

    嗨 大家好 我是法医 一只治疗系前端码猿 与代码对话 倾听它们心底的呼声 期待着大家的点赞 与关注 游戏开始前 请容我感叹一声 掘金活动真多鸭 快卷不过来了 哈哈 其实最近也一直在加班 眼看游戏投稿时间快过去了 当初真想做个小游戏出来参加活
  • 基础编程练习 7-26 单词长度 (15 分)

    这个题的测试用例只卡在了空句子那一个 题目没有明确给出只有一个 的时候 什么也不输出直接结束 C include
  • sublime text中换行符替换成空(mac版)

    sublime text做字符串处理 需要将 xxx xxx xxx 改造为 xxx xxx xxx 1 alt command f 调用替换界面 2 shift command enter 在find what 输入 换行符 3 repl
  • Sqlite 嵌入式数据库移植和使用

    1 源代码的获取 sqlite是一个功能强大 体积小运算速度快的嵌入式数据库 采用了全C语言封装 并提供了八十多个命令接口 可移植性强 使用方便 下载地址 http sqlite org download html sqlite源代码 sq
  • 【Educoder python 作业答案】国防科技大学《大学计算机基础》Python入门-绘制炸弹轨迹 ※

    Educoder python 作业答案 国防科技大学 大学计算机基础 Python入门 绘制炸弹轨迹 第1关 绘制一个坐标点 第2关 绘制n个坐标点 第3关 绘制一条轨迹 第4关 更简单的绘制一条轨迹 第5关 绘制多条轨迹 第1关 绘制一
  • 接口传参时,不写字段,这种格式http://localhost:9000/findData/1 取参

    GetMapping findData id public List
  • 7.GDB与文件IO

    1 GDB 什么是 GDB 调试 1 1 GDB 准备工作 gdb 是一个 shell 指令 必须带有 g 的参数 程序才将调试信息添加到文件中 g g a cpp o a out 先为文件添加调试信息 打开所有的 warning 选项 g
  • C++的类继承与类模板

    类继承是面向对象编程中很重要 也是很难 的内容 其能有效地提高代码复用水平 提高开发的效率 目录 基本概念 公有继承 私有继承 保护继承 包含 多重继承 类模板 基本概念 继承的种类与特点 C 中提供了几种继承 分别为公有继承 public
  • 除自身以外数组的乘积

    目录 1 题目描述 2 题目分析 3 代码实现 1 题目描述 给定长度为 n 的整数数组 nums 其中 n gt 1 返回输出数组 output 其中 output i 等于 nums 中除 nums i 之外其余各元素的乘积 示例 输入
  • 如何选择期权品种

    有朋友问 国内的 疫情目前得到较好的控制 经济也在逐渐恢复运行 国外的疫情在加重 也许会有部分国家经济做阶段停摆 假如上面的成立 我们是不是可以选择一种套利 买入国内经济需求会带动上涨的商品 卖出国外经济下滑会带动下跌的商品 如果这个方案可
  • webpack

    一 是什么 webpack proxy 即webpack提供的代理服务 基本行为就是接收客户端发送的请求后转发给其他服务器 其目的是为了便于开发者在开发模式下解决跨域问题 浏览器安全策略限制 想要实现代理首先需要一个中间服务器 webpac
  • 城市空间数字化浪潮中,万物云以“蝶城”迎“蝶变”

    3月27日 万物云空间科技服务股份有限公司 以下简称 万物云 02602 HK 公布了上市首份年度业绩报告 营收大幅增长 毛利稳定 蝶城模式初步得到验证 财报显示 2022年 万物云营收301亿元 同比增长27 0 毛利为42 31亿元 同
  • 50 Kick-Ass Websites You Need to Know About

    http www maximumpc com 50 kick ass websites you need to know about page 1 It s time to update the entries in your browse
  • 铨顺宏RFID:电子汽车车牌系统具有RFID识别功能

    大城市的车辆数目持续提升 车辆管理方法一直是城市公共交通中的一个关键步骤 RFID系统对车辆开展合理的管理方法 迅速 全自动地鉴别车辆的真实身份 已变成车辆管理方法的关键方式 智能化电子车牌号融合了一般车牌号自动检索和收集的特性 融合RFI
  • 投递简历2500份,46次面试,只拿到了1个offer,IT行业怎么了?

    7月14日 一位被Meta解雇的PM 产品经理 在网上发布的一段文字 标题为 2500 Job apps 46 interviews 1 offer 就这件事以及CSDN发布的 2023中国开发者调查报告 和香帅的 2023年度演讲 聊聊目
  • Blockly 的配置

    本文基于Web Blockly 整理一下可视化编程工具 Blockly 的常用配置 包括 工作区配置 添加自定义块 配置工具箱 配置代码生成器等 固定尺寸工作区 可调尺寸工作区 添加自定义块 Block 工具箱 Toolbox 配置 代码生