• 终端兼容性:支持跨平台,在多种 Unix/Linux 终端和 Windows 命令提示符下运行。
  • 组件丰富:提供表格、表单、列表等多种交互界面组件。
  • 事件处理:简化了 UI 元素的布局和事件处理(如键盘和鼠标事件)。
  • 样式自定义:支持颜色和样式自定义,便于创建个性化界面。
  • 开发便利性:API 设计简洁,易于上手,降低终端 UI 开发复杂度。
  • 为什么使用 tview

    在工作中,我需要使用 tview 编写一个系统控制台程序,实现 ssh 登录成功后显示一个控制台页面,屏蔽所有按键并提供给用户一些特定的功能入口,目的是限制用户使用系统,从而保护系统及相关服务。

    Hello tview

    创建 go 项目并安装 tview。

    1
    go get github.com/rivo/tview@master

    编写一个标题为“Hello, World!”的程序并显示在终端上面。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    package main

    import (
    "github.com/rivo/tview"
    )

    func main() {
    box := tview.NewBox().SetBorder(true).SetTitle("Hello, world!")
    if err := tview.NewApplication().SetRoot(box, true).Run(); err != nil {
    panic(err)
    }
    }

    在上面的代码中,使用 tview.NewApplication() 创建一个 tview 应用,并使用 SetRoot() 函数设置 box 为根元素并启动程序, SetRoot() 函数的第二个参数用于指定是否全屏显示。

    组件

    List 列表

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package main

    import (
    "github.com/rivo/tview"
    )

    func main() {
    app := tview.NewApplication()
    list := tview.NewList().
    AddItem("List item 1", "Some explanatory text", 'a', nil).
    AddItem("List item 2", "Some explanatory text", 'b', nil).
    AddItem("List item 3", "Some explanatory text", 'c', nil).
    AddItem("List item 4", "Some explanatory text", 'd', nil).
    AddItem("Quit", "Press to exit", 'q', func() {
    app.Stop()
    })
    if err := app.SetRoot(list, true).SetFocus(list).Run(); err != nil {
    panic(err)
    }
    }

    使用 tview.NewList() 创建一个 list 组件,并使用 AddItem() 函数添加一些元素,然后使用 app SetRoot() 函数将 list 设置为根元素,然后使用 SetFocus() 函数设置为焦点并启动程序。

    先来看看 AddItem() 函数有哪些参数:

    1
    func (l *List) AddItem(mainText, secondaryText string, shortcut rune, selected func()) *List {}
  • mainText 用于显示一个列表项的主文本
  • secondaryText 用于显示一个列表项的次文本,可以添加一些长的描述文本在这里,用于描述这个列表项的作用
  • shortcut 用于绑定快捷键,它是一个 rune 类型,可以绑定数字类型和字符类型的快捷键
  • selected 参数是一个函数,表示该列表项被选中时的触发操作,为 nil 时表示什么都不做。
  • 程序启动后,可使用设置的快捷键和上下方向键或 Tab 键选择列表项,并按下回车键触发动作。

    值得注意的是,当使用快捷键选择了某个列表项后,无需再次按下回车键就能触发动作。

    使用上下键或 Tab 键选择列表项后则需要按下回车键触发动作。

    如果不想显示每个列表项的次要文本,需要调用 ShowSecondaryText(false) 函数来隐藏次要文本,这样就仅显示列表项的主文本了,不过此时 secondaryText 不能省略,需要传递空字符串:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    func main() {
    app := tview.NewApplication()
    list := tview.NewList().
    AddItem("List item 1", "", 'a', nil).
    AddItem("List item 2", "", 'b', nil).
    AddItem("List item 3", "", 'c', nil).
    AddItem("List item 4", "", 'd', nil).
    AddItem("Quit", "", 'q', func() {
    app.Stop()
    })
    list.ShowSecondaryText(false)

    if err := app.SetRoot(list, true).SetFocus(list).Run(); err != nil {
    panic(err)
    }
    }

    在 UI 设计中,模态窗口属于对话框的一种,模态窗口(Modal Dialog)是指中断用户操作,用户必须完成对话框内任务(或主动关闭对话框后)才能够继续主窗口操作的弹框。

    tview 中针对模态窗口做了单独的封装,使用也很简单。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    package main

    import (
    "github.com/rivo/tview"
    )

    func main() {
    app := tview.NewApplication()
    modal := tview.NewModal().
    SetText("Do you want to quit the application?").
    AddButtons([]string{"Quit", "Cancel"}).
    SetDoneFunc(func(buttonIndex int, buttonLabel string) {
    if buttonLabel == "Quit" {
    app.Stop()
    }
    })
    if err := app.SetRoot(modal, false).SetFocus(modal).Run(); err != nil {
    panic(err)
    }
    }

    tivew 中的模态窗口由提示文本、button 和 button 触发的函数组成, AddButtons() 函数传递一个字符串切片,用于添加 button, SetDoneFunc 函数传递一个 func(buttonIndex int, buttonLabel string) ,我们可以根据 buttonIndex buttonLabel 做一些逻辑处理,其中 buttonIndex 是 Button 在字符串切片中的索引, buttonLabel 是 Button 在字符串切片中的文本。

    Form 表单

    tview 对表单也做了封装,可以很方便的创建一个表单。

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    package main

    import (
    "github.com/rivo/tview"
    )

    func main() {
    app := tview.NewApplication()
    form := tview.NewForm().
    AddDropDown("Title", []string{"Mr.", "Ms.", "Mrs.", "Dr.", "Prof."}, 0, nil).
    AddInputField("First name", "", 20, nil, nil).
    AddInputField("Last name", "", 20, nil, nil).
    AddTextArea("Address", "", 40, 0, 0, nil).
    AddTextView("Notes", "This is just a demo.\nYou can enter whatever you wish.", 40, 2, true, false).
    AddCheckbox("Age 18+", false, nil).
    AddPasswordField("Password", "", 10, '*', nil).
    AddButton("Save", nil).
    AddButton("Quit", func() {
    app.Stop()
    })
    form.SetBorder(true).SetTitle(" Enter some data ").SetTitleAlign(tview.AlignLeft)
    if err := app.SetRoot(form, true).EnableMouse(true).Run(); err != nil {
    panic(err)
    }
    }

    这段代码创建一个简单的 TUI 表单应用程序。下面是代码的主要功能和组件解释:

  • 初始化应用 :通过 tview.NewApplication() 创建一个新的应用实例。
  • 创建表单 :使用 tview.NewForm() 构造一个表单,表单中包括了多种输入组件:
  •