命令行界面库< titlesplit >命令行工具是程序员的秘密武器。它们易于安装、启动速度快且界面简单。一个命令或快捷键即可完成操作,用完走开隐藏。而最方便的就是自己搭建!本期《讲解开源项目》介绍一个跨平台的库,让你快速拥有一个完美的命令行界面——tui.rs。项目地址:https://github.com/fdehau/tui-rs官方文档:https://docs.rs/tui/latest/tui/index.html你一定有过这样的纠结:我的程序需要接口,但是使用Qt等框架比较麻烦。现在tui.rs来了。它是Rust下的命令行UI库。内置多种组件,不仅易于上手,而且效果炫酷,支持跨平台使用。很容易体会到一段代码可以在Linux/Windows/Mac上无缝运行!接下来,你不仅可以快速上手tui.rs,还可以基于它收获各种法宝!1、使用Rust语言编写安装tui.rs,其他所有Rust依赖的安装方式,直接在cargo.toml中添加依赖即可:[dependencies]tui="0.17"crossterm="0.22"如果需要官方示例,直接clone官方仓库:$gitclonehttp://github.com/fdehau/tui-rs.git$cdtui-rs$cargorun--exampledemo2.快速入门2.1概述我们主要使用tui.rs提供的以下模块用于UI编写(AllUIelementsimplementWidgetorStatefuWidgetTrait):baked用于生成后台管理命令行。layout用于管理UI组件的布局。style用于向UI添加样式。要描述带样式的文本小部件,包括预定义的UI组件,可以使用以下代码实现一个非常简单的tui界面:使用crossterm::{event::{self,DisableMouseCapture,EnableMouseCapture,Event,KeyCode},execute,terminal::{disable_raw_mode,enable_raw_mode,EnterAlternateScreen,LeaveAlternateScreen},};usestd::{io,time::Duration};usetui::{backend::{Backend,CrosstermBackend},layout::{对齐方式,约束,方向,布局},style::{Color,Modifier,Style},text::{Span,Spans,Text},widgets::{Block,Borders,Paragraph,Widget},Frame,Terminal,};structApp{url:String,//存储一些数据或UI状态}fnmain()->Result<(),io::Error>{//初始化终端enable_raw_mode()?;让mutstdout=io::stdout();执行!(标准输出,EnterAlternateScreen,EnableMouseCapture)?让后端=CrosstermBackend::new(stdout);letmutterminal=Terminal::new(backend)?;letmutapp=App{url:String::from(r"https://helloithub.com/"),};//渲染界面run_app(&mutterminal,app)?;//恢复终端disable_raw_mode()?;执行!(terminal.backend_mut(),LeaveAlternateScreen,DisableMouseCapture)?;terminal.show_cursor()?;Ok(())}fnrun_app(terminal:&mutTerminal,mutapp:App)->io::Result<()>{loop{terminal.draw(|f|ui(f,&mut应用程序))?;//处理按键事件ifcrossterm::event::poll(Duration::from_secs(1))?{如果让Event::Key(key)=event::read()?{matchkey.code{KeyCode::Char(ch)=>{如果'q'==ch{break;}_=>{}}}}//处理其他逻辑}Ok(())}fnui(f:&mutFrame,app:&mutApp){//让chunks=Layout::default()//首先获取默认结构。constraints([Constraint::Length(3),Constraint::Min(3)].as_ref())//按照3行最小3行的规则分割区域。direction(Direction::Vertical)//垂直分割。分割(f.size());//拆分整个Block终端区域letparagraph=Paragraph::new(Span::styled(app.url.as_str(),Style::default().add_modifier(Modifier::BOLD),)).block(Block::default().borders(Borders::ALL).title("HelloGitHub")).alignment(tui::layout::Alignment::Left);f.render_widget(段落,块[0]);letparagraph=Paragraph::new("在GitHub上分享有趣的入门级开源项目item").style(Style::default().bg(Color::White).fg(Color::Black)).block(Block::default().borders(Borders::ALL).title("purpose")).alignment(Alignment::Center);f.render_widget(paragraph,chunks[1]);}这些代码看起来可能很多,但是大部分都是固定模板,我们不需要重新来一个详见下文2.2创建模板官方示例提供了一个使用tui.rs进行设计的模板,希望读者在使用时能够遵循这个模板,保证程序的可读性使用tui.rs的程序生命周期大致是这样的:它的模块大致可以分为:app.rs实现了App结构体,用于处理UI逻辑,保存UI状态ui.rs实现了UI渲染功能但是对于小程序来说,也可以写在main.rs中,首先我们看一下Terminal在开始和结束时的运行情况,每次运行都会保存原来的Terminal界面内容,在新的窗口运行,结束后会重新存储到原来的Terminal窗体,有效防止弄乱原来的窗口内容。这部分代码模板官方已经给出,基本不需要修改:fnmain()->Result<(),io::Error>{//ConfigureTerminalenable_raw_mode()?;//启动命令行的原始模式letmutstdout=io::stdout();执行!(标准输出,EnterAlternateScreen,EnableMouseCapture)?//在新界面上运行UI,保存原来的终端内容,并启用鼠标捕获letbackend=CrossBackend::new(stdout);letmutterminal=Terminal::new(backend)?;//初始化应用资源letmutapp=App{url:String::from(r"https://hellogithub.com/"),};//主程序逻辑循环...//run_app(&mutterminal,app)?;//恢复终端disable_raw_mode()?;//禁用原始模式执行!(terminal.backend_mut(),LeaveAlternateScreen,//恢复到原始命令行窗口DisableMouseCapture//禁用鼠标捕获)?;terminal.show_cursor()?;//ShowcursorOk(())}接下来是处理UI逻辑的run_app函数,在这里我们处理用户按键、UI状态变化等逻辑:fnrun_app(terminal:&mutTerminal,mutapp:App)->io::Result<()>{loop{//渲染UIterminal.draw(|f|ui(f,&mut应用程序))?;//处理关键事件ifcrossterm::event::poll(Duration::from_secs(1))?{//poll方法非阻塞轮询ifletEvent::Key(key)=event::read()?{//直接读取如果没有事件到达,会阻塞等待匹配key.code{//判断用户击键KeyCode::Char(ch)=>{if'q'==ch{break;}_=>{}}}}//处理其他逻辑}Ok(())}对于一个简单的界面来说,这个函数用处不大,但是如果我们的程序需要更新一些组件状态(比如列表选中项、用户输入、外部数据交互等)应该在这里统一处理。之后,我们将使用terminal.draw()方法绘制界面,它接受一个闭包:fnui(f:&mutFrame,app:&mutApp){//获取分割窗口letchunks=Layout::default()//首先获取默认结构。constraints([Constraint::Length(3),Constraint::Min(3)].as_ref())//遵循3行和最少3行的规则splitarea.direction(Direction::Vertical)//垂直拆分.split(f.size());//拆分整个终端区域letparagraph=Paragraph::new(Span::styled(app.url.as_str(),Style::default().add_modifier(Modifier::BOLD),)).block(Block::default().borders(Borders::ALL).title("HelloGitHub")).alignment(tui::layout::Alignment::Left);f.render_widget(段落,块[0]);letparagraph=Paragraph::new("在GitHub上分享有趣的入门级开源项目").style(Style::default().bg(Color::White).fg(Color::Black)).block(Block::default().borders(Borders::ALL).title("Objective")).alignment(Alignment::Center);f.render_widget(paragraph,chunks[1]);}这里有如下过程:使用Layout给Constraint拆分表单并获得块。每个chunk还可以使用Layout继续拆分和实例化组件。每个组件都实现默认方法。使用时,我们应该先使用xxx::default()获取默认对象,然后用默认对象更新组件样式,如Block::default().borders(Borders::ALL),Style::default(.bg(Color::White)等。这也是官方推荐的。使用f.render_widget在表单上渲染组件。对于列表等状态存在的组件(比如当前选中的元素),使用f.render_stateful_widget来渲染。tui.rs中其他内置组件的使用,可以查看官方示例文件,写套路也是一样的,可以根据需要直接复制粘贴。需要注意的是,这里我们只关心UI组件的显示方式和内容。与程序逻辑相关的内容应该在run_app中处理,以免打乱程序结构或影响UI绘制效果(你不希望UI绘制到一半。因为一些IO操作卡死了吧?)这是tui.rs的介绍到此结束。其实用tui.rs写UI界面很简单。按照创建模板和官方示例一步步搭建即可。任何人都可以立即开始。3.更多实用工具下面将介绍几个基于tui.rs的流行开源项目,它们都是命令行工具中的“法宝”!3.1股票实时数据支持查看不同时间维度和交易量等数据,股票实时数据来自雅虎。地址:https://github.com/tarkah/tickrs3.2文件传输工具支持SCP/SFTP/FTP/S3,一款功能丰富的终端文件传输工具。地址:https://github.com/veeso/termscp3.3网络监控工具用于按进程、连接、远程IP、主机名显示当前网络利用率。地址:https://github.com/imsnif/bandwhich