脚本编写的规则与技巧
1.脚本编写的位置
串口屏脚本是实现功能的重要方式,只需要掌握一点基础的C语言知识,了解我们屏幕的脚本编写规则,就能实现各种功能。
1.1 可见控件下的脚本
此类脚本是最常见和常用的
例如:一个普通按钮控件下的 按下事件 和 弹起事件
按下事件:当按下按钮时,执行一次按下事件里面的脚本;
弹起事件:当松开按钮时,执行一次弹起事件里面的脚本;
除了按钮控件有按下弹起事件外,还有:整数控件、浮点数控件、文本控件、触摸热区控件都有按下弹起事件,这些控件脚本执行逻辑和按钮控件一样。
双态按钮控件,也属于按钮的一种,可看作一个开关控件,点击后松开执行一次按下事件的脚本,再次点击后松开执行一次弹起事件的脚本。
单选控件、多选控件、滑块控件,都有一个值改变事件,当控件的选中值变化时,则会执行其控件下的脚本。
1.2 不可见控件下的脚本
常见于协议处理控件、定时器控件、音频控件等
不可见控件:拖入显示设计区域后,没有实际控件显示,而是在显示区域下方;
协议解析类控件,会在屏幕串口接收到数据时,执行控件下的脚本,每接收一次就执行一次。
定时器控件,在打开定时器后(勾选en属性),会以一个间隔时间循环执行里面的脚本。
音频控件的播放完成事件,会在音频播放完成后执行里面的脚本。
1.3 页面事件脚本
分为 页面加载事件 和 页面离开事件
每个页面都会有加载和离开两个动作事件,都是在加载页面和离开页面时,执行一次里面的脚本。
通常在页面加载和离开事件里做一些初始化的操作。
备注
在多行脚本的中间某行,插入一个翻页的操作,翻页操作后面的脚本会在跳转到其他页面后继续执行,直至执行完所有脚本。
在多行脚本的中间某行,插入一个关闭定时器的操作,定时器关闭后会继续执行剩余的脚本,而不是等待定时器内脚本执行完后再执行剩余的脚本。
脚本的执行顺序:触发某事件后,由第一行依次执行代码直至最后一行。
小技巧
脚本编辑区的三个功能:
脚本搜索:用于在当前脚本区域检索指定的内容,脚本很多时方便定位。
编辑区锁定:有锁定和解锁两种状态。当锁定时,脚本编辑区会锚定所选的控件,点击其他控件,脚本编辑区不会切换过去,免去了频繁的切换。
编辑区放大:可以将编辑区窗口放大至全屏,便于编辑脚本,配合锁定功能食用,效果更佳。
2.关键字
目前支持的关键字(未提到的则不支持):
控制结构关键字:
if:条件判断语句,用于执行基于条件的不同分支。
else:与 if 结合使用,用于在条件不成立时执行另一段代码。
while:循环语句,用于重复执行一段代码直到条件不满足。
do:循环语句,与 while 结合使用,保证循环体至少执行一次。
for:循环语句,用于重复执行一段代码,可以同时初始化、测试和更新循环变量。
break:跳出当前循环。
continue:跳过当前循环体中的剩余代码,继续下一次循环。
数据类型关键字:
void:表示没有类型或不确定类型,常用于函数声明中表示无返回值。
int:整型数据类型。
float:单精度浮点类型。
其他关键字:
return:用于从函数中返回值。
3.变量的定义
3.1 脚本中定义变量
脚本代码中支持定义变量:int、float、byte,脚本中定义的都为局部变量,作用域为当前脚本代码编辑区,临时变量无法跨控件脚本使用,例如:在按钮控件下定义的临时变量int i = 10;
,无法在其他控件的脚本下去调用变量 i。同样的,如按钮控件的按下事件里定义的变量,无法在弹起事件里调用。
脚本中定义int型:
//支持的写法:
int a;
int b = 123;
a = 666;
//不支持的写法:
int a , b;
int a , b = 123;
脚本中定义float型:
//支持的写法:
float f;
float f1 = 1.23;
f = 3.14;
//不支持的写法:
float f , f1;
float f , f1 = 1.23;
脚本中定义byte型数组:
//支持的写法:
byte arr[5] = {0x01 , 0x02 , 0x03 , 0x04 , 0x05};
byte a[5];
a[0] = 0x01;
a[1] = 0x02;
a[2] = 0x03;
a[3] = 0x04;
a[4] = 0x05;
//将某个整数控件的显示值赋给数组某个元素(int型不能直接赋值给byte型,所以用强制转换处理)
a[4] = (byte)num1.val;
脚本中定义int型数组:
//支持的写法:
int a[5] = {0x01 , 0x02 , 0x03 , 0x04 , 0x05};
int b[5] = {1 , 2 , 3 , 4 , 5};
int c[5];
c[0] = 1;
c[1] = 2;
c[2] = 3;
c[3] = 4;
c[4] = 5;
//将某个整数控件的显示值赋给数组某个元素
c[4] = num1.val;
脚本中定义float型数组:
//支持的写法:
float f[5] = {1.1 , 2.1 , 3.1 , 4.1 , 5.1};
float f1[5];
f1[0] = 1.1;
f1[1] = 2.1;
f1[2] = 3.1;
f1[3] = 4.1;
f1[4] = 5.1;
//将某个浮点数控件的显示值赋给数组某个元素
f1[4] = numf1.valf;
需要注意,脚本中变量的定义必须在最前面,否则会编译报错。
正确:定义在赋值语句前面
错误:定义在赋值语句中间
3.2 变量控件
脚本中定义的变量都为局部变量,而要实现全局变量,就需要用变量控件 。
变量控件为不可见控件,拖入后会以控件名的形式存在于设计面板下方:
选中变量控件,可在右侧属性栏设置:
勾选global属性后,可全局使用(整个工程都可调用),变量控件下无法写脚本,只能被调用。
跨页调用控件属性时,需加上控件所在页面的页名称,如:
page1.var2.val = 123;
表示给page1页面的var2控件赋值123。type属性:定义此变量控件的变量类型。
当type选择Int:
var2.val = 123;//赋值
num1.val = var2.val;//取值,用整数控件num1显示
当type选择Float:
var2.valf = 3.14;//赋值
numf2.valf = var2.valf;//取值,用浮点数控件numf2显示
当type选择String:
var2.txt = "";//赋空值
var2.txt = "ABC123";//赋值字符串,注意双引号必须为英文引号
text3.txt = var2.txt;//取值,用文本控件text3显示
String类型变量无法在脚本中直接定义,所以用变量控件实现,或者用文本控件。
备注
type属性选择Int/Float/String时,对应的值属性为val/valf/txt,若设置的类型和调用的值属性不一致,则会编译报错。
Int/Float/String三种类型的变量,可以用整数控件/浮点数控件/文本控件对应代替。
3.3 数组控件
脚本中定义的数组只限制于局部使用,而要实现全局使用,就需要用数组控件 。
数组控件为不可见控件,拖入后会以控件名的形式存在于设计面板下方:
选中数组控件,可在右侧属性栏设置:
勾选global属性后,可全局使用(整个工程都可调用),数组控件下无法写脚本,只能被调用。
type属性:定义此数组控件的数据类型。
type 属性选择 Byte 时:
赋值:arr3.valbs[0] = 0x64;
或者 arr3.valbs[0] = 100;
读取:num1.val = arr3.valbs[0];
//读出用整数控件 num1 显示,显示值为 100
清除:arr3.clear(0,10);
//清除从下标 0 开始,向后的 10 个元素
type 属性选择 Int 时:
赋值:arr3.vals[0] = 0x64;
或者 arr3.vals[0] = 100;
读取:num1.val = arr3.vals[0];
//读出用整数控件 num1 显示,显示值为 100
清除:arr3.clear(0,10);
//清除从下标 0 开始,向后的 10 个元素
type 属性选择 Float 时:
赋值:arr3.valfs[0] = 100.0;
读取:numf1.valf = arr3.valfs[0];
//读出用浮点数控件 numf1 显示,显示值为 100.0
清除:arr3.clear(0,10);
//清除从下标 0 开始,向后的 10 个元素
type 属性选择 String 时,需要用控件的方法函数:
赋值:arr3.set(0,"txt123");
//赋值字符串”txt123”
读取:text1.txt = arr3.get(0);
//读出用文本控件 text1 显示,显示值为”txt123”
清除:arr3.clear(0,10);
//清除从下标 0 开始,向后的 10 个元素
备注
目前只支持一维数组。且只支持上述类型。
可在属性面板设置数组长度,数组控件的元素索引都是从0开始。
type属性选择Byte/Int/Float/String时,对应的值属性为valbs/val/valf/方法函数,若设置的类型和调用的值属性不一致,则会编译报错。
考虑到Byte型和Int型之间相互赋值的问题,目前支持的强制转换:(byte)、(int)。
勾选数组控件的global属性后,可全局使用,跨页调用控件属性时,需加上控件所在页面的页名称,如:
page1.arr3.vals[1] = 0x64;
表示给page1页面的数组控件arr3的第二个元素赋值0x64。
4.控件属性赋值
对控件的操作,就是调用控件属性。
4.1 控件属性的调用方式
三易串口屏有固定的调用属性方式:控件名.属性名。
使用:在脚本中输入实际的控件名称(控件的name属性),然后用符号‘ . ’去定位使用某个属性。
例如:拖入一个整数控件,名称为num1,让控件显示整数‘123’的脚本为num1.val = 123;
,这里调用了控件的值属性‘val’。
编写脚本时,注意使用所拖入控件的实际名称。
4.2 常用属性调用举例
控件的位置:
num1.x = 10;//x值
num1.y = 20;//y值
//x、y属性,可实现弹出类功能,或者连续改变坐标值实现移动效果
//当x、y值大于串口屏实际分辨率时,控件会位于显示区域之外,达到‘隐藏’效果
控件的大小:
num1.width = 60;//控件的宽度
num1.height = 60;//控件的高度
控件的显示与隐藏:
num1.visible = 0;//控件隐藏
num1.visible = 1;//控件显示
//visible属性,通常用来实现提示功能或选择功能
//visible属性,赋值0时为隐藏,非0时都是显示
控件的透明度:
num1.opacity = 0;//控件透明度为0(最小值,完全透明看不见)
num1.opacity = 100;//透明度为100(最大值,完全显示)
//opacity属性,目前G系列不支持
颜色的相关属性:
num1.fontColor = 0xFF000000;//设置控件显示的字体为黑色
num1.bgColor = 0xFF00FF00;//设置控件的背景色为绿色(前提是控件属性中设置的背景类型为颜色)
page0.bgColor = 0xFF00FF00;//设置page0页面的背景色为蓝色(前提是页面属性中设置的背景类型为颜色)
//颜色相关属性,赋值格式为:0xFF+颜色的hex代码
图片的相关属性:
num1.bgImg = user.image.p1;//设置控件的背景为图片p1(前提是控件属性中设置的背景类型为图片)
num1.bgImg = 1;//设置控件的背景为id是1的图片(图片资源栏中,每张图片都有独自的id)
page0.bgImg = user.image.p1;//设置page0页面的背景为图片p1(前提是页面属性中设置的背景类型为图片)
//上述介绍了两种调用图片的方式,推荐使用:user.image.图片名称,调用图片id的方法可能因为图片资源的增删而导致显示出错
//图片名称需要使用:字母和数字的命名组合,才能在脚本中调用
字体的相关属性:
num1.font = 1;//num1控件使用字体id为1的字体
//字体资源栏,每个字体有单独的id,脚本中去更换某个控件使用的字体时,直接赋值id号
赋显示值的相关属性:
num1.val = 123;//给整数控件num1赋值显示123
numf1.valf = 3.14;//给浮点数控件numf1赋值显示3.14
text1.txt = "ABC123";//给文本控件text1赋值显示字符串"ABC123"
text1.txt = "ABC"+"123";//给文本控件text1赋值显示字符串"ABC123"
text1.txt = text2.txt + text3.txt;//将text2和text3的显示字符拼接起来给text1
//字体资源栏,每个字体有单独的id,脚本中去更换某个控件使用的字体时,直接赋值id号
备注
上述举例使用的整数控件num1,编写脚本时请使用所拖入控件的实际名称。
上述举例的属性为可见控件所有,控件实际的属性需选中控件后在属性面板查看。
注意控件属性有可读写和只读写的区别,脚本调用时会有汉字提示;所有控件的name属性,都为只读,不可脚本内修改。
跨页面调用控件属性:
//跨页调用控件属性的前提条件:必须勾选控件的global属性,否则会编译报错
page2.num1.val = 123;//给page2页面中的num1控件赋值123
page3.num3.x = 50;//将page3页面中的num3控件横坐标x值改为50
//对于没有global属性的控件(例如协议解析类的控件),可以直接全局调用
定时器的属性:
//勾选控件的global属性后会全局运行,否则只在当前页面运行
timer1.en = 0;//关闭定时器(1:打开定时器)
//若在定时器运行过程中关闭定时器(执行到某行脚本时关闭),定时器会在执行完后续所有脚本后关闭
备注
因为控件和对应属性较多,无法一一列举,以上列举的属性为部分常用属性作为参考,更多介绍请参考控件的介绍和使用。
5.运算符
5.1 支持的运算符
算术运算符
运算符 |
描述 |
示例 |
---|---|---|
|
加法 |
|
|
减法 |
|
|
乘法 |
|
|
除法 |
|
|
取模(求余数) |
|
关系运算符
运算符 |
描述 |
示例 |
---|---|---|
|
等于 |
|
|
不等于 |
|
|
小于 |
|
|
大于 |
|
|
小于等于 |
|
|
大于等于 |
|
逻辑运算符
运算符 |
描述 |
示例 |
---|---|---|
|
逻辑与(AND) |
|
|
逻辑或(OR) |
|
|
逻辑非(NOT) |
|
位运算符
运算符 |
描述 |
示例 |
---|---|---|
|
按位与 |
|
|
按位或 |
|
|
按位异或 |
|
|
按位取反 |
|
|
左移 |
|
|
右移 |
|
赋值运算符
运算符 |
描述 |
示例 |
---|---|---|
|
简单赋值 |
|
|
加法赋值 |
|
|
减法赋值 |
|
|
乘法赋值 |
|
|
除法赋值 |
|
|
取模赋值 |
|
|
按位与赋值 |
|
|
按位或赋值 |
|
|
按位异或赋值 |
|
|
左移赋值 |
|
|
右移赋值 |
|
备注
上述运算符优先级关系同C语言。
5.2 运算符的使用
基础的加减乘除大于小于等不做介绍,这里着重介绍一下移位的相关写法。
多字节拼接
在处理协议数据的时候,经常会碰到两个甚至多个字节来传递数据的情况。
例如,串口屏的协议解析器控件 protocol3 收到一帧6字节hex数据:0A 0B 12 13 14 05
,收到后会以数组形式缓存在protocol3.rxBuf
属性中。
//基础知识:1 字节(Byte) = 8 比特(Bits)
num1.val = (protocol3.rxBuf[0] << 8) | protocol3.rxBuf[1];//将0A 0B 两个字节拼接成一个数给num1显示
num1.val = (protocol3.rxBuf[2] << 16) | (protocol3.rxBuf[3] << 8) | protocol3.rxBuf[4];//将12 13 14 三个字节拼接成一个数给num1显示
Bit位
比特(Bit):
比特是计算机中最小的数据单位,只能存储两种状态:0 或 1。
通常用来表示二进制位。
例如,5的二级制数为00000101。大多数情况下,比特位的计数是从0开始的,即最右边的比特位是第0位,最左边的比特位是第7位
if((5 & 0x01) != 0)//判断bit0,若为1,则会进入if,若为0,则进入else
{
text1.txt = "打开";
}else
{
text1.txt = "关闭";
}
判断一个数的某个bit位状态,只需要按位与固定的值
比特位 |
二进制表示 |
十六进制值 |
---|---|---|
bit0 |
00000001 |
0x01 |
bit1 |
00000010 |
0x02 |
bit2 |
00000100 |
0x04 |
bit3 |
00001000 |
0x08 |
bit4 |
00010000 |
0x10 |
bit5 |
00100000 |
0x20 |
bit6 |
01000000 |
0x40 |
bit7 |
10000000 |
0x80 |
… |
… |
… |
所以,若需要判断bit6的状态:
if((5 & 0x40) != 0)//判断bit6,若为1,则会进入if,若为0,则进入else
{
text1.txt = "打开";
}else
{
text1.txt = "关闭";
}
备注
能实现判断bit状态的写法有很多种,例如还可以通过右移
>>
将需要判断的bit移到bit0的位置,再按位与上1。
按位与、按位或配合使用,控制bit位状态的改变:
num1.val = num1.val & 0xFE;//bit 0 置0
num1.val = num1.val | 0x01;//bit 0 置1
num1.val = num1.val & 0xFD;//bit 1 置0
num1.val = num1.val | 0x02;//bit 1 置1
num1.val = num1.val & 0xFB;//bit 2 置0
num1.val = num1.val | 0x04;//bit 2 置1
num1.val = num1.val & 0xF7;//bit 3 置0
num1.val = num1.val | 0x08;//bit 3 置1
num1.val = num1.val & 0xEF;//bit 4 置0
num1.val = num1.val | 0x10;//bit 4 置1
num1.val = num1.val & 0xDF;//bit 5 置0
num1.val = num1.val | 0x20;//bit 5 置1
num1.val = num1.val & 0xBF;//bit 6 置0
num1.val = num1.val | 0x40;//bit 6 置1
num1.val = num1.val & 0x7F;//bit 7 置0
num1.val = num1.val | 0x80;//bit 7 置1
6.逻辑语句
6.1 if语句
if语句形式:
if(条件表达式)
{
}
//用法1:判断数值
if(num1.val == 1)//若整数控件显示的值为1,则文本控件text1显示正确
{
text1.txt = "正确!";
}
//用法2:判断字符串
if(text1.txt == "打开")//若文本控件text1显示的“打开”,则改变显示为“关闭”
{
text1.txt = "关闭";
}
//用法3:判断图片
if(num1.bgImg = user.image.p1)//若整数控件显示的背景图片为p1,则改变背景图片显示为p2(前提条件为控件属性中背景类型选择且绑定的图片)
{
num1.bgImg = user.image.p2;
}
//用法4:判断颜色
if(num1.bgColor == 0xFF000000)//若整数控件显示的背景颜色为黑色,则改变背景颜色显示为白色(前提条件为控件属性中背景类型选择的颜色)
{
num1.bgColor = 0xFFffffff;
}
//用法5:配合关系运算符使用
if(num1.val >= num2.val)//若num1的值大于等于num2的值,则num3加1
{
num3.val++;
}
//用法6:配合逻辑运算符使用
if((num1.val > 100) && (num1.val < 200))//若num1的值大于100,且小于200,则num3减1
{
num3.val--;
}
if else 语句形式:
if(num1.val == 1) //若满足if的判断条件,则进入if分支,否则进入else
{
num1.val = 2;
}else
{
num1.val = 3;
}
if(num1.val == 1) //若满足if的判断条件,则进入if分支,否则判断下一个if是否满足
{
num1.val = 2;
}else if(num1.val == 2)
{
num1.val = 3;
}else
{
num1.val = 0;
}
if(num1.val == 1)//if语句的嵌套使用
{
if(num2.val == 1)
{
num1.val = 2;
}else
{
num1.val = 3;
}
}else
{
num1.val = 0;
}
6.2 for语句
for语句:
//用于重复执行一段代码,通常用于已知迭代次数的情况
for(赋值表达式; 条件表达式; 赋值表达式)
{
}
int i;
num1.val = 1;
//使用for循环计算10的阶乘,并用整数控件num1显示结果
for (i=1;i<=10;i++)
{
num1.val *= i;
}
//同样的,for语句也支持嵌套使用
6.3 while语句
while语句:
//用于重复执行一段代码,通常用于未知迭代次数的情况
while(条件表达式)
{
}
int i;
num1.val = 1;
//使用while循环计算10的阶乘,并用整数控件num1显示结果
while (i <= 10)
{
num1.val *= i;
i++;
}
6.4 do while语句
//用于重复执行一段代码,至少执行一次,即使条件一开始就不满足
do
{
num1.val++;
}while (num1.val < 50);//当num1自增到50时,循环停止
6.5 break 语句
//用于立即退出循环
int i;
for(i=0;i<100;i++)
{
if (i == 50)
{
break;
}
num1.val = i;
}