【问题标题】:How to put text in input line: how to ask for user input on the command line while providing a 'default' answer that the user can edit or delete?如何在输入行中输入文本:如何在命令行上要求用户输入,同时提供用户可以编辑或删除的“默认”答案?
【发布时间】:2015-07-23 12:12:07
【问题描述】:

我正在创建一个要求从命令行输入的 Python 脚本。用户将能够编辑文件的一部分。我可以要求新信息并在文件中覆盖它,没问题。但我宁愿将文件的待编辑部分放在命令行中,因此不必完全输入。这可能吗?

文件:

1|This file
2|is not empty

例子:

>>>edit line 2
Fetching line 2
Edit the line then hit enter
>>>is not empty                  #This is written here by the script, not by the user

然后可以改成

>>>is not full either
Edited file

文件更改为:

1|This file
2|is not full either

我希望很清楚我想要完成什么。

Thisquestion 据说可以回答我的问题,在一定程度上确实如此。当我使用readline 运行 Linux 时会出现这种情况。然而,我不是。我正在使用 Windows,但没有使用 readline。我只想使用标准库。
该问题还提供了针对 Windows 的答案。但是,我得到一个ImportErrorwin32console,这可能是因为提到的问题不是关于Python3.4,而是我的问题。 另外,我想知道这是否可能使用标准库,而不是使用外部库。

【问题讨论】:

  • 所以问题是:如何在命令行上要求用户输入,同时提供用户可以编辑或删除的“默认”答案?
  • 我不能肯定地说这是不可能的,但在我这些年来,我从未在标准库中看到任何可以做到这一点的东西。 (除非它位于特定于操作系统的模块中,例如 curses
  • @tobias_k 是的,这正是我要找的
  • @Kevin 不确定curses 是什么意思,但我没有使用任何与默认库不同的库。

标签: python python-3.x


【解决方案1】:

您应该只有 2 个变量:一个用于标准字符串,一个用于用户自行更改的字符串。 喜欢:

str1 = 'String that is standard'
str2 = str1 #it usually will be standard string
usr = input('your text goes here')
if len(usr) != 0:
    str2 = usr
#and here goes code for writing string into file

【讨论】:

  • 这可能会解决具有默认值的问题,但它不允许用户编辑默认值,我认为这是问题的核心。
【解决方案2】:

不幸的是,我不知道标准库中是否有带有默认值的input()

有一个外部解决方案 - 使用 win32console,如 this answer 中所述。但是,据我所知,它有两个陷阱。 首先,导入捆绑在一个包pywin32 中。所以你会使用pip install pywin32,除非它不起作用,因为 second 陷阱:关于 pypi 包的信息已过时,它说包与 Python 3.4 不兼容...

但事实上,它可以工作!您应该按照 pypi 项目页面上可见的“下载 URL”(即 https://sourceforge.net/projects/pywin32/files/pywin32/ )并安装最新版本。我刚刚为 Py3.4 安装了 build 219,因为我自己也使用这个 Python 版本。页面上为 32 位和 64 位 Windows 的多个 Python 版本提供了安装程序。

另外,我已经调整了上面链接的 SO 答案中的代码以在 Python 3 中工作:

import win32console

_stdin = win32console.GetStdHandle(win32console.STD_INPUT_HANDLE)

def input_def(prompt, default=''):
    keys = []
    for c in str(default):
        evt = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
        evt.Char = c
        evt.RepeatCount = 1
        evt.KeyDown = True
        keys.append(evt)

    _stdin.WriteConsoleInput(keys)
    return input(prompt)

if __name__ == '__main__':
    name = input_def('Folder name: ', 'it works!!!')
    print()
    print(name)

这适用于我的 Windows 机器...如果这不适用于您的机器,您能否提供错误消息?

【讨论】:

  • OP 要求 stdlib 解决方案(原则上,您可以make the calls using ctypes module)。如果我们要安装 3-rd 方库;我会尝试pyreadline with this answer(和win-unicode-console 以支持控制台中的任意Unicode输入/输出)。
  • 从问题中的措辞来看,我认为 OP 只是“想知道”是否有 stdlib 解决方案,但要求任何可行的解决方案:) 包 readline 在 Windows 上不可用,替换包 @ 987654335@ 不在核心中。 win-unicode-console 也是如此。不过,我刚刚测试了pyreadline,因为它看起来更简单,但this gist 在我的情况下不起作用。
  • 这段代码有时会在我的默认字符串的开头添加随机字符,知道为什么吗? (我猜罪魁祸首是evt = win32console.PyINPUT_RECORDType(win32console.KEY_EVENT)
  • 实际上keys 变量似乎很好,所以我猜问题出在_stdin.WriteConsoleInput(keys)
【解决方案3】:

你可以用 tkinter 做到这一点:

from tkinter import *
def enter():
    global commandEntry
    command = commandEntry.get()
    # Do stuff with command
    commandEntry.delete(0, END)
def edit_line(line):
    global commandEntry
    commandEntry.insert(0, line)
root = Tk()
messageVar = StringVar()
messageVar.set("Enter a command:")
message = Label(root, textvariable=messageVar)
commandEntry = Entry(root)
enterButton = Button(root, text="Enter", command=enter)
root.mainloop()

【讨论】:

    【解决方案4】:

    我已经编写了一个行编辑器,希望它能满足您的需求。但这是一个快速而肮脏的黑客攻击。它仅适用于 Windows,并在 Windows 10 上使用 CPython 3.6.5 编写,因此它的使用可能会受到限制。它已在代码页 1252(ANSI 拉丁语 1;西欧 (Windows))和代码页 65001 (utf-8) 上进行了测试。这是非常基本的并且有点迟钝,因为它没有进行速度优化。 (我应该用 C 重写它,但我没有时间。)它几乎没有经过测试,文档也很差。

    import msvcrt
    import os
    import sys
    
    if os.name != 'nt':
        raise NotImplementedError('This module works only on MS Windows!')
    
    CTRL_00         = 0
    CTRL_E0         = 224
    KEY_BACKSPACE   = 8
    KEY_DELETE      = 83                                            # CTRL
    KEY_END         = 79                                            # CTRL
    KEY_ESC         = 27
    KEY_HOME        = 71                                            # CTRL
    KEY_INSERT      = 82                                            # CTRL
    KEY_LEFT        = 75                                            # CTRL
    KEY_RETURN      = 13
    KEY_RIGHT       = 77                                            # CTRL
    
    flush = sys.stdout.flush
    write = sys.stdout.write
    
    mode    = ('[OVR]> ', '[INS]> ')                                # overwrite, insert
    prefix  = len(mode[0])
    
    def _update_line(insert, source, length, line, target):
        """Write a new line and position the cursor.
        source: previous cursor position
        length: old line length
        line:   edited line
        target: next cursor position
        """
        write('\b' * source)                                        # set cursor to start of line
        write(' ' * length)                                         # erase old line
        write('\b' * length)                                        # again, set cursor to start of line
        write(mode[insert] + line[prefix:])                         # write updated line
        write('\b' * (len(line) - target))                          # set cursor to new position
        flush()                                                     # write buffer to screen
    
    def mswin_line_edit(default_string, insert=True):
        """Edit a MS Windows CLI line."""
    
        insert = insert
        line = mode[insert] + default_string
        count = len(line)
        before = line[:count]
        after = line[count:]
        print(line, end='', flush=True)
        cursor = count
    
        while True:
            key = msvcrt.getwch()
            num = ord(key)
            if num == KEY_ESC:                                      # abort edit
                return default_string
            if num == KEY_RETURN:                                   # finish edit
                return line
            if num == KEY_BACKSPACE:                                # delete character before cursor
                if cursor > prefix:
                    before = line[:cursor - 1]
                    after = line[cursor:]
                    line = before + after
                    _update_line(insert, cursor, count, line, cursor - 1)
                    cursor -= 1
                    count = len(line)
            elif num == CTRL_E0 or num == CTRL_00:                  # CTRL
                ctrl = ord(msvcrt.getwch())
                if ctrl == KEY_END:                                     # set cursor after last character
                    if cursor < count:
                        before = line
                        after = ''
                        _update_line(insert, cursor, count, line, count)
                        cursor = count
                elif ctrl == KEY_HOME:                                  # set cursor before first character
                    if cursor > prefix:
                        before = ''
                        after = line
                        _update_line(insert, cursor, count, line, prefix)
                        cursor = prefix
                elif ctrl == KEY_LEFT:                                  # move cursor 1 character to the left
                    if cursor > prefix:
                        before = line[:cursor]
                        after = line[cursor:]
                        _update_line(insert, cursor, count, line, cursor - 1)
                        cursor -= 1
                elif ctrl == KEY_RIGHT:                                 # move cursor 1 character to the right
                    if cursor < count:
                        before = line[:cursor]
                        after = line[cursor:]
                        _update_line(insert, cursor, count, line, cursor + 1)
                        cursor += 1
                elif ctrl == KEY_DELETE:                                # delete character after cursor
                    if cursor < count:
                        before = line[:cursor]
                        after = line[cursor + 1:]
                        line = before + after
                        _update_line(insert, cursor, count, line, cursor)
                        count = len(line)
                elif ctrl == KEY_INSERT:                                # switch insert/overwrite mode
                    insert ^= True
                    _update_line(insert, cursor, count, line, cursor)
            else:                                                   # ordinary character
                before = line[:cursor] + key
                if insert:
                    after = line[cursor:]
                else:
                    after = line[cursor + 1:]
                line = before + after
                _update_line(insert, cursor, count, line, cursor + 1)
                cursor += 1
                count = len(line)
    
    if __name__ == '__main__':
        test_string = input('test string: ')
        result = mswin_line_edit(test_string)
        print(f'\n{result}')
    

    【讨论】:

    • 太糟糕了,它不能在 Mac 上开箱即用(进行一些最小的调整)。也许您可以调整Python read a single character from the user 中的“通用getch 答案?
    • 我很抱歉。我很少使用 Linux (Unix),也没有任何使用 Mac 的权限。所以我无法编写(尤其是测试)与操作系统无关的版本。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-30
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多