为什么在使用 python curses window 后 Windows 提示没有响应?

Why is the Windows prompt unresponsive after using a python curses window?

首先,我是python的新手。不得不在大学里学习它的课程,并被它的效率所吸引。

我有一个棘手的问题,在使用 curses window 后 Windows 7 提示变得无响应。在 Windows 10 中效果很好。请注意,我使用的是具有默认设置的 Win7 终端。在我的代码中,我创建了一个 curses window 来同时显示 2 个进度条,每个进度条用于文件下载。我通过将 curses window 传递给 FileDownload class(每次下载一个 class 实例)来实现这一点,该实例在 window 中处理其进度条。奇怪的是,在 Windows 7 中,当下载完成并根据提示控制 returns 时,它对键盘没有反应。我通过在使用 window 后调用 curses.endwin() 来解决这个问题,但这会导致提示一直显示在屏幕缓冲区中,隐藏了诅咒 window.

这是我的代码。非常感谢任何想法。谢谢!

# Skeleton version for simulations.
# Downloads 2 files simultaneously and shows a progress bar for each.
# Each file download is a FileDownload object that interacts with a
# common curses window passed as an argument.

import requests, math, threading, curses, datetime


class FileDownload:

    def __init__(self, y_pos, window, url):
        # Y position of the progress bar in the download queue window.
        self.__bar_pos = int(y_pos)
        self.__progress_window = window
        self.__download_url = url
        # Status of the file download object.
        self.__status = "queued"
        t = threading.Thread(target=self.__file_downloader)
        t.start()

    # Downloads selected file and handles its progress bar.
    def __file_downloader(self):
        file = requests.get(self.__download_url, stream=True)
        self.__status = "downloading"
        self.__progress_window.addstr(self.__bar_pos + 1, 1, "0%" + " " * 60 + "100%")
        size = int(file.headers.get('content-length'))
        win_prompt = "Downloading " + format(size, ",d") + " Bytes:"
        self.__progress_window.addstr(self.__bar_pos, 1, win_prompt)

        file_name = str(datetime.datetime.now().strftime("%Y-%m-%d_%H.%M.%d"))
        dump = open(file_name, "wb")

        # Progress bar length.
        bar_space = 58
        # Same as an index.
        current_iteration = 0
        # Beginning position of the progress bar.
        progress_position = 4
        # How many iterations will be needed (in chunks of 1 MB).
        iterations = math.ceil(size / 1024 ** 2)

        # Downloads the file in 1MB chunks.
        for block in file.iter_content(1024 ** 2):
            dump.write(block)

            # Progress bar controller.
            current_iteration += 1
            step = math.floor(bar_space / iterations)
            if current_iteration > 1:
                progress_position += step
            if current_iteration == iterations:
                step = bar_space - step * (current_iteration - 1)
            # Updates the progress bar.
            self.__progress_window.addstr(self.__bar_pos + 1, progress_position,
                                          "#" * step)
        dump.close()
        self.__status = "downloaded"

    # Returns the current status of the file download ("queued", "downloading" or
    # "downloaded").
    def get_status(self):
        return self.__status


# Instantiates each file download.
def files_downloader():

    # Creates curses window.
    curses.initscr()
    win = curses.newwin(8, 70)
    win.border(0)
    win.immedok(True)

    # Download URLs.
    urls = ["http://ipv4.download.thinkbroadband.com/10MB.zip",
            "http://ipv4.download.thinkbroadband.com/5MB.zip"]

    downloads_dct = {}
    for n in range(len(urls)):
        # Progress bar position in the window for the file.
        y_pos = n * 4 + 1
        downloads_dct[n + 1] = FileDownload(y_pos, win, urls[n])

    # Waits for all files to be downloaded before passing control of the terminal
    # to the user.
    all_downloaded = False
    while not all_downloaded:
        all_downloaded = True
        for key, file_download in downloads_dct.items():
            if file_download.get_status() != "downloaded":
                all_downloaded = False

    # Prevents the prompt from returning inside the curses window.
    win.addstr(7, 1, "-")

    # This solves the unresponsive prompt issue but hides the curses window if the screen buffer
    # is higher than the window size.
    # curses.endwin()

while input("\nEnter to continue: ") == "":
    files_downloader()

也许您正在使用 cygwin(和 ncurses):ncurses(与任何其他 curses 实现一样)在 运行 时更改终端 I/O 模式。您可能看到的变化是

  • 输入的字符不回显
  • 你必须键入 controlJ 来结束输入行,而不仅仅是 Enter
  • 输出不会在每行末尾自动刷新

它进行了这些更改以允许它读取单个字符并更有效地使用终端。

要将 back 更改为终端的正常 I/O 模式,您可以使用 endwin function. The reset_shell_mode 函数也很有用。

进一步阅读: