Rust终端UI插件库tui-term的使用,tui-term提供强大的终端界面构建与交互功能

Rust终端UI插件库tui-term的使用,tui-term提供强大的终端界面构建与交互功能

tui-term是ratatui库的伪终端组件。

Demo of tui-term

状态

注意:该项目目前处于积极开发阶段,应视为进行中的工作。tui-term的目标是为ratatui库的用户提供一个健壮且经过良好测试的伪终端组件。

安装

要使用tui-term,只需在Cargo.toml文件中添加依赖:

[dependencies]
tui-term = "0.2.0"

或使用cargo add:

cargo add tui-term

示例

查看示例目录获取更多信息,或运行示例:

cargo run --example simple_ls_rw

控制器

控制器是一个实验性功能,帮助管理在伪终端内生成的命令的生命周期。目前支持仅限于一次性命令。

要激活该功能:

cargo add tui-term -F unstable

聊天室

加入我们的Matrix聊天室,进行可能的同步交流。

架构

有关tui-term架构和设计原则的概述,请参阅架构文档。

贡献

我们欢迎社区的贡献!查看贡献指南了解如何开始。

发布说明

通过查看变更日志了解最新变化。

许可证

tui-term在MIT许可证下可用。查看许可证获取更多信息。

完整示例代码

use ratatui::{
    backend::CrosstermBackend,
    layout::{Constraint, Direction, Layout},
    style::{Color, Style},
    widgets::{Block, Borders, Paragraph},
    Terminal,
};
use std::io;
use tui_term::widget::PseudoTerminal;

fn main() -> Result<(), Box<dyn std::error::Error>> {
    // 初始化终端后端
    let stdout = io::stdout();
    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;

    // 创建伪终端实例
    let mut pseudo_term = PseudoTerminal::new();

    // 主应用循环
    loop {
        terminal.draw(|f| {
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .margin(1)
                .constraints([Constraint::Percentage(80), Constraint::Percentage(20)].as_ref())
                .split(f.size());

            // 渲染伪终端区域
            let term_block = Block::default()
                .title("Terminal")
                .borders(Borders::ALL)
                .style(Style::default().bg(Color::Black).fg(Color::White));
            
            f.render_widget(&pseudo_term, chunks[0]);
            f.render_widget(term_block, chunks[0]);

            // 渲染状态信息区域
            let status_block = Block::default()
                .title("Status")
                .borders(Borders::ALL)
                .style(Style::default().bg(Color::DarkGray).fg(Color::White));
            
            let status_text = Paragraph::new("Press 'q' to quit, 'c' to clear terminal")
                .block(status_block);
            
            f.render_widget(status_text, chunks[1]);
        })?;

        // 处理键盘输入
        if crossterm::event::poll(std::time::Duration::from_millis(100))? {
            if let crossterm::event::Event::Key(key) = crossterm::event::read()? {
                match key.code {
                    crossterm::event::KeyCode::Char('q') => break,
                    crossterm::event::KeyCode::Char('c') => {
                        // 清空终端内容
                        pseudo_term.clear();
                    }
                    _ => {
                        // 将按键输入发送到伪终端
                        if let Some(c) = key_to_char(key.code) {
                            pseudo_term.input(c);
                        }
                    }
                }
            }
        }
    }

    Ok(())
}

// 辅助函数:将KeyCode转换为字符
fn key_to_char(key_code: crossterm::event::KeyCode) -> Option<char> {
    match key_code {
        crossterm::event::KeyCode::Char(c) => Some(c),
        crossterm::event::KeyCode::Enter => Some('\n'),
        crossterm::event::KeyCode::Tab => Some('\t'),
        crossterm::event::KeyCode::Backspace => Some('\x08'),
        _ => None,
    }
}

这个示例展示了如何使用tui-term创建一个基本的终端界面应用,包含:

  • 伪终端显示区域
  • 状态信息区域
  • 键盘输入处理
  • 清空终端功能
  • 退出机制

要运行此示例,需要在Cargo.toml中添加相关依赖:

[dependencies]
ratatui = "0.23"
crossterm = "0.27"
tui-term = "0.2"

1 回复

tui-term:Rust终端UI插件库

介绍

tui-term是一个用于构建终端用户界面的Rust库,提供强大的终端界面构建与交互功能。它基于tui-rs生态系统,专门设计用于创建复杂的终端应用程序,支持多窗口、文本编辑、滚动视图等高级功能。

主要特性

  • 多窗口支持
  • 文本编辑和格式化
  • 键盘和鼠标交互
  • 可自定义的主题和样式
  • 响应式布局系统

安装方法

在Cargo.toml中添加依赖:

[dependencies]
tui-term = "0.5"

基本使用方法

1. 创建基本终端应用

use tui_term::widget::Terminal;
use tui_term::backend::CrosstermBackend;
use tui_term::Termion;
use std::io;

fn main() -> Result<(), io::Error> {
    let stdout = io::stdout();
    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;
    
    terminal.clear()?;
    // 在这里添加你的UI逻辑
    terminal.draw(|f| {
        // 绘制界面
    })?;
    
    Ok(())
}

2. 创建带有多窗口的界面

use tui_term::widgets::{Block, Borders, Window};
use tui_term::layout::{Layout, Constraint, Direction};
use tui_term::style::{Style, Color};

terminal.draw(|f| {
    let chunks = Layout::default()
        .direction(Direction::Vertical)
        .margin(1)
        .constraints(
            [
                Constraint::Percentage(30),
                Constraint::Percentage(70),
            ].as_ref()
        )
        .split(f.size());
    
    let left_window = Window::new("终端")
        .borders(Borders::ALL)
        .style(Style::default().fg(Color::White));
    
    let right_window = Window::new("输出")
        .borders(Borders::ALL)
        .style(Style::default().fg(Color::Yellow));
    
    f.render_widget(left_window, chunks[0]);
    f.render_widget(right_window, chunks[1]);
})?;

3. 处理用户输入

use tui_term::event::{Event, KeyCode};

loop {
    terminal.draw(|f| {
        // 绘制界面
    })?;
    
    if let Event::Key(key) = tui_term::event::read()? {
        match key.code {
            KeyCode::Char('q') => break,
            KeyCode::Up => {
                // 处理上箭头按键
            }
            KeyCode::Down => {
                // 处理下箭头按键
            }
            _ => {}
        }
    }
}

4. 文本输入示例

use tui_term::widgets::{Paragraph, Text};
use tui_term::style::{Style, Color};

let text = vec![
    Text::styled("欢迎使用 ", Style::default().fg(Color::Green)),
    Text::styled("tui-term", Style::default().fg(Color::Yellow)),
    Text::raw(" 终端UI库!")
];

let paragraph = Paragraph::new(text.iter())
    .block(Block::default().title("输入").borders(Borders::ALL))
    .wrap(true);

terminal.draw(|f| {
    f.render_widget(paragraph, f.size());
})?;

高级功能示例

创建可滚动的日志视图

use tui_term::widgets::{List, ListItem};
use tui_term::text::Spans;

let items: Vec<ListItem> = (0..100)
    .map(|i| ListItem::new(Spans::from(format!("日志条目 {}", i))))
    .collect();

let list = List::new(items)
    .block(Block::default().title("日志").borders(Borders::ALL))
    .scroll((scroll, 0));

完整示例demo

use tui_term::widget::Terminal;
use tui_term::backend::CrosstermBackend;
use tui_term::widgets::{Block, Borders, Window, Paragraph, Text, List, ListItem};
use tui_term::layout::{Layout, Constraint, Direction};
use tui_term::style::{Style, Color};
use tui_term::event::{Event, KeyCode};
use tui_term::text::Spans;
use std::io;

fn main() -> Result<(), io::Error> {
    // 初始化终端后端
    let stdout = io::stdout();
    let backend = CrosstermBackend::new(stdout);
    let mut terminal = Terminal::new(backend)?;
    
    // 清空终端
    terminal.clear()?;
    
    let mut scroll = 0;
    
    // 主事件循环
    loop {
        // 绘制界面
        terminal.draw(|f| {
            // 创建垂直布局分割
            let chunks = Layout::default()
                .direction(Direction::Vertical)
                .margin(1)
                .constraints(
                    [
                        Constraint::Percentage(30),
                        Constraint::Percentage(40),
                        Constraint::Percentage(30),
                    ].as_ref()
                )
                .split(f.size());
            
            // 创建标题窗口
            let title_window = Window::new("tui-term 示例应用")
                .borders(Borders::ALL)
                .style(Style::default().fg(Color::Cyan));
            
            // 创建文本内容
            let welcome_text = vec![
                Text::styled("欢迎使用 ", Style::default().fg(Color::Green)),
                Text::styled("tui-term", Style::default().fg(Color::Yellow)),
                Text::raw(" Rust终端UI库!")
            ];
            
            let paragraph = Paragraph::new(welcome_text.iter())
                .block(Block::default().borders(Borders::ALL))
                .wrap(true);
            
            // 创建日志列表
            let log_items: Vec<ListItem> = (0..50)
                .map(|i| ListItem::new(Spans::from(format!("日志条目 {} - 示例内容", i))))
                .collect();
            
            let log_list = List::new(log_items)
                .block(Block::default().title("系统日志").borders(Borders::ALL))
                .scroll((scroll, 0));
            
            // 渲染各个部件
            f.render_widget(title_window, chunks[0]);
            f.render_widget(paragraph, chunks[1]);
            f.render_widget(log_list, chunks[2]);
        })?;
        
        // 处理用户输入
        if let Event::Key(key) = tui_term::event::read()? {
            match key.code {
                KeyCode::Char('q') => break, // 按q退出
                KeyCode::Up => {
                    // 向上滚动日志
                    if scroll > 0 {
                        scroll -= 1;
                    }
                }
                KeyCode::Down => {
                    // 向下滚动日志
                    scroll += 1;
                }
                _ => {}
            }
        }
    }
    
    Ok(())
}

注意事项

  • 确保终端支持ANSI转义序列
  • 处理终端大小变化事件
  • 合理管理内存,特别是在处理大量文本时
  • 使用async/await处理异步事件时需要考虑线程安全

这个库特别适合构建命令行工具、终端监控应用、交互式配置工具等需要丰富用户界面的终端应用程序。

回到顶部