V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
推荐学习书目
Learn Python the Hard Way
Python Sites
PyPI - Python Package Index
http://diveintopython.org/toc/index.html
Pocoo
值得关注的项目
PyPy
Celery
Jinja2
Read the Docs
gevent
pyenv
virtualenv
Stackless Python
Beautiful Soup
结巴中文分词
Green Unicorn
Sentry
Shovel
Pyflakes
pytest
Python 编程
pep8 Checker
Styles
PEP 8
Google Python Style Guide
Code Style from The Hitchhiker's Guide
lswang
V2EX  ›  Python

分享一个 Python 命令行下列表选择

  •  
  •   lswang · 2020-11-25 17:56:25 +08:00 · 2250 次点击
    这是一个创建于 1462 天前的主题,其中的信息可能已经有所发展或是发生改变。

    可以通过上下键 + Enter 来选择,Ctrl+C 取消

    [注意] 只能支持 linux + macos

    超过 20 个数据,会出现 ==== ^ ======== v ==== 提示还有额外数据

    # -*- coding: utf-8 -*-
    import sys
    import termios
    import tty
    
    
    left = u'\u001b[1000D'
    right = u'\u001b[1000C'
    clear_line = u'\u001b[2K'
    up = u'\u001b[1A'
    down = u'\u001b[1B'
    clear_to_bottom = u'\u001b[J'
    
    MAX_SHOW_COUNT = 20
    
    class CommandSingleSelector:
        selectors = None
        cur_index = 0
        cursor_index = -1
        header_word = None
        is_cancel = False
        top_index = 0
        bottom_index = 0
    
        def __init__(self, selectors, header_word):
            self.selectors = selectors
            if len(selectors) > MAX_SHOW_COUNT:
                self.bottom_index = MAX_SHOW_COUNT
            else:
                self.bottom_index = len(selectors) - 1
    
            self.header_word = header_word
    
        def up(self):
            if self.cur_index > 0:
                self.cur_index = self.cur_index - 1
                if self.cur_index < self.top_index:
                    self.top_index = self.top_index - 1
                    self.bottom_index = self.bottom_index - 1
                self.print_multi_line()
    
        def down(self):
            if self.cur_index < len(self.selectors) - 1:
                self.cur_index = self.cur_index + 1
                if self.cur_index > self.bottom_index:
                    self.top_index = self.top_index + 1
                    self.bottom_index = self.bottom_index + 1
                self.print_multi_line()
    
        def exit(self):
            self.clear_multi_line()
    
        def get_selector(self):
            sys.stdout.write(self.header_word + '\n')
            self.print_multi_line()
            is_multi_first = False
            is_multi_second = False
    
            while True:
                fd = sys.stdin.fileno()
                old_settings = termios.tcgetattr(fd)
                try:
                    tty.setraw(sys.stdin.fileno())
                    ch = sys.stdin.read(1)
                    as_code = ord(ch)
    
                    # find the multi key for up/down
                    if as_code == 27:
                        is_multi_first = True
                    elif as_code == 91:
                        if is_multi_first:
                            is_multi_second = True
                    elif as_code == 65:
                        if is_multi_first and is_multi_second:
                            self.up()
                        is_multi_second = False
                        is_multi_first = False
                    elif as_code == 66:
                        if is_multi_first and is_multi_second:
                            self.down()
                        is_multi_second = False
                        is_multi_first = False
                    elif as_code == 13:
                        is_multi_second = False
                        is_multi_first = False
                        self.exit()
                        break
                    elif as_code == 3:
                        is_multi_second = False
                        is_multi_first = False
                        self.exit()
                        self.is_cancel = True
                        break
                    else:
                        is_multi_second = False
                        is_multi_first = False
                finally:
                    termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
    
            if self.is_cancel:
                raise Exception('Cancel this operation.')
    
            return self.selectors[self.cur_index]
    
        def print_multi_line(self):
            sys.stdout.write((self.cursor_index + 1) * up + left)
            sys.stdout.write(clear_to_bottom)
    
            up_size = self.bottom_index - self.cur_index + 1 + 1 + 1 # first and last word
    
            has_first_sign = '          '
            if self.top_index > 0:
                has_first_sign = '   ==== ^ ===='
    
            sys.stdout.write(left + clear_line + has_first_sign + '\n')
    
    
            for index, item in enumerate(self.selectors):
                if self.top_index <= index <= self.bottom_index:
                    first_word = '   '
                    if index == self.cur_index:
                        first_word = ' > '
                        sys.stdout.write(left + clear_line + first_word + item + '\n')
                    else:
                        sys.stdout.write(left + clear_line + first_word + item + '\n')
            has_last_sign = '          '
            if len(self.selectors) - 1 > self.bottom_index:
                has_last_sign = '   ==== v ===='
            sys.stdout.write(left + clear_line + has_last_sign + '\n')
    
            back_to_cur_index = up * (up_size -1)
            self.cursor_index = self.cur_index - self.top_index
            sys.stdout.write(back_to_cur_index)
            sys.stdout.write(left)
    
        def clear_multi_line(self):
            back_to_top = up * (self.cur_index - self.top_index + 2)
            sys.stdout.write(back_to_top)
            sys.stdout.write(left)
            sys.stdout.write(clear_to_bottom)
    
    
    if __name__ == '__main__':
        selectors = ['HP Printer', 'XiaoMi Printer','DELL Printer']
        command_selector = CommandSingleSelector(selectors=selectors, header_word='Please Select One Printer: ')
        try:
            sel = command_selector.get_selector()
            print sel
        except Exception, e:
            print e.message
    
    
    4 条回复    2020-11-26 09:42:05 +08:00
    lisniuse
        1
    lisniuse  
       2020-11-25 21:11:24 +08:00
    可以来这里分享: https://www.kuxai.com/forum
    ClericPy
        2
    ClericPy  
       2020-11-25 22:11:21 +08:00
    以前倒是折腾过类似 terminal UI 的, 有三个库印象深刻 PyInquirer, console-menu, questionary

    还好我只 star 最好的, 所以还能找到
    renmu123
        3
    renmu123  
       2020-11-25 22:40:45 +08:00
    我最近再用一个 poetry 出品的 celo 工具,也是类似的交互式命令行工具,相比楼上的几个,弱化了交互式组件的部分,增强了作为命令行的部分(其实完全可以拼一起造个融合怪)
    frostming
        4
    frostming  
       2020-11-26 09:42:05 +08:00
    Paging 嘛,rich 有现成的支持,prompt_toolkit 也有
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5797 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 03:35 · PVG 11:35 · LAX 19:35 · JFK 22:35
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.