跳转至

Mind+ Python 积木模式扩展开发手册

本文档将指导开发者创建适用于 Mind+ Python 积木模式的扩展。

1. 扩展包文件结构

一个标准的扩展包目录结构如下:

extensionName/                  # 扩展库根目录
├── func.js                     # 生成代码 (原 opcodeFunc.js)
├── index.js                    # 图形块定义主文件
├── public/                     # 静态资源目录 (原 template/)
│   ├── cover.png               # 扩展封面图
│   ├── config.json             # 扩展配置文件(元数据)
│   ├── libraries/              # Python 扩展库文件目录 (可选)
│   │   └── df_xxx_lib.py       # Python 库文件
│   └── requirements.txt        # Python 项目依赖包 (可选)
├── icon/                       # 图标资源目录
│   ├── blockIcon.svg           # 积木图标
│   └── menuIcon.svg            # 分类菜单图标
├── locales/                    # 国际化语言包
│   ├── zh-cn.json              # 简体中文翻译
│   ├── en.json                 # 英文翻译
│   └── index.js                # 翻译入口文件

2. 配置文件 (public/config.json)

config.json 是扩展的核心配置文件。

示例配置

{
    "id": "pinpongBase",
    "version": "0.0.1",
    "name": {
        "zh-cn": "pinpong初始化",
        "en": "pinpong initialize"
    },
    "description": {
        "zh-cn": "pinpong库初始化选择主控板功能和gpio",
        "en": "initialize pinpong library select board function and GPIO"
    },
    "author": "DFRobotTest",
    "cover": "cover.png",
    "main": "main.js",
    "isDevice": false,
    "mode": "python-block",
    "meta": {
        "runtimeVersion": "0.0.2"
    }
}

关键字段说明

字段 类型 说明
id string 扩展唯一标识符。
author string 与id一起组成扩展唯一标识。
version string 版本号。
name object 多语言名称。
description object 多语言描述。
cover string 封面图片文件名,需位于 public 目录下。
mode string 模式,此处应为 python-block
isDevice boolean Python模式均为 false
meta.runtimeVersion string 扩展 runtime 接口版本(用于控制 API 兼容性)。

meta.runtimeVersion

runtimeVersion 表示开发扩展时依赖的 runtime API 最低版本。应按实际使用的 API 能力填写最小版本,避免随意填写过大导致扩展只能在高版本 Mind+ 中加载。


3. Python 依赖管理 (public/requirements.txt)

如果有配置,Mind+会在加载扩展时,在库管理中显示当前扩展的 Python 依赖库。

示例 requirements.txt:

flask==2.3.2
requests>=2.25.0
numpy<1.24.0


4. 图形块描述文件 (index.js)

index.js 是扩展的入口文件。

类结构定义

import ArgumentType from '../utils/argument-type';
import BlockType from '../utils/block-type';
import blockIconURI from './icon/blockIcon.svg';
import menuIconURI from './icon/menuIcon.svg';
import Func from './func'; // 引入生成代码类
import { setLocaleData, formatMessage, setLocale } from '../utils/translation';
import LocaleData from "./locales";

setLocaleData(LocaleData);

class Extension {
    constructor(runtime, extensionId) {
        this.runtime = runtime;
        // 初始化生成代码的实例
        this.funcs = new Func(runtime, extensionId);
    }

    // 必须实现:切换语言的钩子
    setLocale(locale) {
        setLocale(locale);
    }

    // 必须实现:返回生成代码的实例
    getCodePrimitives() {
        return this.funcs;
    }

    // 必须实现:返回扩展的详细信息
    getInfo() {
        return {
            name: "pinpong",
            blockIconURI: blockIconURI,
            menuIconURI: menuIconURI,
            color1: "#FF6680",
            color2: "#FF4D6A",
            color3: "#FF3355",
            blocks: [
                {
                    opcode: 'pinpongInit',
                    blockType: BlockType.COMMAND,
                    text: formatMessage({id: 'gui.extension.pinpongBase.pinpongInit', default: 'pinpong初始化'}),
                    arguments: {}
                }
            ],
            menus: {},
        };
    }
}

export default Extension;

5. 代码生成文件 (func.js)

func.js 负责将图形块转换为 Python 代码。

核心结构

class Func {
    constructor(runtime, extensionId) {
        this.runtime = runtime;
    }

    pinpongInit(generator, block, parameter) {
        // 生成 Python 代码逻辑
        // 示例:导入库
        generator.addImport('import time');
        return `print("Hello Python Mode")`;
    }
}

export default Func;

6. 翻译资源文件 (locales)

locales/zh-cn.json:

{
  "gui.extension.pinpongBase.pinpongInit": "pinpong初始化",
  "gui.extension.pinpongBase.pinpongUpdateFirmware": "pinpong更新固件 板型[Board]端口[Port]",
  "gui.extension.pinpongBase.pinInit": "[Instance]pin初始化 引脚号[Pin] 模式[Mod]",
  "gui.extension.pinpongBase.readPinValue": "[Instance]读数字值",
  "gui.extension.pinpongBase.setPinValue": "[Instance]设置数字输出 值[Value]",
  "gui.extension.pinpongBase.readAnalogValue": "[Instance]读模拟值",
  "gui.extension.pinpongBase.setAnalogValue": "[Instance]设置模拟输出(PWM) 值[Value]"
}


7. Python 生成代码 API

1. 导入库模块 addImport

addImport(code: string) * 功能:导入 Python 库模块 * 参数: * code - 实际生成代码(code 去掉空格作为 id) * 覆盖规则:相同 id 时覆盖旧代码 * 示例

generator.addImport("from pinpong.board import Board");
generator.addImport("import time");
* 生成效果
from pinpong.board import Board
import time

while True:
    pass

2. 添加全局变量 addVariable

addVariable(id: string, code: string, coverage: boolean = false) * 功能:定义全局变量 * 参数: * id - 变量字符串标识符 * code - 实际生成代码 * coverage - 是否覆盖同 id 变量的生成代码(默认 false) * 示例

generator.addVariable("myserial", `myserial = serial.Serial()`);
* 生成效果
myserial = serial.Serial()

while True:
    pass

3. 添加初始化区域代码 addInit

addInit(id: string, code: string, priority: number = 0, isCover: boolean = false) * 功能:添加初始化的代码(相当于 Arduino 的 addSetup) * 参数: * id - 字符串标识符 * code - 实际生成代码 * priority - 优先级(0-9,9最靠前,默认 0) * isCover - 是否覆盖同 id 的生成代码(默认 false) * 示例

generator.addInit('aip_addUser_result', 'global aip_addUser_result', 1, true);
* 生成效果
global aip_addUser_result

while True:
    pass

4. 添加全局函数 addFunction

addFunction(code: string, isCover: boolean = false) * 功能:添加全局函数 * 参数: * code - 函数内容生成代码(函数名作为 id) * isCover - 是否覆盖同 id 的生成代码(默认 false) * 示例

let funcCode = [
    `def send_to_serial(key, value):`,
    `    if myserial.isOpen():`,
    `        json_message = json.dumps({key: value})`,
    `        myserial.write(bytes(json_message, "utf-8"))`,
];
generator.addFunction(funcCode.join('\n'), true);
* 生成效果
def send_to_serial(key, value):
    if myserial.isOpen():
        json_message = json.dumps({key: value})
        myserial.write(bytes(json_message, "utf-8"))

while True:
    pass

5. 创建事件回调函数

  • 示例
    return (`def on_serial_message${index}_callback(msg):`);
    
  • 生成效果
    # 事件回调函数
    def on_serial_message0_callback(msg):
        pass
    
    while True:
        pass
    

6. 在积木位置生成 code

  • 位置while True: 或积木当前位置
  • 规则
    • 返回字符串直接插入代码
    • 返回数组 [code, priority] 处理优先级(如运算符顺序)
  • 示例
    return (`p_ens160.get_status()`);
    
  • 生成效果
    while True:
        # block返回的生成代码
        p_ens160.get_status()
    

7. 进阶用法

  • 添加注释 addComment(id: string, comment: any, position: number, isCover: boolean)
  • 添加错误提示 addErrorPrompt(message: string)
  • 获取资源路径 get_asset_path_by_name (全局函数)
    • 示例
      createImageReaderFromFile(generator, block, parameter) {
          generator.addImport("from model_mp_io import ImageReader");
          const reader = parameter.READER.code;
          const path = parameter.PATH.code;
          return(`${reader} = ImageReader(source=get_asset_path_by_name(${path}))`);
      }