2015-07-21 45 views
1

我在我的python程序中運行兩個線程,一個線程使用python curses運行菜單系統並等待輸入,另一個線程根據菜單選項進行分析並輸出結果狀態通過內置的print()功能。我的問題在於,打印與詛咒的打印效果不佳,因爲如果curses.echo()打開,則打印到我等待輸入的行,並且如果使用curses.noecho(),則根本不會顯示輸出。python curses處理另一個線程的stdout

因爲我想在這裏和顯示輸出的時候,我解決了這個最初是設置window.timeout(1000)控制,然後讓輸入迴路是這樣的:

try: 
    c = window.getkey() 
except: 
    c = -1 #timeout or error in input 

if c == -1: 
    check_for_input() 
elif c == 'KEY_RESIZE': 
    ... 

這工作得很好,讓我每秒檢查stdout的輸出,然後如果需要更新菜單,同時仍允許用戶輸入。我遇到的問題是我不知道如何捕獲標準輸出,並選擇在需要時顯示它。這是可能嗎?

回答

2

所以我想出了這一個,但作爲一個免責聲明,我不知道如果這是線程安全的(迄今沒有問題)。

可以使用該庫的python庫io,更具體地說是StringIO來捕獲打印輸出。

N.B.這是用於Python3的

實質上,解決方案是將sys.stdout設置爲io.StringIO的實例並從中讀取。

external_output = None 
stdout_buff = io.StringIO() 
sys.stdout = stdout_buff 
stream_pos = 0 # lst read position of the stdout stream. 

while True: #input loop 
    ... 
    if stdout_buff.tell() > stream_pos: 
     stdout_buff.seek(stream_pos) 
     external_output = stdout_buff.read() 
     stream_pos = stdout_buff.tell() 
    ... 

下面,我包括我使用的情況下,上面的菜單系統的簡短例子並不清楚有這個問題的人,在希望這將清除它。

乾杯!


未修改的版本

所以使用菜單的顯示和事件循環看起來就像這樣:(注意,這是一個事物的簡化版本,因此很多事情要做與顯示菜單和顯示用戶輸入的內容已被省略)。這個基本的例子顯示一個菜單,並允許用戶退出程序,輸入數字到他們的選擇,或輸入他們的選擇,然後打印出來。

import sys 
import curses 

def menu(stdscr): 
    # initial startup settings 
    curses.start_color() 
    curses.use_default_colors() 
    stdscr.timeout(1000) #timeout the input loop every 1000 milliseconds 
    user_selection = '' 
    # other unrelated initial variables 

    while True: #display loop 
     stdscr.clear() 
     # the following is actually in a function to handle automatically 
     # taking care of fitting output to the screen and keeping 
     # track of line numbers, etc. but for demonstration purposes 
     # I'm using the this 
     start_y = 0 
     stdscr.addstr(start_y, 0, 'Menu Options:') 
     stdscr.addstr(start_y+1, 0, '1) option 1') 
     stdscr.addstr(start_y+2, 0, '1) option 2') 
     stdscr.addstr(start_y+3, 0, '1) option 3') 
     stdscr.addstr(start_y+4, 0, '1) option 4') 

     while True: #input loop 
      c = stdscr.getkey() 
      if c == 'KEY_RESIZE': 
       handle_window_resize() # handle changing stored widths and height of window 
       break #break to redraw screen 
      elif c.isdigit(): 
       # if user typed a digit, add that to the selection string 
       # users may only select digits as their options 
       user_selection += c 
      elif c == '\n': 
       # user hit enter to submit their selection 
       if len(user_selection) > 0: 
        return user_selection 
      elif c == 'q': 
       sys.exit() 



result = curses.wrapper(menu) 
print(result) 

在這個例子中仍出現該問題,從該一個同時運行的線程的任何輸出將在光標的stdscr打印該程序當前正在等待來自用戶的輸入。


修改版本

import sys 
import curses 
from io import StringIO 


def menu(stdscr): 
    # initial startup settings 
    curses.start_color() 
    curses.use_default_colors() 
    stdscr.timeout(1000) #timeout the input loop every 1000 milliseconds 
    user_selection = '' 
    # other unrelated initial variables 

    # output handling variables 
    external_output = None # latest output from stdout 
    external_nlines = 2 # number of lines at top to leave for external output 
    stdout_buff = StringIO() 
    sys.stdout = stdout_buff 
    stream_pos = 0 # lst read position of the stdout stream. 

    while True: #display loop 
     stdscr.clear() 
     # the following is actually in a function to handle automatically 
     # taking care of fitting output to the screen and keeping 
     # track of line numbers, etc. but for demonstration purposes 
     # I'm using the this 
     if external_output is not None: 
      stdscr.addstr(0, 0, "stdout: " + external_output) 

     start_y = external_nlines 
     stdscr.addstr(start_y, 0, 'Menu Options:') 
     stdscr.addstr(start_y+1, 0, '1) option 1') 
     stdscr.addstr(start_y+2, 0, '1) option 2') 
     stdscr.addstr(start_y+3, 0, '1) option 3') 
     stdscr.addstr(start_y+4, 0, '1) option 4') 

     while True: #input loop 
      try: 
       c = stdscr.getkey() 
      except: 
       c = -1 # 1000ms timeout or error 

      if c == -1: 
       if stdout_buff.tell() > stream_pos: 
        # current stdout_buff pos is greater than last read 
        # stream position, so there is unread output 
        stdout_buff.seek(stream_pos) 
        external_output = stdout_buff.read().strip() #strip whitespace 
        stream_pos = stdout_buff.tell() #set stream_pos to end of stdout_buff 
        break #redraw screen with new output 
      elif c == 'KEY_RESIZE': 
       handle_window_resize() # handle changing stored widths and height of window 
       break #break to redraw screen 
      elif c.isdigit(): 
       # if user typed a digit, add that to the selection string 
       # users may only select digits as their options 
       user_selection += c 
      elif c == '\n': 
       # user hit enter to submit their selection 
       if len(user_selection) > 0: 
        sys.stdout = sys.__stdout__ # reset stdout to normal 
        return user_selection 
      elif c == 'q': 
       sys.stdout = sys.__stdout__ # reset stdout to normal 
       sys.exit() 



result = curses.wrapper(menu) 
print(result)