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

讨论 angelfairy
Lv2 初级炼丹师
发布在 Python编程   108   0
讨论 angelfairy   108   0

可以通过上下键 + 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
版权声明:作者保留权利,不代表意本站立场。如需转载请联系本站以及作者。

参与讨论

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

EditorJs 编辑器

沙发,很寂寞~
反馈
to-top--btn