Rust终端UI开发库ncurses的使用,ncurses提供跨平台终端界面控制和文本模式图形化编程

Rust终端UI开发库ncurses的使用,ncurses提供跨平台终端界面控制和文本模式图形化编程

ncurses-rs 是一个非常轻量级的 ncurses TUI 库的 Rust 封装。

注意: ncurses 库本身非常不安全,而 ncurses-rs 只是最轻量的封装。如果你想要一个安全且符合 Rust 惯用法的 TUI 库,请考虑其他选择。如果你想要一个 C 到 Rust 的 1:1 移植,或者想用 C 风格的方式在 Rust 中快速开发 TUI,那么这个库会很适合。

构建

编译后的库会放在 target 目录下。

cargo build

注意你需要安装 ncurses 库并确保它可以被链接才能使用 ncurses-rs。在 Linux 上这很简单。在 macOS 上,可以考虑使用 Homebrew 安装 ncurses。(注意你需要强制 Homebrew 将库链接到 /usr/local/libbrew link --force ncurses 并将该路径设置为环境变量 LIBRARY_PATH。)

示例

示例通过 cargo build 构建。要运行它们,使用 cargo run --example ex_<NUMBER>。示例编号越大,示例越复杂。

当前示例:

  1. Hello World
  2. Basic Input & Attributes
  3. Simple Pager
  4. Window Movement
  5. Menu Library (需要 Rust nightly)
  6. Pager & Syntax Highlighting
  7. Basic Input & Attributes (Unicode)
  8. Special ACS Characters

环境变量

build.rs 会读取一些环境变量:

如果设置了 NCURSES_RS_RUSTC_LINK_LIB,它将用于 NCURSES_RS_RUSTC_LINK_LIB

如果设置了 NCURSES_RS_RUSTC_FLAGS,它将用于 cargo:rustc-flags

如果设置了 NCURSES_RS_CFLAGS,它将用于编译测试程序 chtype_size.c

完整示例代码

下面是一个使用 ncurses-rs 的完整 “Hello World” 示例:

extern crate ncurses;

use ncurses::*;

fn main() {
    // 初始化 ncurses
    initscr();
    
    // 打印字符串到标准屏幕
    printw("Hello, world!");
    
    // 刷新屏幕
    refresh();
    
    // 等待用户输入
    getch();
    
    // 结束 ncurses
    endwin();
}

另一个更复杂的示例,展示基本输入和属性:

extern crate ncurses;

use ncurses::*;

fn main() {
    // 初始化 ncurses
    initscr();
    raw();
    keypad(stdscr(), true);
    noecho();
    
    // 打印欢迎信息
    printw("Type any character to see it in bold\n");
    
    // 获取字符并显示
    let ch = getch();
    
    // 如果不是 F1 键
    if ch == KEY_F(1) {
        printw("F1 Key pressed");
    } else {
        // 开启属性
        attron(A_BOLD);
        printw(&format!("Key pressed is: {}\n", ch as u8 as char));
        // 关闭属性
        attroff(A_BOLD);
    }
    
    printw("Press any key to exit");
    refresh();
    getch();
    endwin();
}

要运行这些示例,你需要先在你的 Cargo.toml 中添加 ncurses 依赖:

[dependencies]
ncurses = "6.0.1"

然后使用 cargo run 来运行你的程序。

更完整的示例:创建颜色窗口

下面是一个完整的示例,展示如何创建带颜色的窗口:

extern crate ncurses;

use ncurses::*;

fn main() {
    // 初始化ncurses
    initscr();
    start_color();  // 启用颜色功能
    cbreak();       // 行缓冲禁用,直接传递所有输入
    noecho();       // 关闭输入回显
    keypad(stdscr(), true); // 启用功能键
    
    // 初始化颜色对
    init_pair(1, COLOR_RED, COLOR_BLACK);
    init_pair(2, COLOR_GREEN, COLOR_BLACK);
    
    // 创建新窗口
    let height = 10;
    let width = 30;
    let y = 5;
    let x = 10;
    let win = newwin(height, width, y, x);
    
    // 在窗口中打印带颜色的文本
    box_(win, 0, 0); // 绘制边框
    wattr_on(win, COLOR_PAIR(1)); // 启用颜色对1
    mvwprintw(win, 1, 1, "This is a red text");
    wattr_off(win, COLOR_PAIR(1)); // 关闭颜色对1
    
    wattr_on(win, COLOR_PAIR(2)); // 启用颜色对2
    mvwprintw(win, 2, 1, "This is a green text");
    wattr_off(win, COLOR_PAIR(2));
    
    // 刷新窗口
    wrefresh(win);
    
    // 在主窗口打印信息
    mvprintw(0, 0, "Press any key to exit");
    refresh();
    
    // 等待用户输入
    getch();
    
    // 删除窗口
    delwin(win);
    
    // 结束ncurses
    endwin();
}

这个示例展示了:

  1. 如何初始化ncurses并启用颜色功能
  2. 如何创建新的窗口
  3. 如何使用颜色对来显示不同颜色的文本
  4. 如何在特定位置打印文本
  5. 如何绘制窗口边框
  6. 如何清理资源

要运行这个示例,同样需要先在Cargo.toml中添加ncurses依赖。


1 回复

Rust终端UI开发库ncurses的使用指南

ncurses简介

ncurses是一个用于终端UI开发的C语言库,提供了跨平台的终端界面控制和文本模式图形化编程功能。在Rust中,我们可以通过ncurses crate来使用这个强大的库。

ncurses主要功能包括:

  • 创建基于文本的用户界面
  • 控制终端光标位置
  • 处理键盘和鼠标输入
  • 创建窗口和面板
  • 支持颜色和属性(如粗体、下划线等)

使用方法

1. 添加依赖

首先在Cargo.toml中添加依赖:

[dependencies]
ncurses = "5.101.0"

2. 基本使用示例

use ncurses::*;

fn main() {
    // 初始化ncurses
    initscr();
    
    // 启用颜色支持
    start_color();
    
    // 打印欢迎信息
    printw("欢迎使用ncurses Rust示例!");
    
    // 刷新屏幕显示
    refresh();
    
    // 等待用户输入
    getch();
    
    // 清理ncurses
    endwin();
}

3. 窗口管理

use ncurses::*;

fn main() {
    initscr();
    cbreak(); // 禁用行缓冲
    noecho(); // 不显示输入字符
    keypad(stdscr(), true); // 启用功能键
    
    // 创建一个新窗口
    let win = newwin(10, 30, 5, 5);
    box_(win, 0, 0); // 给窗口添加边框
    
    // 在窗口中打印文本
    mvwprintw(win, 1, 1, "这是一个子窗口");
    
    // 刷新窗口
    wrefresh(win);
    
    // 主窗口显示信息
    mvprintw(0, 极狐,0, "按任意键退出...");
    refresh();
    
    getch();
    endwin();
}

4. 颜色使用

use ncurses::*;

fn main() {
    initscr();
    start_color();
    
    // 初始化颜色对
    init_pair(1, COLOR_RED, COLOR_BLACK);
    init_pair(2, COLOR_GREEN, COLOR_BLACK);
    
    // 使用颜色
    attron(COLOR_PAIR(1));
    printw("红色文本");
    attroff(COLOR_PAIR(1));
    
    attron(COLOR_PAIR(2) | A_BOLD);
    printw(" 粗体绿色文本");
    attroff(COLOR_PAIR极狐(2) | A_BOLD);
    
    refresh();
    getch();
    endwin();
}

5. 键盘输入处理

use ncurses::*;

fn main() {
    initscr();
    cbreak();
    noecho();
    keypad(stdscr(), true);
    
    printw("按方向键测试 (按q退出)...");
    
    loop {
        let ch = getch();
        match ch {
            KEY_UP => mvprintw(1, 0, "上方向键      "),
            KEY_DOWN => mvprintw(1, 0, "下方向键      "),
            KEY_LEFT => mvprintw(1, 0, "左方向键      "),
            KEY_RIGHT => mvprintw(1, 0, "右方向键      "),
            b'q' => break,
            _ => mvprintw(1, 0, "未知按键: {} ", ch),
        }
        refresh();
    }
    
    endwin();
}

高级功能

1. 创建多窗口应用

use ncurses::*;

fn main() {
    initscr();
    cbreak();
    noecho();
    curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE); // 隐藏光标
    
    // 创建两个窗口
    let win1 = newwin(10, 30, 1, 1);
    let win2 = newwin(10, 30, 1, 35);
    
    // 填充窗口内容
    box_(win1, 0, 0);
    mvwprintw(win1, 1, 1, "窗口1");
    
    box_(win2, 0, 0);
    mvwprintw(win2, 1, 1, "窗口2");
    
    // 刷新窗口
    wrefresh(win1);
    wrefresh(win2);
    
    // 主窗口信息
    mvprintw(0, 0, "多窗口示例 (按q退出)");
    refresh();
    
    // 事件循环
    loop {
        let ch = getch();
        if ch == b'q' {
            break;
        }
    }
    
    // 删除窗口
    delwin(win1);
    delwin(win2);
    endwin();
}

2. 使用鼠标事件

use ncurses::*;

fn main() {
    initscr();
    cbreak();
    noecho();
    keypad(stdscr(), true);
    
    // 启用鼠标支持
    mousemask(ALL_MOUSE_EVENTS, None);
    
    printw("点击鼠标测试 (按q退出)...");
    refresh();
    
    loop {
        let ch = getch();
        match ch {
            KEY_MOUSE => {
                // 处理鼠标事件
                let mut event: MEVENT = MEVENT {
                    id: 0, x: 0, y: 0, z: 0, bstate: 0
                };
                if getmouse(&mut event) == OK {
                    mvprintw(1, 0, &format!(
                        "鼠标点击: x={}, y={}, 按钮={:b}      ",
                        event.x, event.y, event.bstate
                    ));
                    refresh();
                }
            }
            b'q' => break,
            _ => (),
        }
    }
    
    endwin();
}

完整示例:带菜单的终端应用

use ncurses::*;

fn main() {
    // 初始化ncurses
    initscr();
    start_color();
    cbreak();
    noecho();
    keypad(stdscr(), true);
    curs_set(CURSOR_VISIBILITY::CURSOR_INVISIBLE);

    // 初始化颜色
    init_pair(1, COLOR_WHITE, COLOR_BLUE);  // 菜单选中项
    init_pair(2, COLOR_BLACK, COLOR_WHITE); // 菜单未选中项

    // 创建菜单窗口
    let menu_win = newwin(10, 30, 5, 5);
    box_(menu_win, 0, 0);
    let title = "Rust ncurses菜单";
    mvwprintw(menu_win, 0, (30 - title.len() as i32) / 2, title);

    // 菜单选项
    let menu_items = ["选项1", "选项2", "选项3", "退出"];
    let mut selected = 0;

    // 主循环
    loop {
        // 绘制菜单
        for (i, item) in menu_items.iter().enumerate() {
            if i == selected {
                wattron(menu_win, COLOR_PAIR(1));
                mvwprintw(menu_win, i as i32 + 2, 2, &format!("> {}", item));
                wattroff(menu_win, COLOR_PAIR(1));
            } else {
                wattron(menu_win, COLOR_PAIR(2));
                mvwprintw(menu_win, i as i32 + 2, 2, &format!("  {}", item));
                wattroff(menu_win, COLOR_PAIR(2));
            }
        }

        wrefresh(menu_win);

        // 处理输入
        match getch() {
            KEY_UP => {
                if selected > 0 {
                    selected -= 1;
                }
            }
            KEY_DOWN => {
                if selected < menu_items.len() - 1 {
                    selected += 1;
                }
            }
            10 => { // 回车键
                if selected == menu_items.len() - 1 {
                    break;
                }
                mvprintw(15, 5, &format!("你选择了: {}", menu_items[selected]));
                refresh();
                getch();
            }
            _ => {}
        }
    }

    // 清理
    delwin(menu_win);
    endwin();
}

注意事项

  1. 总是调用endwin()来正确清理终端状态
  2. 在修改窗口内容后记得调用refresh()wrefresh()
  3. 考虑使用panel crate来管理重叠窗口
  4. 对于更现代的替代方案,可以考虑tui-rscursive等库

总结

ncurses为Rust提供了强大的终端UI开发能力,虽然API是C风格的,但通过Rust的包装,我们可以安全地使用它来创建丰富的终端应用程序。从简单的文本显示到复杂的多窗口界面,ncurses都能胜任。

回到顶部