我写了一个python库:dumb_meun

我之前再我的程序里面 用了一个第三方库:simple_term_menu,提供菜单功能。但是当我再windows上尝试跑一下终于写好的程序时,竟然不能用。原因是它依赖于 termios 库, 不支持 win 。 因为我的功能需求比较简单,我决定自己下一个代替品,就是这个 dumb_meun 了。

主要要实现的功能:

  • 传入一个 list ,显示菜单, 返回选中的 index
    • 上下键选择
    • enter建确认
    • *支持快捷键

因为python没有提供类似于c语言中的getchar函数,再 linux 上用 termios 库可以实现,但是在windows上使用的是 msvcrt 库。所以实际上在 python 中实现起来不是很容易。

 1def get_key(): #get keypress using getch , msvcrt = windows or termios = linux
 2    try :
 3        import getch
 4        first_char = getch.getch()
 5        if first_char == '\x1b': #arrow keys
 6            a=getch.getch()
 7            b=getch.getch()
 8            return {'[A': 'up', '[B': 'down', '[C': 'right', '[D': 'left' }[a+b]
 9        if ord(first_char) == 10:
10            return  'enter'
11        if ord(first_char) == 32:
12            return  'space'
13        else:
14            return first_char #normal keys like abcd 1234
15    except :
16        pass
17    try:
18        import msvcrt
19        key = msvcrt.getch()  # get keypress
20        if key == b'\x1b':  # Esc key to exit
21            return 'esc'
22        elif key == b'\r':  # Enter key to select
23            return 'enter'
24        elif key == b'\x48':  # Up or Down arrow
25            return  'up'
26        elif key == b'\x50':  # Up or Down arrow
27            return 'down'
28        else:
29            return key.decode('utf-8')
30    except:
31        pass
32
33while True:
34    key = get_key()
35    print("You pressed: ", key)

这个函数可以获取键盘输入,返回值是一个字符串,比如up down / enter / a b c / 1 2 3,这样统一一下,可以兼容linux 和 win。

测试结果:

1Press a key to test out!
2You pressed:  down
3You pressed:  up
4You pressed:  a
5...

我拷问了chatgpt老半天,他才给我编好

后补充的流程图

  • 提取快捷键是用 re 库的正则表达式

  • 显示菜单通过 os.system("cls" if os.name == "nt" else "clear") 清屏,再打印出来,每次给菜单传入 list 和选中的 index ,这样用户可以看到自己正在选择哪个。

 1import os
 2import re
 3def get_menu_choice(options):
 4    shortcuts = scan_short_cuts(options)  # scan for shortcuts
 5    selected_index = 0
 6    print(shortcuts)
 7    while True:
 8        show_menu(options, selected_index)
 9        key = get_key()
10        if key == 'enter':  # Enter key to select
11            return selected_index
12        elif key in ('up','down'):  # Up or Down arrow
13            selected_index = (selected_index + (1 if key == 'down' else -1) + len(options)) % len(options)
14        elif key in shortcuts:  # Shortcut key
15            show_menu(options, shortcuts[key]) #show selected option when using shortcut
16            return shortcuts[key]
17
18def scan_short_cuts(options):
19    shortcuts = {}
20    for i, option in enumerate(options):
21        match = re.match(r"\[(.*)\](.*)", option)
22        if match:
23            shortcut, text = match.group(1, 2)
24            shortcuts[shortcut] = i
25    return shortcuts
26
27
28def show_menu(options, selected_index):
29    os.system("cls" if os.name == "nt" else "clear")
30    print("Menu","current option:",selected_index)
31    for i, option in enumerate(options):
32        if i == selected_index:
33            print(f"> {option}")
34        else:
35            print(f"  {option}")
36    print("\nUse the arrow keys to move, Enter/Hotkey to select.")

使用方法非常直白:

1options = ["[1]Option 1", "[2]Option 2", "[3]Option 3","[q]quit"]
2index = get_menu_choice(options)
3print(f"You selected option {index + 1}: {options[index]}")

看看效果:

代码地址

其实我一开始想要参考一下 simple_term_menu 的源码,但是我发现我太菜了,根本看不懂他们的贼复杂的源代码(还用了信号啥的,我都不会),所以我就自己写了一个简单的。

使用的话就:

1pip install dumb-menu

可以看看我在 pypi 写的简明教程


用 Simple_term_menu 创建更好的python菜单
Python打包和上传到pypi