Python Curses 编程实战:打造炫酷文本界面应用
本文是 Python 的 curses 模块编程教程。curses 库为文本终端提供屏幕绘制和键盘处理功能,能开发出基于文本的交互式应用。本教程将深入介绍 Python 的 curses 模块,从基础概念、初始化与结束应用,到窗口、面板、文字显示、属性颜色设置、用户输入处理等,结合丰富示例与清晰图表,助力读者掌握用 curses 打造功能丰富的文本模式程序的技能。同时通过一个文件管理器项目案例,展示 curses 在实际开发中的应用。
一、curses 是什么?
curses 库为基于文本的终端(如 VT100、Linux 控制台等)提供独立于终端的屏幕绘制和键盘处理功能。不同终端控制代码差异大,curses 库对程序员屏蔽了这些差异,提供了包含多个非重叠文本窗口显示的抽象。虽然图形显示广泛应用,但在小型或嵌入式 Unix(不运行 X server)以及一些操作系统安装程序、内核配置程序等场景,基于文本的终端应用仍有价值。curses 库最初为 BSD Unix 编写,后经 AT&T 的 Unix System V 版本增强,如今 BSD curses 被 ncurses 取代。Python 的 Windows 版不含 curses 模块,可用 UniCurses 替代。
二、Python 的 curses 模块
Python 的 curses 模块是对 curses 提供的 C 函数的简单包装。与 C 语言的 curses 编程相比,Python 接口将多个类似的 C 函数(如addstr()
、mvaddstr()
和mvwaddstr()
)合并为一个addstr()
方法,使用更简便。本教程是 curses 和 Python 编程的概述,如需完整 API 指南,可参考 ncurses 的 Python 库指南章节和 ncurses 的 C 手册页。
三、开始和结束 curses 应用程序
(一)初始化 curses
在使用 curses 前,需调用initscr()
函数进行初始化,它会确定终端类型、发送设置代码并创建内部数据结构。成功执行后返回代表整个屏幕的窗口对象stdscr
。例如:
| import curses |
| stdscr = curses.initscr() |
(二)配置终端设置
- 关闭按键自动上屏:调用
noecho()
函数,使程序读取按键但不在屏幕上显示,常用于需要精确控制输入显示的场景。
- 开启 cbreak 模式:使用
cbreak()
函数开启 “cbreak” 模式,让程序立即响应按键,无需等待回车键,提升交互实时性。
- 启用 keypad 模式:调用
stdscr.keypad(True)
,使 curses 能处理特殊按键(如光标键、导航键),返回特殊值(如curses.KEY_LEFT
) ,方便程序识别处理。
(三)结束 curses 应用程序
程序结束时,需还原终端设置,依次调用nocbreak()
、stdscr.keypad(False)
、echo()
和endwin()
函数 。例如:
| curses.nocbreak() |
| stdscr.keypad(False) |
| curses.echo() |
| curses.endwin() |
(四)使用 curses.wrapper ()
为避免程序异常退出导致终端混乱,可使用curses.wrapper()
函数。它接受一个可调用对象,自动完成初始化(包括颜色初始化) ,运行传入的可调用对象,并在结束时恢复终端初始状态,还能捕获异常防止终端异常。示例如下:
| from curses import wrapper |
| def main(stdscr): |
| |
| stdscr.clear() |
| |
| for i in range(0, 11): |
| v = i - 10 |
| stdscr.addstr(i, 0, '10 divided by {} is {}'.format(v, 10/v)) |
| stdscr.refresh() |
| stdscr.getkey() |
| wrapper(main) |
四、窗口和面板
(一)窗口操作
- 创建窗口:使用
newwin()
函数可创建新窗口,需指定窗口的高度、宽度、起始 y 坐标和起始 x 坐标。例如:
| begin_x = 20; begin_y = 7 |
| height = 5; width = 40 |
| win = curses.newwin(height, width, begin_y, begin_x) |
curses 的坐标系统以(y, x)
顺序传递,左上角为(0, 0)
,屏幕尺寸可通过curses.LINES
和curses.COLS
获取,有效坐标范围是(0, 0)
到(curses.LINES - 1, curses.COLS - 1)
。
- 更新屏幕:对窗口的操作(如显示、擦除文本)不会立即显示,需调用窗口对象的
refresh()
方法更新屏幕。这是因为 curses 最初针对低速终端设计,会累积屏幕修改以高效显示。实际编程中,在等待用户输入前调用refresh()
方法确保屏幕更新。
(二)面板操作
面板是特殊窗口,可大于实际显示屏幕,仅显示部分内容。创建面板用newpad()
函数 ,如:
| pad = curses.newpad(100, 100) |
| |
| for y in range(0, 99): |
| for x in range(0, 99): |
| pad.addch(y, x, ord('a')+(x*x + y*y)%26) |
| |
| pad.refresh(0, 0, 5, 5, 20, 75) |
refresh()
方法的参数分别为面板显示区域的左上角坐标、屏幕填充区域的左上角坐标和右下角坐标。面板与普通窗口类似,支持相同方法 。多个窗口和面板更新时,可先调用各窗口的noutrefresh()
方法更新底层数据结构,再调用doupdate()
方法更新屏幕,减少闪烁。
五、显示文字
Python 的 curses 模块中,stdscr
和其他窗口对象的addstr()
方法用于显示文字,有多种参数形式:
参数形式 |
描述 |
示例 |
str 或 ch |
在当前位置显示字符串str 或字符ch |
stdscr.addstr("Hello") |
str 或 ch, attr |
在当前位置使用attr 属性显示字符串str 或字符ch |
stdscr.addstr("World", curses.A_BOLD) |
y, x, str 或 ch |
移动到窗口内的(y, x) 位置,并显示str 或ch |
stdscr.addstr(1, 2, "!") |
y, x, str 或 ch, attr |
移至窗口内的(y, x) 位置,并使用attr 属性显示str 或ch |
stdscr.addstr(3, 4, "End", curses.A_UNDERLINE) |
addch()
方法用于显示单个字符,可接受长度为 1 的字符串、字节串或整数 。对于特殊扩展字符,可用大于 255 的常量(如ACS_PLMINUS
、ACS_ULCORNER
)或 Unicode 字符表示。窗口会记住光标位置,可使用move(y, x)
方法移动光标,也可调用curs_set(False)
或leaveok(True)
隐藏光标。
六、属性和颜色
(一)属性设置
curses 通过属性值控制文本显示样式,属性值是整数,不同二进制位代表不同属性 。常见有效属性如下:
属性 |
描述 |
示例 |
A_BLINK |
闪烁文本 |
stdscr.addstr("Blinking text", curses.A_BLINK) |
A_BOLD |
超亮或粗体文本 |
stdscr.addstr("Bold text", curses.A_BOLD) |
A_DIM |
半明亮文本 |
stdscr.addstr("Dim text", curses.A_DIM) |
A_REVERSE |
反相显示文本 |
stdscr.addstr("Reverse text", curses.A_REVERSE) |
A_STANDOUT |
可用的最佳突出显示模式 |
stdscr.addstr("Standout text", curses.A_STANDOUT) |
A_UNDERLINE |
带下划线的文本 |
stdscr.addstr("Underline text", curses.A_UNDERLINE) |
(二)颜色设置
- 初始化颜色:使用颜色前,调用
start_color()
函数初始化默认颜色集(curses.wrapper()
会自动完成) ,之后可用has_colors()
函数检查终端是否支持颜色显示。
| if curses.has_colors(): |
| |
| pass |
- 颜色对使用:curses 库维护有限数量的颜色对(前景色和背景色组合) ,用
color_pair()
函数获取颜色对对应的属性值,可与其他属性按位或运算。如:
| stdscr.addstr("Colored text", curses.color_pair(1) | curses.A_BOLD) |
- 定义颜色对:使用
init_pair(n, f, b)
函数定义颜色对n
的前景色f
和背景色b
,颜色对 0 为黑底白字且不可改变。颜色编号从 0 开始,8 种基本颜色有对应常量(如curses.COLOR_BLACK
、curses.COLOR_RED
等) 。例如,将颜色对 1 设置为红色文本白色背景:
| curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) |
- 高级颜色功能:部分高端终端支持修改实际颜色定义为 RGB 值,可调用
can_change_color()
函数检查终端是否支持此功能,支持则可查阅系统帮助页面了解详情。
七、用户输入
(一)基本输入方法
getch()
方法:getch()
用于获取用户按键,会刷新屏幕并等待用户输入(若之前调用过echo()
,还会显示所按键) ,也可指定坐标让光标移动到该位置后等待输入。返回值为整数,0 - 255 代表 ASCII 码,大于 255 代表特殊键(如curses.KEY_PPAGE
、curses.KEY_HOME
) 。
| c = stdscr.getch() |
| if c == ord('p'): |
| |
| pass |
| elif c == curses.KEY_HOME: |
| |
| pass |
getkey()
方法:getkey()
功能与getch()
类似,但会将返回的整数转换为字符串,普通字符返回长度为 1 的字符串,特殊键返回包含键名的字符串(如KEY_UP
、^G
) 。
| key = stdscr.getkey() |
| if key == 'KEY_UP': |
| |
| pass |
(二)非阻塞输入
使用nodelay()
窗口方法可实现非阻塞输入。设置nodelay(True)
后,getch()
和getkey()
将变为非阻塞,输入未就绪时,getch()
返回curses.ERR
(值为 -1) ,getkey()
引发异常。halfdelay()
函数可设置getch()
的等待时间(以十分之一秒为单位) ,超时则引发异常。
| stdscr.nodelay(True) |
| c = stdscr.getch() |
| if c == curses.ERR: |
| |
| pass |
(三)获取字符串输入
getstr()
方法:getstr()
用于获取用户输入的字符串,功能有限,仅支持 Backspace 和 Enter 键进行编辑,也可限制输入字符数量。
| curses.echo() |
| s = stdscr.getstr(0, 0, 15) |
curses.textpad.Textbox
类:curses.textpad
模块的Textbox
类提供更丰富的文本输入功能,支持类似 Emacs 的键绑定集和输入验证。示例如下:
| import curses |
| from curses.textpad import Textbox, rectangle |
| |
| def main(stdscr): |
| stdscr.addstr(0, 0, "Enter IM message: (hit Ctrl-G to send)") |
| editwin = curses.newwin(5, 30, 2, 1) |
| rectangle(stdscr, 1, 0, 1 + 5 + 1, 1 + 30 + 1) |
| stdscr.refresh() |
| box = Textbox(editwin) |
| |
| box.edit() |
| |
| message = box.gather() |
八、项目实际应用案例:文本模式文件管理器
下面通过一个简单的文本模式文件管理器案例,展示 curses 模块在实际项目中的应用。这个文件管理器可以列出当前目录下的文件和文件夹,允许用户通过键盘选择文件或文件夹,并进行一些基本操作,如进入文件夹、返回上一级目录、查看文件内容等。
| import curses |
| import os |
| |
| |
| def file_manager(stdscr): |
| curses.curs_set(0) |
| current_path = os.getcwd() |
| while True: |
| stdscr.clear() |
| stdscr.addstr(0, 0, f"当前目录: {current_path}") |
| |
| files = os.listdir(current_path) |
| selected_index = 0 |
| |
| for i, file in enumerate(files): |
| if i == selected_index: |
| stdscr.addstr(i + 2, 0, file, curses.A_REVERSE) |
| else: |
| stdscr.addstr(i + 2, 0, file) |
| |
| stdscr.refresh() |
| key = stdscr.getch() |
| |
| if key == curses.KEY_UP and selected_index > 0: |
| selected_index -= 1 |
| elif key == curses.KEY_DOWN and selected_index < len(files) - 1: |
| selected_index += 1 |
| elif key == ord('\n'): |
| selected_file = os.path.join(current_path, files[selected_index]) |
| if os.path.isdir(selected_file): |
| current_path = selected_file |
| elif os.path.isfile(selected_file): |
| try: |
| with open(selected_file, 'r') as f: |
| content = f.read() |
| stdscr.clear() |
| stdscr.addstr(0, 0, f"文件内容: {selected_file}\n\n") |
| stdscr.addstr(content) |
| stdscr.refresh() |
| stdscr.getch() |
| except Exception as e: |
| stdscr.addstr(0, 0, f"无法打开文件: {e}") |
| stdscr.refresh() |
| stdscr.getch() |
| elif key == ord('q'): |
| break |
| elif key == curses.KEY_BACKSPACE or key == ord('..'): |
| if current_path != os.path.expanduser("~"): |
| current_path = os.path.dirname(current_path) |
| |
| |
| if __name__ == "__main__": |
| curses.wrapper(file_manager) |
在这个案例中:
- 首先使用
curses.wrapper()
函数来初始化和管理 curses 环境,确保程序异常退出时能正确恢复终端设置。
file_manager
函数中,通过os.listdir()
获取当前目录下的文件和文件夹列表,并在屏幕上显示。使用curses.A_REVERSE
属性突出显示当前选中的文件或文件夹。
- 利用
getch()
获取用户按键,根据不同的按键(如上下箭头键、回车键、q
键、退格键等)执行相应操作,如切换选中项、进入文件夹、查看文件内容、退出程序、返回上一级目录等。
- 对于文件查看功能,尝试打开文件并读取内容显示在屏幕上,若出现错误则显示错误信息。
通过这个案例,可以看到 curses 模块如何实现一个简单但功能实用的文本模式应用程序,展示了其在构建基于终端的交互应用方面的能力。
总结
本文全面介绍了 Python 的 curses 模块编程,涵盖 curses 库基础概念、Python 的 curses 模块使用、应用程序的初始化与结束、窗口和面板操作、文字显示、属性和颜色设置、用户输入处理以及一个文件管理器的实际项目案例。通过学习这些知识,读者能够利用 curses 模块开发出功能丰富、交互性强的文本模式应用程序。在实际应用中,可根据具体需求灵活运用这些技能,进一步探索 curses 模块的更多高级功能。
TAG: Python;curses 模块;文本模式编程;终端应用开发;窗口操作;用户输入处理;文件管理器
相关学习资源
- Python 官方文档:curses 模块文档,提供了 curses 模块的详细 API 说明。
- ncurses 官方文档:可查阅 ncurses 的相关手册页和指南,深入了解 curses 库的底层实现和更多功能细节。
- UniCurses 官方文档:如果在 Windows 系统上使用 UniCurses 替代 curses 模块,可参考其官方文档了解使用方法。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· 写一个简单的SQL生成工具
· AI 智能体引爆开源社区「GitHub 热点速览」
· C#/.NET/.NET Core技术前沿周刊 | 第 29 期(2025年3.1-3.9)