Quikjs简介
QuickJS是一个小型的可嵌入Javascript引擎。它支持ES2020规范,包括模块、异步生成器和代理。它还支持数学扩展,比如大整数(BigInt)、大浮点数(BigFloat)和操作符重载。Sifli在Quickjs官方基础上增加了RT-Thread和LVGL的移植适配,沿用原有的C应用框架,使用户可以用javascipt来开发表盘/应用/AOD。
应用框架支持
Sifli 有明确的应用框架,在此基础上添加了应用/表盘/AOD的注册接口,系统启动时,会自动扫描qjs目录,应用子目录扫描qjs_app,表盘扫描子目录qjs_wf,AOD扫描qjs_aod 如果:
发现目录以
JA_
开头,JA_xxx
这是一个js的应用,JA_xxx子目录src中的JA_xxx_main.js是应用的主程序,需要js代码中定义一个JA_xxx,基于app,注册到全局变量中。发现目录是以
JW_
开头,JW_xxx
这是一个js的表盘,JW_xxx子目录src中的JW_xxx_main.js是表盘的主程序,需要js代码中定义一个JW_xxx,基于app,注册到全局变量中。发现目录是以
AOD_
开头,AOD_xxx
这是一个js的AOD应用,AOD_xxx子目录src中的AOD_xxx_main.js是AOD的主程序,需要js代码中定义一个AOD_xxx,基于app,注册到全局变量中。
根据类型,注册不同的C框架中,由C框架统一调度执行。
LVGL的支持
lvgl的支持主要有obj class, obj extension, functions三种
obj class obj class封装了lv_obj_xxx函数,输出一个lvobj class,每个method是相应lv_obj_xxx的封装
obj extension obj extension封装了lv_class_xxx函数,和生成的class.js配合,输出class,如 label.js和封装的lv_label_xxx,定义了label class,用户使用的时候,只需要import label from “/support_script/qjs/label.js”,就可以使用lable class了。
functions functions封装了在lvgl.h和lvsf中,不是以上两种的函数。function属于lvobj模块,使用时需要通过lv模块调用,如
gui_app_run(app)
,js中使用为lv.gui_app_run(app)
注意
目前函数的或者返回值是int/bool/color16_t/string/object之外的,无法生成。lv_obj_add_event_cb的参数类型是函数指针,无法生成,所以在lvobj module在C中实现了set_event_cb方法,通过定义一个proxy来实现event事件传递。
demo使用
quickjs支持板级/模拟器运行,使用步骤如下:
打开HCPU工程/SIMU工程quickjs依赖宏
勾选预置qjs脚本和应用
勾选编译选项JS应用,然后开始编译
注意
只修改JS应用中的代码,只需单独编译烧录JS应用即可
目录结构
框架代码
QJS框架代码放置在qjs_framework目录中,包含了应用框架,页面调度,lv,lv_ext内置模块等处理。
生成代码
qjs c映射代码放置qjs_generated目录中。
lv_qjs_ext_obj.c 是obj extension封装集合
lv_qjs_function.c 是functions封装集合
lv_qjs_obj.c 是obj class封装集合
lv_qjs_generated.c 是文件包含和一些编译打桩函数
class代码
js class是obj externsion的封装,放置在如下图所示目录,使用时需要通过butterfli预置到文件系统中使用。
应用代码
qjs_aod存放js aod应用,子目录必须以
AOD_
打头qjs_app存放js app应用,子目录必须以
JA_
打头qjs_wf存放js wf应用,子目录必须以
JW_
打头readme.init 编译依赖,参考 外置应用编译依赖
JA_app1_language.js,编译时由多语言excel表转换生成,提供了app_get_str方法获取多语言
JA_app1_main.js 主程序,命名必须是
JA_xxx_main.js
规则JA_app1_page1.js 子页面,page1是子页面名,需要和js 代码中匹配,JA_app1_page2.js 同理,命名规则同主程序
resource目录,资源放置目录,参考资源位置
应用图标必须命名为
thumb.png
QJS生成
QJS新增的接口建议统一放到lv_qjs_generated.c/h中,对于生成的接口有如下要求:
参数和返回值类型只能是
int
,bool
,lv_color_t
,string
,lv_obj_t*
,接口名以lv_
打头入参个数最大支持8
为了避免生成时编译报错,新增函数尽量不要包含除了LVGL相关的文件,如果需要用到其中的函数,通过export的方式
自动生成
自动生成涉及到以下脚本:
脚本 |
目录 |
说明 |
---|---|---|
sol_gen.sh |
sdk\external\micropython\lib\lv_bindings |
linux系统使用,生成json中间件 |
qjs_gen_blacklist.bat |
solution\components\quickjs\script |
windows系统使用,生成black_list.py |
qjs_gen_js_class.bat |
solution\components\quickjs\script |
windows系统使用,生成ext obj对应的.js |
qjs_gen_lv_ext_obj.bat |
solution\components\quickjs\script |
windows系统使用,生成对ext obj的C支持 |
qjs_gen_lv_functions.bat |
solution\components\quickjs\script |
windows系统使用,生成对function的C支持 |
qjs_gen_lv_obj.bat |
solution\components\quickjs\script |
windows系统使用,生成对lv obj的C支持 |
inherit_list.py |
solution\components\quickjs\script |
指明生成JS class继承的模块 |
string_list.py |
solution\components\quickjs\script |
指定接口参数为字符串 |
预处理
继承
非lvobj
模块的js class,需要在inherit_list.py中指明其继承的父类C控件中有些接口参数可能是void*,但是实际上参数为char* 字符串,如果不修改C接口,可以
string_list.py
中指明这些接口,脚本会做处理
脚本执行
自动生成按照以下步骤:
对于lv obj和function类型的接口,将需要新增的接口实现放到lv_qjs_generated.c中,申明放到lv_qjs_generated.h, ext obj类型直接在lv_qjs_generated.h中include
linux系统, 需要安装python2.7版本,(建议通过Ubuntun 18.04.6 LTS版本, 默认安装了python2.7), 进入到工程
/sdk/external/micropython/lib/lv_bindings
目录执行带参的脚本./sol_gen.sh,如
./sol_gen.sh nand 16 qjs
会生成基于nand工程,16位色的json中间件windows进入到
solution/components/quikcjs/script
目录,执行qjs_gen_blacklist.bat
生成blacklist.py
,所有在black_list中的函数不会生成,可以减少ram的使用,用户根据需求打开或者关闭根据添加的函数类型对应执行脚本:
如果是lv obj类型,执行
qjs_gen_lv_obj.bat
,会更新../qjs_generated/lv_qjs_obj.c
文件,在文件中查找是否有新增接口生成如果是function类型,执行
qjs_gen_lv_functions.bat
,会更新../qjs_generated/lv_qjs_functions.c
文件,在文件中查找是否有新增接口生成如果是ext obj类型,执行
qjs_gen_lv_ext_obj.bat
,会更新../qjs_generated/lv_qjs_ext_obj.c
文件,在文件中查找是否有新增接口生成,同时执行qjs_gen_js_class.bat
,更新../../support_script/qjs
目录下的js程序
注意
脚本sol_gen.sh执行可能会报错,可能原因文件不是unix格式,编译C文件有未知字符或者格式不是utf8等,可以根据报错添加打印定位
.bat脚本生成的接口受black_list.py中的接口影响,在black_list.py中的接口不会生成
不支持的接口会在生成的.c中提示,可以搜索查看具体原因
手动添加
C文件修改
对quickjs有一定程序了解后,可以通过手动添加来增加接口,好处是不需要处理编译生成中间件的问题,只需要根据函数类型修改qjs_generated对应的.c,如function类型修改lv_qjs_functions.c
类型 |
文件 |
枚举定义表 |
函数格式表 |
函数列表 |
回调接口 |
---|---|---|---|---|---|
lvobj |
lv_qjs_obj.c |
JS_lv_obj_methods_enum |
lv_obj_protos |
js_lv_obj_methods |
lv_obj_call_method |
ext obj |
lv_qjs_ext_obj.c |
JS_lv_ext_funcs_enum |
lv_ext_funcs_proto |
js_lv_ext_funcs |
lv_ext_call_func |
functions |
lv_qjs_functions.c |
JS_lv_funcs_enum |
lv_funcs_proto |
js_lv_funcs |
lv_obj_call_method |
定义枚举值
在枚举定义表中添加接口对应的enum值, functions和ext obj类型enum名按照LVFUNC
+去掉lv打头的函数名
,如lv_version_major,enum就是LVFUNC_version_major,lvobj类型以LVOBJ
+去掉lv_obj的函数名
,如lv_obj_clean,enum就是LVOBJ_clean添加数据格式
在函数格式表中按照在枚举定义表中的顺序添加函数的数据格式。name
属性是去掉lv_或者lv_obj_打头的函数名字符串,param_type
是数组,根据函数的参数顺序依次填入类型,没有入参忽略,return_type
是返回值类型添加函数列表 在函数列表中按照在枚举定义表的顺序顺序添加函数定义JS_CFUNC_MAGIC_DEF, 参数1是步骤2中的
name
,参数2是入参的个数,需要步骤2中param_type
的个数匹配,参数3是函数回调接口,根据函数类型填写, 参数4是步骤1中的枚举值增加回调实现 对于functions和ext obj类型的对应的回调实现中,C接口的入参根据个数依次使用param[0],param[1]…,对于lv obj类型回调实现中的,第一个参数应该是s->lv_obj,后续的参数依次是param[0],param[1]…,函数的返回值根据类型调用接口返回。
返回值是int,lv_obj_t*类型,调用JS_NewInt32
返回值是bool类型,调用JS_NewBool
返回值是字符串类型,调用JS_NewString
返回值是lv_color_t,先调用lv_color_to32转成uint32_t,再调用JS_NewInt32
参数或者返回值的类型有:
enum {
LVTYPE_none=0,
LVTYPE_int,
LVTYPE_bool,
LVTYPE_func, //固定为lv_event_cb_t使用
LVTYPE_object,
LVTYPE_color,
LVTYPE_string,
};
示例:
typedef enum
{
...,
LVFUNC_version_info,
LVFUNC_version_major,
LVFUNC_version_minor,
LVFUNC_version_patch,
}JS_lv_funcs_enum;
const JS_lv_protos lv_funcs_proto[]=
{
....,
//顺序要和枚举中的顺序保持对应
{
.name = "version_info",
.return_type = LVTYPE_string
},
{
.name = "version_major",
.return_type = LVTYPE_int
},
{
.name = "version_minor",
.return_type = LVTYPE_int
},
{
.name = "version_patch",
.return_type = LVTYPE_int
},
};
const JSCFunctionListEntry js_lv_funcs[] =
{
...,
//顺序要和枚举中的顺序保持对应
JS_CFUNC_MAGIC_DEF("version_info", 0, js_lv_func, LVFUNC_version_info),
JS_CFUNC_MAGIC_DEF("version_major", 0, js_lv_func, LVFUNC_version_major),
JS_CFUNC_MAGIC_DEF("version_minor", 0, js_lv_func, LVFUNC_version_minor),
JS_CFUNC_MAGIC_DEF("version_patch", 0, js_lv_func, LVFUNC_version_patch),
};
static JSValue lv_call_func(JSContext *ctx, int magic, JSValueConst param[]) =
{
JSValue r = JS_UNDEFINED;
switch(magic)
{
...,
case LVFUNC_version_info:
{
char *r_in = (char *)lv_version_info();
r = JS_NewString(ctx, r_in);
break;
}
case LVFUNC_version_major:
{
int r_in = lv_version_major();
r = JS_NewInt32(ctx, r_in);
break;
}
case LVFUNC_version_minor:
{
int r_in = lv_version_minor();
r = JS_NewInt32(ctx, r_in);
break;
}
case LVFUNC_version_patch:
{
int r_in = lv_version_patch();
r = JS_NewInt32(ctx, r_in);
break;
}
}
return r;
}
lv obj类型和ext obj类型添加方式类似。
JS文件添加
对于ext obj类型,还需要添加对应的js文件才能使用。在support_scipt/qjs/目录下,添加对应的js文件,如lv_qrcode控件,需要添加lvsfbarcode.js,实现lvsfbarcode这个类 示例:
import * as lv from "lv"; //lv模块
import * as lvext from "lvext"; //lvext模块
import {img} from "/support_script/qjs/img.js"; //img模块
export class lvsfbarcode extends img { //继承自img,需要和C代码控件class变量中的base_class保持一致
constructor(parent,parentobj=undefined) {
var nativeobj=parentobj;
if(nativeobj==undefined) {
nativeobj=lvext.lvsfbarcode_create(parent);
}
super(parent,nativeobj);
this.nativeobj=nativeobj;
this.set_obj(this.nativeobj);
}
set_text(text){
return lvext.lvsfbarcode_set_text(this.nativeobj, text);
}
set_line_color(index){
return lvext.lvsfbarcode_set_line_color(this.nativeobj, index);
}
set_parse(parse){
return lvext.lvsfbarcode_set_parse(this.nativeobj, parse);
}
set_dir(dir){
return lvext.lvsfbarcode_set_dir(this.nativeobj, dir);
}
}
const lv_obj_class_t lv_lvsfbarcode_class =
{
.constructor_cb = lv_lvsfbarcode_constructor,
.destructor_cb = lv_lvsfbarcode_destructor,
.instance_size = sizeof(lv_lvsfbarcode_ext_t),
.base_class = &lv_img_class //c代码中是继承img
};
注意
为了减少对生成的依赖和ROM的占用,强烈建议对接口进行整理归类,如通过传入不同的enum值,封装get/set字符串,获取整形等的统一接口
支持功能
数据订阅
JS中支持控件可以通过lvobj模块的bind的方法订阅数据, 当数据更新后,C端进行通知,示例如下:
label = new label(this.root);
//如果C端通知时没有数据,那么val就不存在
function callback(id, type, val){
//do something
}
//id 字符串类型,id可为NULL,type必须为唯一值
//type 整形
//回调函数
label.bind(id, type, callback);
注意
建议每个控件只订阅一个类型的数据,就可以不再回调里判断id和type
可以封装get接口,再消息回调中直接调用获取,就无需关心val类型以及是否存在
控件的取消订阅处理由框架管理,用户不需要手动取消
使用示例参考JA_app1
编码器
lvapp模块提供wheel()方法来进行旋钮编码器注册和反注册接口功能,典型使用方式在js app的resume方法中注册,在pause方法中反注册,示例如下
wheel_handler(key) {
//do something
}
resume() {
this.wheel( //传入参数时表示注册
function (key) {
this.wheel_handler(key);
}
);
}
pause(){
this.wheel(); //没有参数表示反注册
}
注意
key只有17,18两个值,17表示向上,18表示向下,见lv_enums.js
使用示例参考JA_app2
按键
lvapp模块提供keypad()方法来进行按键注册和反注册接口功能,典型使用方式在js app的resume方法中注册,在pause方法中反注册,示例如下
keypad_handler(key, state) {
if(key == lv_enums.KEY_HOME && state == 1){
return 1; //返回1不执行按键默认功能
}
return 0; //返回0执行按键默认功能
}
resume() {
this.keypad( //传入参数时表示注册
function (key, state) {
return this.keypad_handler(key, state);
}
);
}
pause(){
this.keypad(); //没有参数表示反注册
}
注意
按键回调中需要返回值,框架会判断返回值来决定是否执行默认功能
使用示例参考JA_app2
数据存储
qjs框架中的nvm模块通过文件系统操作,将数据序列化为JSON字符串写入文件实现存储功能,读取文件并反序列化为object来实现读取功能,使用示例如下
import * as nvm from "nvm" //头文件引入
var user = {
id:1,
name:"Bob",
scores:[90,85]
};
var str_data = JSON.stringify(user)
//filename, str_data 序列化后的JSON字符串
nvm.wirte(filename, str_data);
//读取文件字符串
var read_string = nvm.read(filename);
//反序列化为object
var conv_obj = JSON.parse(read_string);
注意
数据存储/读取时需要考虑异常场景,如文件不存在,数据格式发生变化等
使用示例参考JA_app3
多语言
qjs支持多语言,通过将excel表翻译成js代码,从而实现多语言功能, 按照以下步骤来使用多语言
js程序中导入多语言接口,如 import {app_get_str} from “./JA_app1_language.js”
js代码中通过app_get_str接口获取当前多语言 注意
在excel转换js过程中,工具会移除未在代码中调用的多语言条目, 由于JS APP/WF/AOD的标题都是在C代码中获取和显示,所以为了避免名称被移除,需要在代码中显示调用以下应用名的多语言获取,如下:
文件操作
quickjs支持文件操作相关接口在 os module。
/*Open a file. Return a handle or < 0 if error.
Supported flags:
O_RDONLY
O_WRONLY
O_RDWR
O_APPEND
O_CREAT
O_EXCL
O_TRUNC
*/
open(filename, flags, mode = 0o666)
//Seek in the file. Use std.SEEK_* for whence. offset is either a number or a BigInt. If offset is a BigInt, a BigInt is returned too
seek(fd, offset, whence)
//Read length bytes from the file handle fd to the ArrayBuffer buffer at byte position offset. Return the number of read bytes or < 0 if error
read(fd, buffer, offset, length)
//Write length bytes to the file handle fd from the ArrayBuffer buffer at byte position offset. Return the number of written bytes or < 0 if error.
write(fd, buffer, offset, length)
//Return [str, err] where str is the current working directory and err the error code.
getcwd()
//Change the current directory. Return 0 if OK or -errno.
chdir(path)
//Create a directory at path. Return 0 if OK or -errno.
mkdir(path, mode = 0o777)
//Return [array, err] where array is an array of strings containing the filenames of the directory path. err is the error code. array contains at least "." and ".." if successful.
readdir(path)
//Close the file handle fd
close(fd)
注意
使用示例参考JA_app1
动画
提供了动画功能的模块,支持多实例,支持接口参考solution\components\quickjs\qjs_framework\lvgl_anim_qjs.c
,示例如下
import * as lv from "lv"
import { app } from "lvapp"
import { anim } from "anim" //anim 模块
class ja_app extends app {
constructor() {
super();
this.anim = undefined;
}
start() {
print("start");
this.anim = new anim();
a.set_values(0, 200); //x or y
a.set_path(a.PATH_LINEAR)
a.set_time(1000);
a.set_delay(2000);
a.set_start_cb(function(anim){
print("anim start");
});
a.set_ready_cb(function(anim){
print("anim ready");
});
a.set_exec_cb(function(val){
print(val);
});
//a.set_playback_time(200);
//a.set_playback_delay(200);
a.set_early_apply(false);
a.start();
}
resume() {
print("resume");
}
pause(){
print("pause");
}
stop(){
print("stop");
this.anim.del(); //避免循环引用
}
}
globalThis.JA_app5 = {
root:ja_app
};
app_get_str("key_qjs_name","ja_app5");
注意
在callback中如果存在循环引用,退出应用时需要将涉及的控件置空,打破循环引用
anim使用demo参考JA_app5
实例化动画模块的数量会影响js页面的帧率
SENSOR
内置封装了全局sensor模块,便于获取sensor数据,支持接口参考solution\components\quickjs\qjs_framework\lvgl_sensor_qjs.c
,示例如下
var level = sensor.BatLevel;
注意
sensor模块只支持getter,不支持setter
sensor模块是全局,JS代码中无需import
控件
TBD.
应用开发
方法实现
QJS 应用开发是通过创建一个继承自父类app 的class,在这个class中实现一些方法,从而使应用能够得到框架调度实现运行。
父类class app提供了以下方法使用:
start() 应用初始化,子类实现
resume() 应用恢复,子类实现
pause() 应用暂停,子类实现
stop() 应用销毁,子类实现
root() 子类用来获取当前的parent,创建控件必须基于此接口获取的parent创建
path() 子类获取当前的应用的路径,和具体的资源名组合成完整路径
keypad() 子类用以注册按键回调,见按键
wheel() 子类用以注册编码器回调,见编码器
task() 父类通过的task,只支持一个,子类创建或者销毁task,一般在resume()方法中创建,pause()方法中销毁,示例使用如下
pause(){
this.task(); // 销毁
}
resume(){
this.task(){
function(){
//do something; //处理
},
1000 //刷新间隔
};
}
子类class需要实现4个父类method:
start() 页面初始化,主要用来创建页面,获取初始化数据等
resume() 页面恢复,一般用来开启task
pause() 页面暂停,一般用来暂停task
stop() 页面销毁,一般用来释放自定义的控件数据
注意:不能存在空方法,如果某个方法为空,要么不实现,要么添加一个print打印。
应用框架使用lv obj的user_data,所以不能在js代码中或者自定义的控件使用user_data
task 刷新不能过于频繁,不能做大量计算和逻辑,一般都是推荐task中只调用接口,让C处理,提高效率
接口注册
qjs应用(app/wf/aod)通过定义全局唯一object来提供调用处理。以JA_app1应用为例,具体如下:
应用主程序页面(JA_app1_main.js)中定义object并初始化
globalThis.JA_app1 = {
root : app_root,
};
其他子页面中追加定义object的属性
globalThis.JA_app1.page1 = app_page1;
说明:
主程序页面中定义的变量名
JA_app1
必须和应用的目录名保持一致主程序页面中的属性名必须固定为
root
,值为页面代码中定义的class名追加的属性名
page1
必须和子页面文件名后半部相同应用名_page1.js
,值是页面代码中定义的class名在js的回调方法中,需要注意循环引用问题
APP开发
主页面文件命名规则是应用名_main.js,此例中为JA_app1_main.js
import * as lv from "lv" //lv obj module
import * as os from "os" //os模块
import * as std from "std" //std模块
import {app} from "lvapp" //app模块
import * as lv_enums from "/support_script/qjs/lv_enums.js" //枚举模块
import {qrcode} from "/support_script/qjs/qrcode.js" //qrcode控件
import {lvsfbarcode} from "/support_script/qjs/lvsfbarcode.js" //barcode 控件
import {app_get_str} from "./JA_app1_language.js" //引入多语言接口
//arraybuffer转字符串
function arrayBufferToString(buffer){
return String.fromCharCode.apply(null, new Uint16Array(buffer));
}
//字符串转arraybuffer
function stringToArrayBuffer(str) {
var buf = new ArrayBuffer(str.length * 2);
var bufView = new Uint16Array(buf);
for (var i = 0, strLen = str.length; i < strLen; i++) {
bufView[i] = str.charCodeAt(i);
}
return buf;
}
var string = "0705632441974";
var txt = "0123456-ABC-abcd";
class JA_app1 extends app{ //继承父类app, class 名需要和目录名相同
constructor() {
super(); //构造函数中调用父类app的构造函数
this.count = 0; //初始化变量
}
refresh() {
this.count++;
print("count ", this.count);
}
start() {
// Demo for QRcode
this.qrcode = new qrcode(this.root()); //this.root() 获取当前parent
this.qrcode.setparam(200, lv.color_make(0xc0,0xc0,0xc0), lv.color_make(0xFF,0xFF,0xFF));
this.qrcode.set_text(txt, txt.length);
this.qrcode.align(lv_enums.ALIGN_TOP_MID, 0, 0);
this.qrcode.set_event_cb( //set_event_cb是固定控件回调处理
function(event){
if (event==lv_enums.EVENT_SHORT_CLICKED){
print("qrcode event ", event);
lv.gui_app_self_exit(); //退出应用
}
}
);
// Demo for Barcode
this.barcode = new lvsfbarcode(this.root());
this.barcode.set_size(300, 100);
this.barcode.set_text(string, 66);
this.barcode.align(lv_enums.ALIGN_BOTTOM_MID, 0, 0);
this.barcode.set_event_cb(
function(event){
if(event == lv_enums.EVENT_SHORT_CLICKED){
print("barcode event ", event);
lv.qjs_app_page_create("page1"); //创建子页面page1
}
}
);
print("Started
");
}
resume() {
// Demo for OS file access
this.f=os.open("/a.txt", os.O_RDWR | os.O_CREAT | os.O_TRUNC); //打开文件
print("f=",this.f);
if(this.f > 0){
const buffer1 = stringToArrayBuffer('Hello, QuickJS File Operations!');
os.write(this.f, buffer1, 0, buffer1.byteLength) //写入数据
print("write:", arrayBufferToString(buffer1));
os.seek(this.f, std.SEEK_SET, 0);
const buffer = new ArrayBuffer(buffer1.byteLength);
os.read(this.f,buffer,0, buffer1.byteLength); //读取数据
print("read:", arrayBufferToString(buffer));
os.close(this.f); //关闭文件
}
// Demo for OS directory
os.chdir("/support_script/qjs"); //切换目录
print(os.getcwd()); //获取当前路径
var local_files=os.readdir("/"); //读取目录
print(local_files[0].length);
for (var i=0;i<local_files[0].length;i++)
{
print(local_files[0][i]);
}
// Demo for task
this.task(
function() {
this.refresh(); //刷新实现
}
, 1000 //刷新间隔
);
}
pause(){
this.task(); //销毁task
}
stop(){
print("stop"); //stop没有实际执行内容,增加打印。
}
}
//初始化赋值
globalThis.JA_app1 = {
root : JA_app1,
};
app_get_str("key_qjs_name","key_test"); //应用标题申明
多页支持
多页面文件命名规则是应用名_页面名.js,此例中为JA_app1_page1.js
import * as lv from "lv"
import * as os from "os"
import * as lv_enums from "/support_script/qjs/lv_enums.js"
import {app} from "lvapp"
import {label} from "/support_script/qjs/label.js"
class page1 extends app{
constructor() {
super();
}
start(){
this.label = new label(this.root());
this.label.set_local_font(lv_enums.FONT_SUPER, lv.color_make(0xff,0xff,0xff));
this.label.set_text("subpage 1");
this.label.align(lv_enums.ALIGN_CENTER, 0, 0);
this.label1 = new label(this.root());
this.label1.set_local_font(lv_enums.FONT_TITLE, lv.color_make(0xff,0xff,0xff));
this.label1.set_text("click to subpage 2");
this.label1.align(lv_enums.ALIGN_CENTER, 0, 60);
this.label1.add_flag(lv_enums.FLAG_CLICKABLE);
this.label1.set_event_cb(
function(event){
if(event == lv_enums.EVENT_SHORT_CLICKED)
lv.qjs_app_page_create("page2");
}
);
}
resume(){
print("subpage resume");
}
pause(){
print("subpage pause");
}
stop(){
print("subpage stop");
}
}
globalThis.JA_app1.page1 = page1;
WF开发
import * as lv from "lv"
import {app} from "lvapp"
import {analogclk} from "/support_script/qjs/analogclk.js"
class JW_wf1 extends app{ //继承父类app, class 名需要和目录名相同
constructor() {
super();
}
start() {
this.anaclk=new analogclk(this.root());
this.anaclk.pos_off(10,10,105);
this.anaclk.img(this.path() + "bg.bin", this.path() + "hour.bin", this.path() + "minute.bin", this.path() + "second.bin"); //this.path()获取当前路径
this.rate = 30;
}
pause() {
this.anaclk.refr_inteval(0);//暂停模拟表盘
}
resume() {
this.anaclk.refr_inteval(30);//刷新模拟表盘
}
}
globalThis.JW_wf1 = {
root : JW_wf1;
};
AOD开发
import * as lv from "lv"
import {app} from "lvapp"
import * as lv_enums from "/support_script/qjs/lv_enums.js"
import {analogclk} from "/support_script/qjs/analogclk.js"
class AOD_wf1 extends app{
constructor() {
super();
}
start() {
this.anaclk=new analogclk(this.root());
this.anaclk.pos_off(10,10,105);
this.anaclk.img(this.path() + "bg.bin", this.path() + "hour.bin", this.path() + "minute.bin", this.path() + "second.bin");
this.rate = 30;
}
pause() {
this.anaclk.refr_inteval(0);
}
resume() {
this.anaclk.refr_inteval(this.rate);
}
}
globalThis.AOD_wf1 = {
root : AOD_wf1,
};
demo说明
qjs demo应用存放在\solution\examples_dynamic_app\qjs目录中,具体说明如下:
应用 |
说明 |
---|---|
JA_app1 |
条形码,二维码,文件IO操作,task使用,多页面功能 |
JA_app2 |
按键,旋钮功能 |
JA_app3 |
数据存储和读取功能 |
JA_app4 |
图标功能,可以修改chart_type显示不同风格 |
JA_app5 |
多实例动画 |
JW_wf1 |
模拟表盘功能 |
JW_wf2 |
序列帧功能 |
JW_wf4 |
GIF功能 |
JW_wf5 |
ezipa功能 |
JW_wf6 |
数据订阅功能 |
AOD_wf1 |
熄屏显示功能 |
注意
JW_wf5需要硬件支持,不支持模拟器,其余应用模拟器均能正常显示
JW_wf6模拟器上无数据
编写建议
枚举类型的js文件建议根据类型进行拆分,不要一个文件过大,提高js解释运行速率
变量名尽量精简,减少内存占用
js程序中不建议进行大量计算,可以交给C去执行,js只负责调用接口