内容简介:大多数商业化软件产品一般会通过实现GUI(Graphical User Interface)或者TUI(Text-based User Interface/Textual User Interface/Terminal User Interface)来降低软件的使用难度。本文简要介绍一个TUI库:因为
大多数商业化软件产品一般会通过实现GUI(Graphical User Interface)或者TUI(Text-based User Interface/Textual User Interface/Terminal User Interface)来降低软件的使用难度。本文简要介绍一个TUI库: newt
。
newt
是由RedHat开发,主要用在RatHat的 Linux 发行版本(RHEL, Fedora和CentOS)的安装程序项目 Anaconda
中。 NEWT
的全称是: Not Erik’s Windowing Toolkit
,它基于 S-Lang
库实现。
因为 newt
是用于Linux发行版本的安装程序中,因而它的运行空间非常有限,需要保证程序尽可能小,所以在设计时选择使用 C语言 进行开发,也尽量不支持多余特性。于是在设计之初就不支持事件驱动。在支持事件驱动的窗口库中,有一个事件循环监听事件发生,根据发生的事件展示不同的窗口。在 newt
中,窗口创建和销毁基于栈模式,新创建的窗口位于之前的窗口之上,且只能展示最上层窗口,当最上层窗口销毁之后才能展示下边的窗口。这也就是说所有窗口都为模态窗口( model windows
)。这种特性限制了 newt
库本身只适合用于完成顺序的程序流程,一个典型的场景就是程序的安装向导。SHELL程序往往也是顺序的流程,从 SHELL 程序转换为 newt
窗口程序完全不涉及程序流程的改变,实现非常简单。
newt
中的主要结构为组件( components
), 它和其他窗口库中所称的 widget
为相同概念。 newt
组件有许多, 常用的包括:
- Button
- CompactButton
- EntryBox
- CheckBox
- RadioButton
- Textbox
- Scrollbars
- Listbox
具体可以参考这篇 tutorail文章 。这篇文章写于2003年, 当时 newt
版本为v0.31,略有些老,但内容上在现在的版本(v0.52)上依然适用。
Form
是一种特殊的组件,它将其他组件组合在一起。 newt
应用程序使用 form
做为与用户交互的媒介。 newt
应用程序与用户交互的的简要逻辑为: 当 newt
应用需要从用户获取输入时,它运行一个 form
。此时, newt
应用开始等待用户输入。用户输入信息到 form
中所包含的组件后,将控制权交还 newt
应用(如通过触发按钮等行为), newt
应用再从组件中获取到用户输入的内容继续执行。 form
可以包含任意组件,也包括其他 form
。 form
嵌套可以用于改变 TAB
键触发的移动顺序、控制窗口不同区域的背景色,滚动特定窗口区域等。
newt
代码中将所有的组件都用一种变量类型: newtComponent
来表示。我们可以给组件注册回调函数,回调函数何时被触发取决于组件类型。回调函数原型和注册函数原型如下:
typedef void (*newtCallback)(newtComponent, void *); void newtComponentAddCallback(newtComponent co, newtCallback f, void * data);
newt
所有组件都绘制在窗口( window
)上, newt
默认创建了一个特殊的背景窗口,叫做 root
窗口。我们能够在该窗口上放置组件,也可以使用API创建新的窗口。 newt
库的坐标系统原点坐于屏幕左上角,函数原型中, left
参数表示 x
坐标,从原点出发从左至右增大, top
参数表示 y
坐标,从上到下增大。 left
可以对应为列数, top
可以对应为行数,两个坐标都可以为负数,表示从反方向开始计算。如:
newtDrawRootText(10, -5, "Hello NEWT");
表示在距屏幕左上角的第10列,倒数第5行的位置输出字符串 Hello NEWT
。
下边我们以一个实例来说明 newt
的使用。
#include <stdio.h> #include <stdlib.h> #include <ctype.h> #include <newt.h> void show_message_window(void) { char message[] = "This is a pretty long message. It will be displayed " "in a newt textbox, and illustrates how to construct " "a textbox from arbitrary text which may not have " "very good line breaks.\n\n" "Notice how literal \\n characters are respected, and " "may be used to force line breaks and blank lines."; newtComponent text, button, form; newtCls(); text = newtTextboxReflowed(1, 1, message, 30, 5, 5, 0); button = newtButton(12, newtTextboxGetNumLines(text) + 2, "Exit"); newtCenteredWindow(37, newtTextboxGetNumLines(text) + 7, "MESSAGE"); form = newtForm(NULL, NULL, 0); newtFormAddComponents(form, text, button, NULL); newtRunForm(form); newtFormDestroy(form); newtPopWindow(); } void show_login_failed_window(void) { newtComponent label, button, form; newtCls(); newtCenteredWindow(60, 6, "NOTE"); label = newtLabel(10, 2, "Login failed!"); button = newtCompactButton(30, 4, "Exit"); form = newtForm(NULL, NULL, 0); newtFormAddComponents(form, label, button, NULL); newtRunForm(form); newtFormDestroy(form); newtPopWindow(); } int main(void) { int cols, rows; newtInit(); do { newtCls(); newtPushHelpLine(NULL); newtGetScreenSize(&cols, &rows); newtOpenWindow(1, 1, cols - 2, rows - 4, "LOGIN"); newtComponent form, btn_ok, btn_cancel, label_username, label_password, entry_username, entry_password, result; const char *username, *password; label_username = newtLabel(10, 3, "Username:"); entry_username = newtEntry(20, 3, "root", 20, &username, NEWT_FLAG_SCROLL); label_password = newtLabel(10, 5, "Password:"); entry_password = newtEntry(20, 5, "", 20, &password, NEWT_FLAG_PASSWORD | NEWT_FLAG_SCROLL); result = newtLabel(10, 10, ""); btn_ok = newtButton(10, 7, "OK"); btn_cancel = newtButton(20, 7, "Cancel"); form = newtForm(NULL, NULL, 0); newtFormAddComponents(form, label_username, entry_username, label_password, entry_password, btn_ok, btn_cancel, result, NULL); struct newtExitStruct exit_status; newtFormRun(form, &exit_status); if (exit_status.reason == NEWT_EXIT_COMPONENT) { if (exit_status.u.co == btn_ok) { if ((strcmp(username, "root") == 0) && (strcmp(password, "123456") == 0)) { show_message_window(); break; } else { show_login_failed_window(); } } else if (exit_status.u.co == btn_cancel) { break; } } newtRefresh(); newtFormDestroy(form); newtPopWindow(); newtPopHelpLine(); } while (1); newtFinished(); return 0; }
CentOS发行版默认已带有 newt
库,我们需要安装 newt-devel
库:
yum install newt-devel
编译该程序:
gcc -o newtdemo newtdemo.c -lnewt
执行 newtdemo
, 界面如下图:
在密码框中输入 123456
后,移动焦点选择 OK
按钮,回车后显示另一个窗口:
我们简单说明下源代码。 main
函数中首先调用了:
newtInit();
newtInit()
用于初始化 newt
库的内部数据结构,并将终端设置为 raw
模式。接着调用 newtCls()
来清空屏幕。 newt
窗口的最后一行用于显示帮助信息,如每个快捷键所对应的功能。代码中调用 newtPushHelpLine(NULL)
显示默认的帮助信息。接下来通过调用 newtOpenWindow()
创建了一个新窗口,后续的组件将绘制在该窗口中。接下来,在窗口上创建了 Username
和 Password
两个输入框用于接收用户输入,以及两个按钮。
从组件定义的代码行中可以看到所有的组件都是 newtComponent
类型:
newtComponent form, btn_ok, btn_cancel, label_username, label_password, entry_username, entry_password, result;
我们使用一个 Form
表单将这些组件组合起来:
newtFormAddComponents(form, label_username, entry_username, label_password, entry_password, btn_ok, btn_cancel, result, NULL);
当执行 newtFormRun()
时, newt
应用将等待用户操作。我们通过 exit_status
结构体接收用户的操作信息来判断哪个按钮被触发。如果用户触发了 Cancel
按钮则程序结束。如果触发的为 OK
按钮,则检测输入的密码是否为 123456
。如果输入错误,则显示一个错误信息窗口。密码验证通过则显示一个长消息 Textbox
窗口。
这里我们也可以通过注册回调函数给两个按钮组件,根据哪个回调函数被执行来判断哪个按钮被触发。
newt
整体使用非常简单,然而比较遗憾的是,它基本没有文档,最好的参考资料为官方源码中的几个示例:
- https://pagure.io/newt/blob/master/f/test.c
- https://pagure.io/newt/blob/master/f/testgrid.c
- https://pagure.io/newt/blob/master/f/textbox.c
官方 newt
库中还提供了 Python 封装库,名称为 snack
。具体用法和C库类似,官方也提供了两个示例:
除此之外, newt
库中还提供了一个 whiptail
的命令行程序,用在SHELL脚本中显示对话框。比如,我们在BASH中执行:
whiptail --yesno "Continue?" 10 50
这将显示如下窗口:
当选择 YES
按钮时,程序退出码为 0
,选择 NO
按钮,程序退出码为 1
。我们选择 YES
之后,查看退出码:
[root@centos2 newt]# echo $? 0
具体信息可以参考:
类似的TUI工具还有 CDK 和 dialog 。CDK是一个程序库,能够链接进我们自己开发的应用程序, dialog
为一个命令行程序,与 whiptail
类似,可以在SHELL脚本中实现TUI界面。它们都是基于 ncurses
开发的, ncurses
是目前最为广泛使用的TUI库。不过它的API过于底层,我们直接使用相对繁琐,对于简单的界面实现,还是推荐使用更为上层的封装库。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Python TUI库npyscreen简要介绍
- Python远程执行库Fabric简要介绍
- 干货:人脸识别的简要介绍(附实例、Python代码)
- ElementUI文档中忽略或简要介绍的内容补充
- 回顾 Erlang 简要
- Toast 源码简要分析
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
在线进制转换器
各进制数互转换器
RGB HSV 转换
RGB HSV 互转工具