LiTouch是一个基于pygame的图形化定制UI工具,通过集成ROS1来实现与C++程序的通信。同时,在Livz可视化工具中提供了一些接口便于和LiTouch交互。
pip intsll pygame
注意:python3.11与ROS环境存在一些兼容性问题,无法新建rosnode。因此,你的电脑上最好有3.10及以下的python版本。Livz的启动API将会自动寻找合适的版本。
cd Livz/example
catkin_make
source devel/setup.bash
roslaunch example run_demo.launch
这个演示程序提供了一些使用Livz可视化的示例,同时,使用LiTouch工具来提供交互操作。
这个演示程序的流程如下:
首先,在main.cpp中,调用API来启动LiTouch,并传递UI配置文件。
Livz::launchLiTouch( ui_config_path );
UI配置文件的地址为
example/src/example/config/touch_layout.json
ui_config_path使用了绝对路径,因此,如果LiTouch没有正确启动,请首先修改UI配置文件的路径。如果你使用VSCode,可以在资源管理器的文件上右键->复制路径。
然后,调用API绑定Action回调函数
Livz::setLitouchAction(litouchAction);
Action回调函数的类型必须为:
[](const int cmd_id, const std::vector<double> params)->void
由于LiTouch是 python实现的,而用户程序可能是C++,因此在Livz内部定义了一个接口用于将Litouch的组件交互信息传递到C++中。
实现组件的交互回调需要编写回调逻辑,LiTouch的回调逻辑统一编写在一个.py文件中,这个文件的路径由UI配置文件中"action_code"属性指定,使用相对于UI配置文件的相对目录。示例程序使用的回调逻辑文件目录为:
example/src/example/config/touch_action.py
在回调逻辑文件中,定义了每一个组件的动作响应。其中
pubLiTouchCmd(cmd_id, [params] )
是interface提供的API,它负责发送一个LiTouchCmd消息,可以附带任意长度的浮点数参数,这个消息会被Action回调函数所接收,从而实现python程序与C++的通信。
如示例程序所介绍,自定义一个LiTouch UI需要两个文件:UI配置文件和回调逻辑文件。其中UI配置文件的格式为 .json, 回调逻辑文件的格式是 .py
UI配置文件的构成如下(注意,json文件中不支持注释,实际使用时务必不要带“//”注释):
{
"UI": {
"action_code": "/path/to/your/action.py", //回调逻辑文件路径
"window": {
"title": "LiTouch", //窗口标题
"width": 800, //窗口尺寸
"height": 600,
"theme": 1, // 主题,取值1~7 : 秋,春,夏,冬,湛蓝之海, 猛男色,静谧深空
"components": [ //组件列表
{
"type": "label",
"cid": -1,
"label": "Demos",
"x": 2,
"y": 38,
"width": 120,
"height": 36,
"color_r": 255,
"color_g": 255,
"color_b": 120
},
......
]
}
}
}
而回调逻辑文件的构成如下:
import os
import sys
install_prefix = os.environ.get('LIVZ_TOUCH_LIB_VAR', './')
sys.path.append(install_prefix)
import interface
################################################################
################################################################
# 必须包含一个init函数,该函数在初始化时被调用一次,用于创建自定义内容
def init():
pass
# 必须包含一个tick函数,这个函数将在每一帧被调用,dt为距离上一次调用经过的时间(毫秒)
def tick(dt):
pass
################################################################
################################################################
# 在下面添加自定义回调函数
def button1_callback():
print("button1 click")
......
如果想要快速编写这两个文件,可以如下操作:首先使用不带参数的API启动LiTouch:
Livz::launchLiTouch();
这会打开默认配置,然后点击菜单栏上的 [Template] 按键,这将打开UI配置文件和回调逻辑文件的模板,可以按照模板内容修改。此外,你可以着重于修改UI,而无需关注回调逻辑文件,因为每次启动时,将自动补全缺少的回调函数,无需手动编写回调逻辑文件框架。
LiTouch上方的菜单栏不能修改。它包含6个功能键。注意:当窗口尺寸太小时,菜单按钮会变成缩写。
-- Layout 开启/关闭 布局模式,开启布局模式后可以对窗口中的组件进行移动和缩放。
-- Save 保存当前布局,这将覆盖UI配置文件的内容
-- Top 开启/关闭 窗口置顶
-- Template 打开配置文件的模板
-- Config code 打开 UI配置文件
-- Action code 打开回调逻辑文件
底部状态栏默认会记录最近一次操作,同时可以进行闪烁提示。LiTouch也提供API控制底部状态栏进行不同类型的闪烁。
目前,LiTouch包含的组件为
- 静态文字 "label"
- 按钮 "button"
- 开关 "switch"
- 按钮列表 "button_list"
- 滑块 "slider"
- 迷你终端 "mini_terminal"
静态文字组件用于在LiTouch中显示静态标题,没有交互行为。但是,你可以在回调逻辑中修改它的内容,将它作为状态监视器使用。静态文字组件的属性如下:
{
"type": "label",
"cid": -1, //标识符,配合getComponentByCid使用
"label": "static label", //标题内容
"x": 259, //x,y,width,height是位置和尺寸
"y": 20,
"width": 496,
"height": 47,
"color_r": 255, //颜色
"color_g": 120,
"color_b": 120
}
"type","cid","x","y","width","height"这些参数大部分组件都有,后面不再赘述。 静态文字的字体大小是根据尺寸和文字内容自动调节的。
按钮组件提供单击的交互行为。它的回调函数没有参数。按钮的属性如下:
{
"type": "button",
"cid": -1,
"label": "Button1", //标题内容
"x": 276,
"y": 358,
"width": 471,
"height": 47,
"action": "button1_callback" //回调函数名
}
开关组件提供布尔状态改变的交互行为。它的回调函数有一个参数,表示当前开关状态。开关的属性如下:
{
"type": "switch",
"cid": -1,
"label": "Switch1",
"x": 640,
"y": 441,
"width": 30,
"height": 30,
"action": "switch_callback"
}
开关在关闭状态下,左右两侧有橙色竖线,开启状态下是被橙色框包围。
需要大量按钮的场景,可以使用按钮列表组件。它包括若干个列表按钮,每一个列表按钮都需要定义一个没有参数的回调函数。按钮列表的属性如下:
{
"type": "button_list",
"cid": -1,
"x": 27,
"y": 79,
"width": 228,
"height": 419,
"button_height": 40, //列表中每个按钮的高度
"buttons": [ //每一个列表按钮只需要3个属性
{
"label": "run_demo1", //标题
"cid": -1, //标识符
"action": "demo1_callback" //回调函数
},
{
"label": "run_demo2",
"cid": -1,
"action": "demo2_callback"
},
......
]
}
滑动条组件提供连续浮点数变动的交互行为。它的回调函数有一个参数,表示当前滑块的值。滑动条的属性如下:
{
"type": "slider",
"cid": -1,
"label": "Slider",
"x": 260,
"y": 87,
"width": 496,
"height": 46,
"min": 0, // 滑块值的区间
"max": 100,
"action": "slider_callback"
}
迷你终端提供简单的终端模拟功能,它可以用作监视器接收日志输出,也可以实现简单的命令交互(尽管现在没有多少命令可以用)。你可以在LiTouch中放置多个迷你终端,然后用Livz提供的API向不同的终端发送不同的信息,这比所有信息都输出到系统终端更整洁。迷你终端的属性如下:
{
"type": "mini_terminal",
"cid": -1,
"label": "terminal1",
"x": 280,
"y": 83,
"width": 142,
"height": 158,
"buffer_rows": 200, //缓存区容量(回滚行数)
"font_size": 14, //字体大小
"text_r": 30, //字体颜色
"text_g": 255,
"text_b": 30,
"background_r": 0, //背景颜色
"background_g": 0,
"background_b": 0
}
迷你终端提供指令交互功能。单击一个迷你终端,出现黄色光标闪烁表示进入编辑状态,再次单击会退出编辑状态。因此,你可以单击多个迷你终端实现指令同步。
和其它终端类似,支持方向键唤出历史记录、Tab补全功能、Ctrl+C取消功能。
目前支持的指令如下:
-- clear 清除终端缓存区
-- ip 查看本机所有IPV4地址
-- exportToFile [file_name] 将终端缓存区内容导出到文件,文件放在回调逻辑函数同一级目录
-- setTextColor [r] [g] [b] 设置字体颜色
-- setBgColor [r] [g] [b] 设置背景颜色
在回调逻辑文件的模板中,引入interface来为自定义内容提供一些API。
LiTouch端的API包括:
-- pubLiTouchCmd(id, params = []) 发布LiTouchCmd消息,可以被Livz绑定的回调函数接收
-- flashInfo(info) 底部状态栏闪烁
-- flashWarning(info) 底部状态栏闪烁,背景为橙色(警告)
-- flashError(info) 底部状态栏闪烁,背景为红色(错误)
-- flashGood(info) 底部状态栏闪烁,背景为绿色(喜报)
-- getComponentbyName(name) 通过名称获取组件
-- getComponentvbyCid(cid) 通过cid获取组件,假设你想要让一个Label的内容经常变动,那么通过名称获取组件显然不是一个明智的方法。
-- printToTerminal(name, content) 向名字为name的迷你终端发送内容
-- launchLiTouch(ui_conf_path) 使用指定配置文件启动LiTouch,如果不传递参数,则使用默认配置。
除了LiTouch端提供的API,Livz也提供了一些API来负责C++部分与LiTouch的交互,这些API包括:
-- Livz::launchLiTouch(const string& UI_conf_path); 使用指定配置文件启动LiTouch,如果不传递参数,则使用默认配置。会自动寻找合适的python版本启动LiTouch
-- Livz::setLitouchAction( [](const int cmd_id, const std::vector params)->void f); 绑定LiTouchCmd回调函数。
-- Livz::LTout(const string& terminal_name) 创建一个向LiTouch中名称为terminal_name的迷你终端的“输出流”。使用方法与cout类似,但是刷新缓冲标志要使用LTEndl。