把伪代码字面翻译成代码块图,给文档与教程用。只读、不检查语法,仅供参考。
页面上没有预览时它什么都不加载。默认写法就是 Markdown 的
```ai2-blocks 围栏——文档照常写,渲染出来的
<pre><code class="language-ai2-blocks"> 会被原地换成块图;
本页除了特意演示的那一张,全是这么写的。手写页面也可以用
<div data-ai2-blocks> 容器(能带 data-* 选项)。
语言名 ai2-blocks / ai-blocks / ai2-block
都认,其它语言的代码块一律不碰。下面这张特意用的是 kramdown/rouge 渲染出来的形态
(语言 class 挂在最外层 div 上、里面的 code 一个 class 都没有),
和 markdown-it / hugo 的 <pre><code class="language-…"> 一样能认。
when 按钮_保存.Click() {
微数据库1.StoreValue("note", 文本输入框_笔记.Text)
对话框1.ShowAlert("已保存")
}页面上引的是 fun123.cn 生产环境同款
/static/js/prism.js + /static/css/prism.css
(注意 css 不在 js 目录下,写成 /static/js/prism.css 会 404——
而这份 prism.js 带 toolbar/copy 插件,没样式时那些按钮会变成裸文字,看着就是"排版乱了")。
只有 ai2-blocks 类的围栏会被换成块图,
其它语言一概不碰——下面这段 PHP(App Inventor 连 MySQL 常用的那个中转脚本)就是普通代码块,
照常由 Prism 高亮,两者同页互不干扰。
<?php
/*
* Written By: ShivalWolf
* Date: 2011/06/03
* Contact: Shivalwolf@domwolf.net
*
* UPDATE 2014/07/11
* mysql API (deprecated) replaced by mysqli by Taifun
*
* UPDATE 2016/02/21
* Bugfix Undefined variable: csv
*/
/************************************CONFIG****************************************/
//DATABSE DETAILS//
$DB_ADDRESS="";
$DB_USER="";
$DB_PASS="";
$DB_NAME="";
//SETTINGS//
//This code is something you set in the APP so random people cant use it.
$SQLKEY="secret";
/************************************CONFIG****************************************/
//these are just in case setting headers forcing it to always expire
header('Cache-Control: no-cache, must-revalidate');
if( isset($_POST['query']) && isset($_POST['key']) ){ //checks if the tag post is there
header('Content-type: text/csv');
if($_POST['key']==$SQLKEY){ //validates the SQL key
$query=urldecode($_POST['query']);
$conn = new mysqli($DB_ADDRESS,$DB_USER,$DB_PASS,$DB_NAME);
if($conn->connect_error){
header("HTTP/1.0 400 Bad Request");
echo "ERROR Database Connection Failed: " . $conn->connect_error, E_USER_ERROR;
} else {
$result=$conn->query($query); //runs the posted query
if($result === false){
header("HTTP/1.0 400 Bad Request");
echo "Wrong SQL: " . $query . " Error: " . $conn->error, E_USER_ERROR;
} else {
if (strlen(stristr($query,"SELECT"))>0) { //tests if it's a SELECT statement
$csv = '';
while ($fieldinfo = $result->fetch_field()) {
$csv .= $fieldinfo->name.",";
}
$csv = rtrim($csv, ",")."\n";
echo $csv; //prints header row
$csv = '';
$result->data_seek(0);
while($row = $result->fetch_assoc()){
foreach ($row as $key => $value) {
$csv .= $value.",";
}
$csv = rtrim($csv, ",")."\n";
}
echo $csv; //prints all data rows
} else {
header("HTTP/1.0 201 Rows");
echo "AFFECTED ROWS: " . $conn->affected_rows;
}
}
$conn->close(); //closes the DB
}
} else {
header("HTTP/1.0 400 Bad Request");
echo "Bad Request"; //reports if the secret key was bad
}
} else {
header("HTTP/1.0 400 Bad Request");
echo "Bad Request";
}
?>
全页只有这一张用 <div data-ai2-blocks> 容器写法,
用来对照——需要 data-code / data-src / data-png
这类选项时只能用容器;纯写文档用围栏就够了。
组件实例名写中文(按钮1、标签1,与中文版设计器的默认命名一致),
成员名写英文内部名(.Text / .Click),块上自动显示成「文本」「被点击时」。
when Screen1.Initialize() {
global items = []
for i = 1 to 5 {
listAdd(items, random(1, 100))
}
列表显示框1.Elements = items
}
function Add(a, b) {
return a + b
}
procedure ShowSum(x, y) {
标签1.Text = join("和 = ", Add(x, y))
}
when 按钮1.Click() {
ShowSum(3, 4)
}
字符串必须一行写完,换行用 \n 转义——含换行的字符串自动生成
「多行文本」块(text_multiline_text),单行字符串仍是普通文本块;读取现有工程时,
多行内容也以 \n 转义显示。
global 日志 = "开机自检完成\n等待指令"
when 按钮1.Click() {
日志 = join(日志, "\n点击了按钮")
标签1.Text = 日志
}
每张预览默认带 代码块 / 原始 两个 tab,
在同一块视图里切换,不用写任何属性。「原始」就是你写的那段伪代码原文;
三种源码按 tab 懒生成——默认停在「代码块」上,没点开的一分开销都不花。
右上角三个图标:复制(块图 tab 复制可粘贴的代码块,文本 tab 复制当前那段文本)、
下载图片(只在块图 tab)、全屏(再点一次或按 Esc 退出)。
需要 YAIL、Python 或 JavaScript 时显式写入 data-code,例如
data-code="raw,python";完全不要文本 tab 写
data-code="off";单独关图标 data-png="off" /
data-fullscreen="off"。
global 总分 = 0
procedure 记一次(得分) {
总分 = 总分 + 得分
if 总分 >= 100 {
标签1.Text = "过关!"
} else {
标签1.Text = join("还差 ", 100 - 总分, " 分")
}
}
when 按钮1.Click() {
记一次(10)
}
把「一键解释项目 / 当前屏幕」导出的
{"$components":[…],"$blocks":[…]} 整段贴进来就行(参考项目库
ai_reference_projects.json 存的也是这个格式):$blocks 多段会合并进
同一个工作区,$components 另开一个「界面」tab 画成界面示意图
(容器递归、尺寸 -1/-2/百分比、&HAARRGGBB 配色,非可视组件排在下面)。
写在 ```ai2-blocks 围栏里即可(JSON 里的 <xml> 标签由 Markdown
自动转义,手写 HTML 时要自己写成 <)。这类输入是从编辑器原样拷出来的,
不打水印,右上角「复制」也是原样复制并提示到设计视图粘贴。
{
"$components": [{"$Name": "Screen1", "$Type": "Form", "$Version": "31", "Title": "日志小工具",
"$Components": [
{"$Name": "StatusLabel", "$Type": "Label", "$Version": "5", "Text": "就绪", "FontSize": "14", "TextColor": "&HFF3C5CFF", "Uuid": "1"},
{"$Name": "LogInput", "$Type": "TextBox", "$Version": "6", "Hint": "输入点什么…", "Width": "-2", "Uuid": "2"},
{"$Name": "Row1", "$Type": "HorizontalArrangement", "$Version": "4", "Width": "-2", "AlignHorizontal": "3", "$Components": [{"$Name": "BtnSave", "$Type": "Button", "$Version": "7", "Text": "保存", "FontSize": "16", "Uuid": "3"}, {"$Name": "BtnClear", "$Type": "Button", "$Version": "7", "FontSize": "16", "Width": "-2", "Text": "清空", "Uuid": "-51472725"}]},
{"$Name": "AutoSave", "$Type": "CheckBox", "$Version": "2", "Text": "自动保存", "Checked": "True", "Uuid": "4"},
{"$Name": "Logo", "$Type": "Image", "$Version": "4", "Picture": "logo.png", "Height": "60", "Uuid": "5"},
{"$Name": "TinyDB1", "$Type": "TinyDB", "$Version": "3", "Uuid": "6"},
{"$Name": "Clock1", "$Type": "Clock", "$Version": "4", "Uuid": "7"}
]}],
"$blocks": [
"<xml><block xmlns=\"http://www.w3.org/1999/xhtml\" type=\"component_event\" id=\"e1\" x=\"10\" y=\"10\"><mutation component_type=\"Button\" is_generic=\"false\" instance_name=\"BtnClear\" event_name=\"Click\"></mutation><field name=\"COMPONENT_SELECTOR\">BtnClear</field><statement name=\"DO\"><block type=\"component_set_get\" id=\"s1\"><mutation component_type=\"TextBox\" set_or_get=\"set\" property_name=\"Text\" is_generic=\"false\" instance_name=\"LogInput\"></mutation><field name=\"COMPONENT_SELECTOR\">LogInput</field><field name=\"PROP\">Text</field><value name=\"VALUE\"><block type=\"text\" id=\"t1\"><field name=\"TEXT\"></field></block></value><next><block type=\"component_set_get\" id=\"s2\"><mutation component_type=\"Label\" set_or_get=\"set\" property_name=\"Text\" is_generic=\"false\" instance_name=\"StatusLabel\"></mutation><field name=\"COMPONENT_SELECTOR\">StatusLabel</field><field name=\"PROP\">Text</field><value name=\"VALUE\"><block type=\"text\" id=\"t2\"><field name=\"TEXT\">已清空输入</field></block></value></block></next></block></statement></block></xml>",
"<xml><block xmlns=\"http://www.w3.org/1999/xhtml\" type=\"component_event\" id=\"e2\" x=\"10\" y=\"300\"><mutation component_type=\"Form\" is_generic=\"false\" instance_name=\"Screen1\" event_name=\"Initialize\"></mutation><field name=\"COMPONENT_SELECTOR\">Screen1</field><statement name=\"DO\"><block type=\"component_set_get\" id=\"s3\"><mutation component_type=\"Label\" set_or_get=\"set\" property_name=\"Text\" is_generic=\"false\" instance_name=\"StatusLabel\"></mutation><field name=\"COMPONENT_SELECTOR\">StatusLabel</field><field name=\"PROP\">Text</field><value name=\"VALUE\"><block type=\"text\" id=\"t3\"><field name=\"TEXT\">就绪</field></block></value></block></statement></block></xml>"
]
}
拓展没有中文调色板名,实例名保持英文(MLKitTextRecognizer1)。
能渲染的前提是组件类型库里有拓展描述符——component_types.json 里已经合并进来了。
文字识别(OCR) — 选一张图 → 识别出文字。Script 决定用哪套模型:latin 认英文,chinese 认中文(也能认中文里夹的英文)。
global picked = ""
when 图像选择器1.AfterPicking() {
picked = 图像选择器1.Selection
图像1.Picture = picked
MLKitTextRecognizer1.Script = "chinese"
MLKitTextRecognizer1.RecognizeFromFile(picked)
}
when MLKitTextRecognizer1.RecognitionSuccess(text, resultJson) {
标签1.Text = text
}
when MLKitTextRecognizer1.RecognitionFailed(operation, errorMessage) {
标签1.Text = join("识别失败:", errorMessage)
}
条码 / 二维码 — 摄像头实时扫描。预览容器要在设计器里选一个布局;ContinuousScan 为真时扫完一个还能接着扫。
when 按钮1.Click() {
MLKitCodeScanner1.ContinuousScan = true
MLKitCodeScanner1.StartScan()
}
when MLKitCodeScanner1.GotBarcode(rawValue, format, valueType, displayValue, detailJson) {
标签1.Text = join(format, ":", rawValue)
}
when MLKitCodeScanner1.GotWifi(ssid, password, encryption) {
标签1.Text = join("WiFi ", ssid, " 密码 ", password)
}
when MLKitCodeScanner1.ScanFailed(operation, errorMessage) {
标签1.Text = errorMessage
MLKitCodeScanner1.StopScan()
}
文档扫描 — 调起系统扫描界面,返回的是能直接用的文件路径。用户按返回键走 ScanCancelled,不是失败。
when 按钮1.Click() {
MLKitDocScanner1.Scan()
}
when MLKitDocScanner1.GotScanResult(pageCount, imagePaths, pdfPath) {
图像1.Picture = imagePaths[1]
标签1.Text = join("共 ", pageCount, " 页,PDF:", pdfPath)
}
when MLKitDocScanner1.ScanCancelled() {
标签1.Text = "已取消"
}
when MLKitDocScanner1.ScanFailed(errorMessage) {
标签1.Text = errorMessage
}
图像标注 — 认出图里有什么。标签同时给英文原名和中文名,置信度低于阈值的不返回。
when 图像选择器1.AfterPicking() {
MLKitImageLabeler1.ConfidenceThreshold = 0.6
MLKitImageLabeler1.LabelFromFile(图像选择器1.Selection)
}
when MLKitImageLabeler1.GotLabels(topLabel, topName, labels, names, resultJson) {
标签1.Text = join("最像:", topName, "(", topLabel, ")")
}
when MLKitImageLabeler1.NoLabelFound(operation) {
标签1.Text = "没认出来"
}
目标检测 — GotObjects 先报总数,随后每个物体触发一次 GotObject,带位置矩形框。
global found = 0
when 图像选择器1.AfterPicking() {
found = 0
MLKitObjectDetector1.MultipleObjects = true
MLKitObjectDetector1.DetectFromFile(图像选择器1.Selection)
}
when MLKitObjectDetector1.GotObjects(count, imageWidth, imageHeight, resultJson) {
标签1.Text = join("找到 ", count, " 个物体")
}
when MLKitObjectDetector1.GotObject(index, x, y, width, height, label, confidence, trackingId) {
found = found + 1
画框(x, y, x + width, y + height)
}
// Canvas 没有画矩形的方法,用四条线拼
procedure 画框(左, 上, 右, 下) {
画布1.DrawLine(左, 上, 右, 上)
画布1.DrawLine(右, 上, 右, 下)
画布1.DrawLine(右, 下, 左, 下)
画布1.DrawLine(左, 下, 左, 上)
}
人脸检测 — 返回人脸数与详细 JSON(位置框、关键点、微笑/睁眼概率)。开轮廓和分类会更慢。
when 图像选择器1.AfterPicking() {
MLKitFaceDetector1.Classifications = true
MLKitFaceDetector1.DetectFromFile(图像选择器1.Selection)
}
when MLKitFaceDetector1.DetectionSuccess(resultJson, faceCount) {
标签1.Text = join("检测到 ", faceCount, " 张脸")
标签2.Text = resultJson
}
when MLKitFaceDetector1.DetectionFailed(operation, errorMessage) {
标签1.Text = errorMessage
}
面部网格 — 468 个带 xyz 的关键点。可按区域取点(脸廓/左右眼/眉毛/嘴唇/鼻梁),用于贴纸定位。
when 图像选择器1.AfterPicking() {
MLKitFaceMesh1.DetectFromFile(图像选择器1.Selection)
}
when MLKitFaceMesh1.GotFaceMesh(faceCount, pointCount, boundsJson) {
标签1.Text = join("脸 ", faceCount, " 张,关键点 ", pointCount, " 个")
标签2.Text = MLKitFaceMesh1.RegionPoints("leftEye", 1)
}
when MLKitFaceMesh1.NoFaceFound(operation) {
标签1.Text = "没找到脸"
}
姿势检测 — 33 个人体关键点及置信度,可做体感游戏、健身计数。AccurateMode 更准但更慢。
when 图像选择器1.AfterPicking() {
MLKitPoseDetector1.AccurateMode = false
MLKitPoseDetector1.DetectFromFile(图像选择器1.Selection)
}
when MLKitPoseDetector1.DetectionSuccess(resultJson, landmarkCount) {
标签1.Text = join("关键点 ", landmarkCount, " 个")
标签2.Text = resultJson
}
when MLKitPoseDetector1.DetectionFailed(operation, errorMessage) {
标签1.Text = errorMessage
}
人像分割 — 抠人像 / 换背景 / 虚化,结果存成 PNG 并返回路径,可直接给图像组件显示。
global src = ""
when 图像选择器1.AfterPicking() {
src = 图像选择器1.Selection
MLKitSegmenter1.Threshold = 0.8
MLKitSegmenter1.ExtractPerson(src)
}
when 按钮1.Click() {
MLKitSegmenter1.ReplaceBackgroundWithColor(src, "&HFF4CAF50")
}
when MLKitSegmenter1.GotResult(imagePath, width, height) {
图像1.Picture = imagePath
}
when MLKitSegmenter1.SegmentFailed(operation, errorMessage) {
标签1.Text = errorMessage
}
手写识别 — 设计器里给「书写画布」选一块画布,用户写完直接调识别。语言包没下会自动下载。
when 按钮1.Click() {
MLKitHandwriting1.LanguageTag = "zh-Hani"
MLKitHandwriting1.Recognize()
}
when 按钮2.Click() {
MLKitHandwriting1.Clear()
标签1.Text = ""
}
when MLKitHandwriting1.GotText(text, candidatesJson) {
标签1.Text = text
}
when MLKitHandwriting1.ModelDownloadFailed(errorMessage) {
标签1.Text = join("语言包下载失败:", errorMessage)
}
语言识别 — 判断一段文本是哪种语言,100+ 种,完全离线。常用来自动选翻译的源语言。
when 按钮1.Click() {
MLKitLanguageId1.IdentifyLanguage(文本输入框1.Text)
}
when MLKitLanguageId1.GotLanguage(languageTag, name) {
标签1.Text = join(name, "(", languageTag, ")")
}
when MLKitLanguageId1.NoLanguageFound(text) {
标签1.Text = "认不出是什么语言"
}
离线翻译 — 语言包首次用要联网从 Google 下载(每种约 30MB),下完永久离线可用。
when 按钮1.Click() {
MLKitTranslator1.SourceLanguage = "en"
MLKitTranslator1.TargetLanguage = "zh"
MLKitTranslator1.Translate(文本输入框1.Text)
}
when MLKitTranslator1.GotTranslation(originalText, translatedText) {
标签1.Text = translatedText
}
when MLKitTranslator1.ModelDownloaded() {
标签1.Text = "语言包已就绪"
}
when MLKitTranslator1.TranslateFailed(operation, errorMessage) {
标签1.Text = errorMessage
}
实体抽取 — 从文本里挑出地址、日期、电话、快递单号、航班号、银行卡号等,并给出在原文中的位置。
when 按钮1.Click() {
MLKitEntityExtractor1.Extract(文本输入框1.Text)
}
when MLKitEntityExtractor1.GotEntities(count, resultJson) {
标签1.Text = join("抽出 ", count, " 条")
标签2.Text = resultJson
}
when MLKitEntityExtractor1.NoEntityFound(text) {
标签1.Text = "没抽到有用信息"
}
智能回复 — 按对话上下文给 1~3 条候选回复。只支持英语对话,中文会触发「语言不支持」。
when 按钮1.Click() {
MLKitSmartReply1.AddRemoteMessage(文本输入框1.Text, "friend")
MLKitSmartReply1.SuggestReplies()
}
when MLKitSmartReply1.GotSuggestions(count, suggestions) {
列表显示框1.Elements = suggestions
}
when MLKitSmartReply1.LanguageNotSupported() {
标签1.Text = "只支持英语对话"
}
自定义分类 — 用你自己训练的 TFLite 模型分类,突破基础模型 400 个通用类别的限制。模型填素材文件名即可。
when 图像选择器1.AfterPicking() {
MLKitCustomLabeler1.ModelFile = "flowers.tflite"
MLKitCustomLabeler1.ConfidenceThreshold = 0.7
MLKitCustomLabeler1.LabelFromFile(图像选择器1.Selection)
}
when MLKitCustomLabeler1.GotLabels(topLabel, topConfidence, labels, resultJson) {
标签1.Text = join(topLabel, " ", round(topConfidence * 100), "%")
}
when MLKitCustomLabeler1.LabelFailed(operation, errorMessage) {
标签1.Text = errorMessage
}
自定义检测 — 自训模型做检测 + 精细分类,既给位置框也给具体品类。
when 图像选择器1.AfterPicking() {
MLKitCustomObjectDetector1.ModelFile = "parts.tflite"
MLKitCustomObjectDetector1.Classification = true
MLKitCustomObjectDetector1.DetectFromFile(图像选择器1.Selection)
}
when MLKitCustomObjectDetector1.GotObject(index, x, y, width, height, label, confidence, trackingId) {
画布1.DrawShape([[x, y], [x + width, y], [x + width, y + height], [x, y + height]], false)
标签1.Text = label
}
when MLKitCustomObjectDetector1.NoObjectFound(operation) {
标签1.Text = "没检测到物体"
}
从代码块编辑器里复制出来的 XML 也能直接渲染。这类输入没经过任何转换, 所以不打水印,右上角「复制」是原样复制并提示到代码块视图粘贴。
<block type="controls_if"><value name="IF0"><block type="logic_boolean"><field name="BOOL">TRUE</field></block></value></block>