首页 > Python基础教程 >
-
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()函数,使程序读取按键但不在屏幕上显示,常用于需要精确控制输入显示的场景。
curses.noecho()
开启 cbreak 模式:使用cbreak()函数开启 “cbreak” 模式,让程序立即响应按键,无需等待回车键,提升交互实时性。
curses.cbreak()
启用 keypad 模式:调用stdscr.keypad(True),使 curses 能处理特殊按键(如光标键、导航键),返回特殊值(如curses.KEY_LEFT) ,方便程序识别处理。
stdscr.keypad(True)
(三)结束 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()
# 当 i == 10 时这将引发 ZeroDivisionError。
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:
# 处理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) # 在(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)
# 让用户编辑直到按下Ctrl-G
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'): # 按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 模块,可参考其官方文档了解使用方法。
来源:https://www.cnblogs.com/tekin/p/18726801