Node-red基于JS,这是前端技术的“三驾马车”之一。前端技术主要负责界面呈现,与用户交互等等,很多炫酷的特效都是前端呈现的。Node-red技术有这样的“基因”,界面当然不会差劲。它只需要一个控件就可以实现一个页面。由于本书讲述的应用与页面关系不大,就不讲HTTP与websocket相关的控件了。强烈建议感兴趣的读者自己研究一下。
Node-red支持自定义节点,当然也就支持自定义图形化的节点。也有优秀的开发者把自己建立的图形化节点无偿分享。这里给出一个股票界面的例子,让大家看一看优秀的node-red界面能做到什么样子。
我们常用的图形化节点叫做仪表板,dashboard,也能做出效果不错的界面,例如
Dashboard还有一些其它控件,例如
仪表板的安装与串口类似,在安装节点的输入框内输入“dashboard”,找到名为“node-red-dashborad”的控件并点击安装即可。
安装完以后在屏幕左边会多出很多新的控件
我们从中随便选择一个控件来检验安装好的控件是否能够使用。在deshboard的控件区找到“date picker”控件,拖入工作区,然后双击节点,点击Group输入框后边的编辑按钮,如图所示,把Group节点与Tab节点分别命名为Group与Tab。
拖入一个debug节点并连线部署。在浏览器中输入地址http://localhost:1880/ui
能正确打开此网页,已经可以说明安装成功了。
然后可以看到名为date的一行内容。
我们可以随便选择一个日期,例如北京奥运会开幕
在调试窗口下就可以看到一个时间戳。
能用一个控件很简单地就搭建出一个看上去还不错的网页,这就是dashboard的方便之处。
在已经使用过仪表板以后再来看它的简介,不容易被弄糊涂。
dashboard主要用于快速创建实时数据仪表板。它需要node-red版本为0.14或更高。
仪表板的布局依赖于Tab和Group属性。Tab可以理解为页面,Group是分组。Tab可以包含Group。我们在选择“北京开幕式”那天的图片里,就有一个名为Group的分组。每个组的元素默认宽度是6个单位。每个单位默认宽度是48PX,间距6PX。一个页面里可以有多个分组,建议使用多个分组,而不是一个大组。因为node-red可以根据页面的大小动态调整分组的位置。
使用dashboard节点时,屏幕右侧“调试窗口”的旁边会多一个名为dashborad的小标签,下边有Layout,Theme和Site三个选项。
Layout意思是布局,在Layout里可以重新排列Tab,Group与控件,并编辑其属性。也可以把其它网页添加到Tab中。
Theme意思是主题。可以选明亮的,或者暗的,或者自定义。
Site意为地址,可以设置标题的UI,或者选择标题栏。也能够以像素为单位设置网格布局的基本图形,就是刚刚提到默认是48像素的那个“单位”,或者单独设置控件,组的大小。
Dashboard的控件一般都可以设置Label或名字,Label和名字也可以通过传入消息的属性来指定或修改。其实控件的内容都可以用消息来配置,这属于比较高阶的用法,感兴趣的可以参考https://github.com/node-red/node-red-dashboard/blob/master/config-fields.md
Dashboard带有的控件简介见下表。
名称 中文名 详细信息
button 按钮 可以点击,发送一个msg
dropdown 下拉菜单 可以指定标签与值的对应关系
switch 开关 添加一个开关到用户界面,开关变化的切换可以产生一个msg
slider 滑块 水平滑块,可变步长
numeric 数字选择 带有上、下按钮的数字输入控件
text input 文本输入 带有可选标签的文本,还可以支持密码,电子邮件和颜色模式
date picker 日期选择器 用于选择日期
colour picker 拾色器 用于选择颜色
form 表单 一个可由多个子小部件组成的小部件。提交时,所有值均作为单个消息提交。
text 文本 一个只读控件,可以配置Label和Value
gauge 仪表 有四种模式,标准,甜甜圈,指南针和波形。可以指定标准仪表或甜甜圈测量仪的颜色范围
chart 图表 具有折线、条形与饼图模式,可以使用日期格式化程序字符串来自定义X轴标签
audio out 音频输出 用于播放音频或发送文本到语音客户端
notification 通知 为用户创建警报,可以是弹出窗口或是报警框
ui control UI控制 允许对仪表板进行动态控制
template 模板 模板节点允许用户使用HTML,Javascript在框架内创建自己的小部件。
从仪表板的简介表中可以看出,仪表板控件的类型可以分为两种:一种是输入型,功能类似于inject控件,用于生成msg。另一种是显示型,或者说是输出型,与debug控件类似,用于展示某些数据。本章节只讲述输入型的控件。
按钮的帮助信息
向用户界面添加一个按钮。
单击该按钮会生成带有载荷的消息。如果没有指定载荷,则使用节点id。
按钮的图标、文字、背景都可以自定义,也可以使用消息属性来设置,例如msg.background,msg.topic。还可以使用 msg.enabled来使能或禁用按钮。
使用msg来设置的方法要用到函数节点,比如在函数节点内使用代码设置msg.background为“black”,并返回msg。此处对于使用函数来设置控件的方法不做讲解。
在工作区拖入一个按钮节点,并把Tab和Group分别设置为home和control。
再拖入一个debug节点,连线并部署。
在http://localhost:1880/ui页面下就可以看到刚刚设置的的按钮,点击按钮,可以在调试窗口看到以下信息。
这说明,点击按钮以后,按钮可以给流注入一个数据包。
我们在编辑按钮节点时,其实有很多选项都空着,例如尺寸、图标、标签、颜色等等。合理使用这些选项,即便是只用按钮控件也能做出来一些有意思的东西,例如做一个简易的电话拨号界面。
接下来结合这和拨号界面,来讲讲我们设置了哪些参数。以下是button节点可以设置的参数。
首先,很直观可以看到,数字的背景是不一样的,123的背景是灰色,456的背景是浅蓝,
所以123控件的Background处应当填写“gray”。
按键的大小也是不一样的。一般来说,按键的大小默认是自动,也就是长度跟随Group,宽度是1。Group的宽度是多少呢?默认是6,可以修改,拨号界面就改成了3。修改的界面在屏幕右侧的dashboard界面下:
Width就是宽度。顺带提一下,下边的两个选项分别是显示组的名字,和允许收缩。效果就是这样的:
显而易见,普通数字按钮的宽度都是1,拨号键的宽度是2。
还有文字的颜色不一样。这个就很简单了,修改colour即可,比如数字的文字都是black。
另外,还可以看出,拨号的按键上有一个小小的电话图标;按键0的大小好像比别的数字也大一点,这其实也是一个图标。图标如何设置?
在别的软件里,设置图标可能需要一个本地的图片。Node-red使用的是一个在线的图标库(不联网的时候显然没办法使用),地址是https://material.io/tools/icons/?style=baseline
这个网页里有很多图标,我截取一些放在这里:
每个图标下边都有名字,如果你需要使用这个图标,把名字输入到Icon后边即可,例如按键0的图标:
既然是电话拨号按键,当然要有拨号的功能。我们让每一个数字按键被按下去的时候,都可以把数字发送到流里边去;再按下“CALL”的时候,就把之前按下的数字全部打印出来,暂时用debug节点来显示。
分析以后,发现需要一个函数节点利用context功能保存之前按下的数字,等到收到CALL的命令以后,一次性打印出来。函数节点的程序如下:
var temp = context.get('num')||"";
if (msg.topic != "call"){
temp = temp + ""+msg.payload;
context.set('num',temp);
}
else{
msg.payload = temp;
return msg;
}
如图设置流:
如果在仪表板的界面,有数字的顺序是颠倒的,可以再屏幕右侧dashboard页面下通过拖拽节点调整顺序:
我们来“拨打”友道(我的公司)的电话,借以观察现象:按下数字按键时,并不会显示单个数字;按下CALL以后,一串电话号码都能显示出来。
我们举例子详细地介绍了按钮控件的各个选项的作用,其它仪表板控件的选项功能类似,聪明的读者要学会举一反三哦。
代码在这里,导入你的node-red工作区好好研究下吧。
[{"id":"e9614f74.8d223","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":1,"width":"1","height":"1","passthru":false,"label":"1","color":"black","bgcolor":"gray","icon":"","payload":"1","payloadType":"num","topic":"","x":190,"y":60,"wires":[["cc35b265.c22d"]]},{"id":"b98d8139.972c8","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":2,"width":"1","height":"1","passthru":false,"label":"2","color":"black","bgcolor":"gray","icon":"","payload":"2","payloadType":"num","topic":"","x":190,"y":100,"wires":[["cc35b265.c22d"]]},{"id":"ded462b1.c2e2b","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":3,"width":"1","height":"1","passthru":false,"label":"3","color":"black","bgcolor":"gray","icon":"","payload":"3","payloadType":"num","topic":"","x":190,"y":140,"wires":[["cc35b265.c22d"]]},{"id":"857ebe34.c8d85","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":4,"width":"1","height":"1","passthru":false,"label":"4","color":"black","bgcolor":"light blue","icon":"","payload":"4","payloadType":"num","topic":"","x":190,"y":180,"wires":[["cc35b265.c22d"]]},{"id":"f11b9214.04659","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":5,"width":"1","height":"1","passthru":false,"label":"5","color":"black","bgcolor":"light blue","icon":"","payload":"5","payloadType":"num","topic":"","x":190,"y":220,"wires":[["cc35b265.c22d"]]},{"id":"94ebe434.733518","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":6,"width":"1","height":"1","passthru":false,"label":"6","color":"black","bgcolor":"light blue","icon":"","payload":"6","payloadType":"num","topic":"","x":190,"y":260,"wires":[["cc35b265.c22d"]]},{"id":"4043b4bf.bcc71c","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":7,"width":"1","height":"1","passthru":false,"label":"7","color":"black","bgcolor":"orange","icon":"","payload":"7","payloadType":"num","topic":"","x":190,"y":300,"wires":[["cc35b265.c22d"]]},{"id":"2301a306.95065c","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":8,"width":"1","height":"1","passthru":false,"label":"8","color":"black","bgcolor":"orange","icon":"","payload":"8","payloadType":"num","topic":"","x":190,"y":340,"wires":[["cc35b265.c22d"]]},{"id":"aa51af93.26b73","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":9,"width":"1","height":"1","passthru":false,"label":"9","color":"black","bgcolor":"orange","icon":"","payload":"9","payloadType":"num","topic":"","x":190,"y":380,"wires":[["cc35b265.c22d"]]},{"id":"85bdbffa.6a1dc","type":"ui_button","z":"c1c86dfa.43a1a","name":"","Group":"72c43141.3520c","order":0,"width":"2","height":"1","passthru":false,"label":"call","color":"yellow","bgcolor":"green","icon":"phone","payload":"0","payloadType":"num","topic":"call","x":190,"y":460,"wires":[["cc35b265.c22d"]]},{"id":"cc35b265.c22d","type":"function","z":"c1c86dfa.43a1a","name":"储存数字,拨号","func":"var temp = context.get('num')||\"\";\nif (msg.topic != \"call\"){\n temp = temp + \"\"+msg.payload;\n context.set('num',temp);\n}\nelse{\n msg.payload = temp;\n context.set('num',\"\");\n return msg;\n}\n","outputs":1,"noerr":0,"x":560,"y":240,"wires":[["c96d8af2.f3a4b8"]]},{"id":"c96d8af2.f3a4b8","type":"debug","z":"c1c86dfa.43a1a","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":750,"y":240,"wires":[]},{"id":"d011a93b.f06a78","type":"ui_button","z":"c1c86dfa.43a1a","name":"0","Group":"72c43141.3520c","order":9,"width":"1","height":"1","passthru":false,"label":"","color":"black","bgcolor":"orange","icon":"exposure_zero","payload":"0","payloadType":"num","topic":"","x":190,"y":420,"wires":[["cc35b265.c22d"]]},{"id":"72c43141.3520c","type":"ui_Group","z":"","name":"phone","tab":"aacbcc03.d359b","disp":true,"width":"3","collapse":true},{"id":"aacbcc03.d359b","type":"ui_tab","z":"","name":"home","icon":"dashboard"}]
接下来修改按钮,
Dashboard中的switch可以增加一个开关到用户界面。
开关状态的每一个变化都会产生一个带有on或off值的msg.payload。
开关的颜色与图标是可以配置的,也可以由传入的msg更新,方法跟button类似。
之前已经用过函数控件switch了,当时用“岔路口”来比喻那个函数控件。仪表板中的switch是真正的开关,功能上像是一个具备“开”与“关”两种状态的按钮。
直接拖入一个switch节点,修改它的Group和Tab,并与debug节点连接,部署。
在http://localhost:1880/ui/下可以看到这个开关。点击开关可以看到调试窗口下输出了true。
其中的true与false当然也可以改为别的载荷,跟inject节点类似
手机在调节屏幕亮度的时候,常常出现一个横线,串着一个圆球,拖拽圆球就可以改变屏幕亮度。横线加圆球组成的就是一个滑块。看上去有点像被吃的只剩下一个的糖葫芦,或者就叫做滑球更贴切。
Dashboard中的滑块可以帮助用户在最小值与最大值的范围内,改变设置的值。每次变化都会把值作为消息的载荷。
拖入一个slider控件,修改Group与Tab,然后与debug节点连接,部署。
滑块形状如下
拖动滑块,可以在调试窗口见到当前滑块的值。
注意,假如需要从0到3,那么拖动滑块时,1和2的值也会发送。可以直接点击到3的位置。
默认情况下,滑块的范围是从0到10,步进1,也可以修改,例如10到80,步进2。
Text input可以向用户界面添加一个文本输入区域,格式可以是常规文本、电子邮件或颜色选择器等。
输入都以msg.payload的形式发送。也可以通过输入msg的载荷来预设输入的文本。
Delay参数可以设置从输入字符到发送的延时,默认是300毫秒。也可以设置为0,等待按下“Enter”或“Tab”键发送。
电子邮件模式可以使用红色来表示无用的地址。
时间输入类型返回从午夜开始的毫秒值。
接下来通过实践来验证下text input的功能。
拖入一个text input节点,修改Group与Tab,然后与debug节点连接,并部署。
输入任意的文字,在调试窗口可以看到。
不过,你需要输入的稍微快一点,两个字符输入间隔大于300ms的话就会输出不止一条,你会看到这样的结果:
所以我建议把输入的延时设置为0,用“Enter”和“Tab”作为结束的标记。
再来试一试输入框的别的功能,比如密码:
我们把Label也写上,在仪表板的页面可用于显示一些提示。
输入密码时,提示的文字会变小,且密码会用点来代替。
在“number”的模式下,没办法输入其它的字符。
文本输入的模式里有颜色选择,可以调出一个取色板,并返回颜色的编码。我们在讲述按钮的时候,说过可以使用msg来设置按钮,这里正好来把两者结合一下。
首先,我们把text input的模式设置为picker,并用debug节点来看一看选中颜色以后会输出什么。
点击仪表板页面的颜色输入框,可以看到这样的一个取色板:
我们随便选一个颜色并点击确定。比如红色。再来看调试窗口的信息。
收到消息是#ff0000,这就是红色的编号。我们也可以在设置颜色的页面用这种方式。用颜色选择器看颜色的编码,再进行颜色设置的办法很方便。7.2.1小节设置按钮的颜色时,想出gray,orange和light blue这几个单词我都有点绞尽脑汁了。
接下来改进程序,由于比较简单,我就直接把源码附在下边,你们看一看源码应该就能理解。需要注意的点就是,用change节点把msg.payload的属性赋给msg.colour,然后button里的colour并不是空着,而是{{msg.colour}}。Background直 接用编码来设置。
效果就是,不论你选择什么颜色,中间的心就变成什么颜色。
也可以使用这种方法来动态地设置背景或文字的内容,感兴趣的同学可以自己研究下。参考代码在这里:
[{"id":"1fbb5eaf.febb51","type":"ui_text_input","z":"7a8c632b.0c701c","name":"","label":"选择文字或图标颜色","group":"bd84de44.ae507","order":0,"width":0,"height":0,"passthru":true,"mode":"color","delay":"300","topic":"","x":220,"y":120,"wires":[["69912933.9c62e8","4dd666d2.d144e8"]]},{"id":"4dd666d2.d144e8","type":"debug","z":"7a8c632b.0c701c","name":"","active":true,"tosidebar":true,"console":false,"tostatus":false,"complete":"false","x":390,"y":60,"wires":[]},{"id":"69912933.9c62e8","type":"change","z":"7a8c632b.0c701c","name":"payload转字体","rules":[{"t":"move","p":"payload","pt":"msg","to":"colour","tot":"msg"}],"action":"","property":"","from":"","to":"","reg":false,"x":440,"y":120,"wires":[["b22f434.b055dc"]]},{"id":"b22f434.b055dc","type":"ui_button","z":"7a8c632b.0c701c","name":"","group":"bd84de44.ae507","order":0,"width":"0","height":"0","passthru":false,"label":"","color":"{{msg.colour}}","bgcolor":"#ff80ff","icon":"favorite","payload":"","payloadType":"str","topic":"","x":670,"y":120,"wires":[[]]},{"id":"bd84de44.ae507","type":"ui_group","z":"","name":"CONTROL","tab":"aacbcc03.d359b","disp":true,"width":"6","collapse":false},{"id":"aacbcc03.d359b","type":"ui_tab","z":"","name":"home","icon":"dashboard"}]
下拉菜单是输入框与按钮的结合体,常用于有指定选项的输入。例如,您住在哪个省?这个答案就可以用下拉菜单,只不过选型可能会多一点。但是可以避免用户输入信息,也可以避免他犯错。接下来就使用下拉菜单来做一个简单的例子。
俗话说,“桃三杏四李五年,要吃苹果等八年,枣子当年能卖钱”。这句话里包含5个键值对,也就是关键字与数值的对应,及桃对应5年……枣对应1年,我们使用下拉菜单来完成这样的任务:选择某个水果,就输出对应的年份。
拖入一个dropdown节点,如图进行配置:
再拖入一个text节点。注意不是text input。我们以前一直使用debug节点来显示信息,现在想把信息显示在仪表板的页面上,就用这么个节点。然后进行如下修改:
连线并部署。
在仪表板的页面,就可以看到这样的页面。点击下拉菜单,就可以看到候选框。
代码如下:
[{"id":"4989de73.41634","type":"ui_dropdown","z":"7a8c632b.0c701c","name":"","label":"什么果树种下了?","place":"请选择水果","group":"bd84de44.ae507","order":0,"width":0,"height":0,"passthru":true,"options":[{"label":"桃","value":"3年","type":"str"},{"label":"杏","value":"4年","type":"str"},{"label":"李","value":"5年","type":"str"},{"label":"苹果","value":"8年","type":"str"},{"label":"枣子","value":"1年","type":"str"}],"payload":"","topic":"","x":330,"y":260,"wires":[["ba04c2f1.2e9c1"]]},{"id":"ba04c2f1.2e9c1","type":"ui_text","z":"7a8c632b.0c701c","group":"bd84de44.ae507","order":0,"width":0,"height":0,"name":"","label":"几年能结果子?","format":"{{msg.payload}}","layout":"row-spread","x":540,"y":260,"wires":[]},{"id":"bd84de44.ae507","type":"ui_group","z":"","name":"CONTROL","tab":"aacbcc03.d359b","disp":true,"width":"6","collapse":false},{"id":"aacbcc03.d359b","type":"ui_tab","z":"","name":"home","icon":"dashboard"}]