2014-10-02 80 views
3

我目前正在使用python/curses編寫應用程序界面,我想知道是否有可能要求用戶按鍵(cbreak模式)來隱藏或顯示某些面板或窗口而UI則不斷更新。在更新屏幕時Python/curses用戶輸入

我讀了關於curses的官方python文檔並做了一些嘗試,但即使使用cbreak模式和非阻塞輸入模式(nodelay)激活,我也無法使其正常工作(我成功地獲得了用戶輸入,但是以阻止不是我想要的UI爲代價)。

所以我的問題很簡單,可能嗎?如果是,如何?

我可能會誤讀文檔,但我還沒有找到任何其他文檔或示例。

我想過讓應用程序多線程,但我沒有看到這可以幫助我在這種情況下。

感謝您的幫助,建議或指向詳細文檔的指針。

編輯:

我終於結束了下面的多線程代碼,但它並不令人滿意。 U.I是因爲它必須是,但刷新後顯示是borked。

我也不明白爲什麼curses.panel.hidden()在隱藏所考慮的面板時返回False。似乎刷新與面板相關的窗口取消隱藏面板或類似的東西。我真的迷失在這一點!

import threading 
import curses, curses.panel 
import random 
import time 

gui = None 

class ui: 
    def __init__(self): 
     self.feeder = feeder(self) 
     self.stdscr = curses.initscr() 
     curses.noecho() 
     curses.cbreak() 
     curses.curs_set(0) 
     self.stdscr.keypad(1) 

     self.win1 = curses.newwin(10, 50, 0, 0)  
     self.win1.border(0) 
     self.pan1 = curses.panel.new_panel(self.win1) 
     self.win2 = curses.newwin(10, 50, 0, 0)  
     self.win2.border(0) 
     self.pan2 = curses.panel.new_panel(self.win2) 
     self.win3 = curses.newwin(10, 50, 12, 0) 
     self.win3.border(0) 
     self.pan3 = curses.panel.new_panel(self.win3) 

     self.win1.addstr(1, 1, "Window 1") 
     self.win2.addstr(1, 1, "Window 2") 
     self.win3.addstr(1, 1, "Press 's' to switch windows or 'q' to quit.") 


     self.pan1.hide() 
     self.win1.refresh() 

     curses.panel.update_panels() 
     self.win2.refresh() 
     self.feeder.start() 


    def ask(self): 
     while True: 
      self.win3.addstr(5,1, "Hidden = win1: "+str(self.pan1.hidden())+\ 
          "win2:"+str(self.pan2.hidden()), 0) 
      self.win3.refresh() 
      k = self.win3.getkey() 
      if k == 's': 
       if self.pan1.hidden(): 
        self.pan2.hide() 
        self.pan1.show() 
        self.win1.refresh() 
        self.win3.addstr(2, 1, "Pan1 restored") 
       else: 
        self.pan1.hide() 
        self.pan2.show() 
        self.win2.refresh() 
        self.win3.addstr(2, 1, "Pan2 restored") 
       self.win3.addstr(5,1, "Hidden = win1: "+\ 
           str(self.pan1.hidden())+\ 
           " win2:"+str(self.pan2.hidden()), 0) 

      elif k == 'q': 
       break   
     self.quit_ui() 

    def quit_ui(self): 
     self.feeder.stop() 
     curses.nocbreak() 
     self.stdscr.keypad(0) 
     curses.curs_set(1) 
     curses.echo() 
     curses.endwin() 
     exit(0) 

    def display_data(self, window, data): 
     window.addstr(3, 1, data, 0) 



class feeder(threading.Thread): 
    # Fake U.I feeder 
    def __init__(self, ui): 
     super(feeder, self).__init__() 
     self.running = False 
     self.ui = ui 
     self.count = 0 

    def stop(self): 
     self.running = False 

    def run(self): 
     self.running = True 
     self.feed() 

    def feed(self): 
     while self.running: 
      self.ui.win1.addstr(3, 1, str(self.count)+\ 
           ": "+str(int(round(random.random()*9999)))) 
      self.ui.win1.addstr(4, 1, str(self.running)) 
      self.ui.win2.addstr(3, 1, str(self.count)+\ 
           ": "+str(int(round(random.random()*9999)))) 
      self.ui.win2.addstr(4, 1, str(self.running)) 
      time.sleep(0.5) 
      self.count += 1 


if __name__ == "__main__": 
    gui = ui() 
    gui.ask() 
+0

你可以有一個屏幕線程更新你的屏幕刷新率,並有另一個線程讀取你的用戶輸入,可以改變內部對象(如切換窗口)。 – 2014-10-02 16:07:34

+1

不確定Python的設置是什麼,但詛咒通常在多線程中表現不佳。這樣做的一個正常方法是在超時(或有定時器信號中斷它)調用'select()'時循環,如果輸入已準備好,則調用'getch()';否則更新UI。 – 2014-10-03 03:15:12

+0

我沒有發現任何關於python curses模塊中的「select」方法或函數。 「select」的唯一引用是關於一個名爲「KEY_SELECT」的常量。 與「選擇」關鍵字,我發現這個博客[鏈接](http://cc.byexamples.com/2007/04/08/non-blocking-user-input-in-loop-without-ncurses/)解釋如何用C++解決這個問題。感謝您的幫助。我仍在尋找解決方案。 – Doug713705 2014-10-03 06:05:48

回答

3

我終於成功地通過在double while循環中從sys.stdin中讀取一個字節,然後避免使用另一個線程。 下面的代碼可能無法在MS Windows上工作,因爲我不是一個專業的開發人員,它可能未優化或拋出未捕獲的錯誤,但它只是一個草案,使我明白事情如何工作(儘管,評論是歡迎)。 特別感謝Paul Griffiths指導我到sys.stdin。

#!/usr/bin/python 
# -*- coding: iso-8859-1 -*- 

import curses, curses.panel 
import random 
import time 
import sys 
import select 

gui = None 

class ui: 
    def __init__(self): 
     self.stdscr = curses.initscr() 
     curses.noecho() 
     curses.cbreak() 
     curses.curs_set(0) 
     self.stdscr.keypad(1) 

     self.win1 = curses.newwin(10, 50, 0, 0)  
     self.win1.border(0) 
     self.pan1 = curses.panel.new_panel(self.win1) 
     self.win2 = curses.newwin(10, 50, 0, 0)  
     self.win2.border(0) 
     self.pan2 = curses.panel.new_panel(self.win2) 
     self.win3 = curses.newwin(10, 50, 12, 0) 
     self.win3.border(0) 
     self.pan3 = curses.panel.new_panel(self.win3) 

     self.win1.addstr(1, 1, "Window 1") 
     self.win2.addstr(1, 1, "Window 2") 
     self.win3.addstr(1, 1, "Press 's' to switch windows or 'q' to quit.") 

     self.pan1.hide() 

    def refresh(self): 
     curses.panel.update_panels() 
     self.win2.refresh() 
     self.win1.refresh() 

    def switch_pan(self): 
     if self.pan1.hidden(): 
      self.pan2.bottom() 
      self.pan2.hide() 
      self.pan1.top() 
      self.pan1.show() 
     else: 
      self.pan1.bottom() 
      self.pan1.hide() 
      self.pan2.top() 
      self.pan2.show() 

     self.refresh() 

    def quit_ui(self): 
     curses.nocbreak() 
     self.stdscr.keypad(0) 
     curses.curs_set(1) 
     curses.echo() 
     curses.endwin() 
     print "UI quitted" 
     exit(0) 


class feeder: 
    # Fake U.I feeder 
    def __init__(self): 
     self.running = False 
     self.ui = ui() 
     self.count = 0 

    def stop(self): 
     self.running = False 

    def run(self): 
     self.running = True 
     self.feed() 

    def feed(self): 
     while self.running : 
      while sys.stdin in select.select([sys.stdin], [], [], 0)[0]: 
       line = sys.stdin.read(1) 
       if line.strip() == "q": 
        self.stop() 
        self.ui.quit_ui() 
        break 
       elif line.strip() == "s": 
        self.ui.switch_pan() 

      self.ui.win1.addstr(3, 1, str(self.count)+\ 
           ": "+str(int(round(random.random()*999)))) 
      self.ui.win1.addstr(4, 1, str(self.running)) 
      self.ui.win2.addstr(3, 1, str(self.count)+\ 
           ": "+str(int(round(random.random()*999)))) 
      self.ui.win2.addstr(4, 1, str(self.running)) 
      self.ui.refresh() 
      time.sleep(0.1) 
      self.count += 1 

if __name__ == "__main__": 
    f = feeder() 
    f.run()