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

分享一个 Python 命令行下列表选择,第1张

分享一个 Python 命令行下列表选择,第2张

可以通过上下键 + 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

----------------------- 以下是精选回复-----------------------

答:可以来这里分享: https://www.kuxai.com/forum
答:以前倒是折腾过类似 terminal UI 的, 有三个库印象深刻 PyInquirer, console-menu, questionary

还好我只 star 最好的, 所以还能找到
答:我最近再用一个 poetry 出品的 celo 工具,也是类似的交互式命令行工具,相比楼上的几个,弱化了交互式组件的部分,增强了作为命令行的部分(其实完全可以拼一起造个融合怪)
答:Paging 嘛,rich 有现成的支持,prompt_toolkit 也有

DABAN RP主题是一个优秀的主题,极致后台体验,无插件,集成会员系统
网站模板库 » 分享一个 Python 命令行下列表选择

0条评论

发表评论

提供最优质的资源集合

立即查看 了解详情