curses是一個提供終端屏幕打印和鍵盤處理的庫, 我個人的理解就是終端里的gui(當然它是基於文本的)... 寫2048的時候用到了這個庫, 所以現在過來好好研究一下這個庫...
下面是文檔內容 :
首先在你做任何事之前, 你必須先調用 initscr() 初始化curses, 這個函數主要的作用是決定當前終端的類型, 然后發送一些必要的設置給終端, 並且創建獨立的內部數據結構. 如果成功初始化的話, 該函數會返回一個代表屏幕的對象, 我們通常稱為 stdscr (C語言就有的慣例). 另一方面, 正常情況下, 我們在終端上打字時, 你輸入一個a終端上就會出現一個a, 但是對於curses編程來說這往往是沒有必要的, 我們可以用 noecho() 來關掉它. 同時我們也需要按鍵之后得到立即響應而不是傻傻地回車之后再響應, 所以我們需要把輸入模式改成 cbreak 模式而不是通常的緩沖輸入模式, 這需要調用 cbreak() . 同時, 我們輸入過程中有很多特別的鍵位, 比如方向鍵上下左右等等, 我們需要特殊處理這些鍵位的話, 我們可以調用 keypad(True), 這樣的話, 按下左鍵位將返回一個類似 KEY_LEFT 的特殊值. 如果你需要結束終端的話, 只需要把你上面對終端進行的定制化設置關閉即可 :
import curses #開啟 stdscr = curses.initscr() curses.noecho() curses.cbreak() stdscr.keypad(True) #關閉, 回復正常終端 curses.nocbreak() stdscr.keypad(False) curses.echo() curses.endwin()
但是這樣其實是有問題的, 當遇到程序半途出現異常然后游戲直接非正常關閉的情況時, 這樣會使得終端變得很奇怪... 所以python中加入了另外一個函數來幫助我們解決這個問題, 也就是 wrapper(), 該函數需要一個可調用作為參數, 並完成上述的初始化過程, 同時初始化顏色如果當前終端提供了對顏色的支持的話, 一旦調用對象返回, 該函數會自動重載調用前的終端狀態, 因為該函數的內部使用try-catch 實現的, 它在出現異常時也會先回復終端, 然后再次生成異常, 所以在解決了終端問題的同時仍然正常打印異常信息...
1 from curses import wrapper 2 3 def main(stdscr): 4 # Clear screen 5 stdscr.clear() 6 7 # This raises ZeroDivisionError when i == 10. 8 for i in range(0, 11): 9 v = i-10 10 stdscr.addstr(i, 0, '10 divided by {} is {}'.format(v, 10/v)) 11 12 stdscr.refresh() 13 stdscr.getkey() 14 15 wrapper(main)
在curses中, 窗口對象是最基本的抽象, 它代表長方形的屏幕區域, 支持顯示文本, 文本擦除 以及用戶輸入. 之前提到的 stdscr 就是一個窗口對象, 它代表的是整個屏幕. 但單窗口並不是任何時候都能滿足人們的需求, 所以我們可以用 newwin() 來獲得一個新的窗口對象 :
begin_x = 20; begin_y = 7 height = 5; width = 40 win = curses.newwin(height, width, begin_y, begin_x)
需要注意的是, 傳入的第三個和第四個參數是先y再x, 這是歷史遺留原因.
你可以用curses.LINES 和 curses.COLS 來獲得你屏幕的y 和 x 大小... 同時如果你調用方法擦除屏幕的話屏幕並不會立即擦除, 必須要在你調用refresh()之后才行, 設計成這樣當然也是有歷史原因的, 早期的終端為了避免不必要的打印(你顯示某些文字, 之后又顯示另外的文字, 那么之前的文字可以直接不用顯示了), 設計成了這個樣子.
pad 是一個特殊的window, 他可以比實際屏幕更大, 一次只顯示一部分的屏幕, 創建pad只需要高度和寬度, 但pad對象調用刷新的時候需要特別的參數 :
1 pad = curses.newpad(100, 100) 2 # These loops fill the pad with letters; addch() is 3 # explained in the next section 4 for y in range(0, 99): 5 for x in range(0, 99): 6 pad.addch(y,x, ord('a') + (x*x+y*y) % 26) 7 8 # Displays a section of the pad in the middle of the screen. 9 # (0,0) : coordinate of upper-left corner of pad area to display. 10 # (5,5) : coordinate of upper-left corner of window area to be filled 11 # with pad content. 12 # (20, 75) : coordinate of lower-right corner of window area to be 13 # : filled with pad content. 14 pad.refresh( 0,0, 5,5, 20,75)
如果你有許多的窗口對象(都需要刷新), 為了避免不必要的閃爍, 你可以先對各個需要刷新的窗口調用 noutrefresh(), 它將升級內在的數據結構使之匹配你所要的內容, 然后統一調用 doupdate() 來刷新屏幕.
顯示文本 :
C語言中的curses庫中有很多關於文本打印的函數, 功能上只有細微的區別, 這導致該庫顯得凌亂而復雜, 在python中這些函數被封裝在 addstr() 這個函數中, 該函數有4種形式, 參數以及用法如圖 :
Form | Description |
---|---|
str or ch | Display the string str or character ch at the current position |
str or ch, attr | Display the string str or character ch, using attribute attr at the current position |
y, x, str or ch | Move to position y,x within the window, and display str or ch |
y, x, str or ch, attr | Move to position y,x within the window, and display str or ch, using attribute attr |
attr 可以用來指定顯示文本的粗體, 斜體, 反向以及顏色等等, 之后會具體解釋... 該函數接受一個string 或者 bytesstring, 然后使用窗口的encoding屬性編碼成bytes, encoding屬性默認為系統編碼, 可以用 locale.getpreferredencoding() 來查詢, 我的返回值是utf-8.
addch()接受一個長度為1的string或者bytesstring 或者一個整數. 指的注意的是該函數中整數超出255的將作為拓展字符, 例如 :
stdscr.addch(curses.ACS_PLMINUS) stdscr.addch(curses.ACS_ULCORNER) # >>> ±┌
每一次操作完, 窗口對象都會記住光標上次操作的位置, 你也可以是使用move(y, x)來移動光標, 同時你可以使用 curses.curs_set(False)來關閉光標顯示, 在一些較為古老的curses的版本中, 也用 leaveok(bool)達到相同的效果. (親測無效...)
屬性與顏色 :
屬性和顏色可以以不同的方式顯示出來, 屬性可以是一個值或者是一個集合(不知道怎么弄成一個集合的形式), 但並不能保證所有的效果都能成功的表現出來, 這取決於你的終端, 下面是一些相對來說比較保險的屬性...
Attribute | Description |
---|---|
A_BLINK |
Blinking text |
A_BOLD |
Extra bright or bold text |
A_DIM |
Half bright text |
A_REVERSE |
Reverse-video text |
A_STANDOUT |
The best highlighting mode available |
A_UNDERLINE |
Underlined text |
比如這樣你可以顯示出一個類似vim狀態欄的東西 :
1 stdscr.addstr(0, 0, "Current mode: Typing mode", 2 curses.A_REVERSE) 3 stdscr.refresh()
為了使用不同的顏色, 你必須在調用 initscr() 之后調用 start_color(), 當然如果你使用的是 curses.wrapper(), 那么這個函數會自動地調用, 一旦這些完成了, 你可以調用has_colors()函數來確認你的是否的你的終端可以表現顏色(可以的話將返回True), 庫中有多對顏色對, 每一對中都有對應的前景色和背景色, 你可以使用得到相應的顏色屬性通過 color_pair() 函數, 你可以用 bitwise-or 與其他屬性比如A_REVERSE 一起使用, 但不確保一定有效.
stdscr.addstr("Pretty text", curses.color_pair(1)) stdscr.refresh()
就像之前說過的, 每一個顏色對都有前景色和背景色, 我們可以用 init_pair(n, f, b) 來改變第n個顏色對的顏色, 當然第0對是無法改寫的, 它將黑色寫在白色的背景上. start_color() 初始化8中基本的顏色, 他們分別是 : 0:black, 1:red, 2:green, 3:yellow, 4:blue, 5:magenta, 6:cyan, and 7:white. 在curses這些顏色常數被定義為了curses.COLOR_BLACK
, curses.COLOR_RED ...
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_WHITE) #When you change a color pair, any text already displayed using that color pair
#will change to the new colors. You can also display new text in this color with: stdscr.addstr(0,0, "RED ALERT!", curses.color_pair(1))
用戶輸入 :
C語言的curses庫只是提供了非常簡單的輸入方法, 在python中這個模塊加上了一個基本文本輸入的玩意(widget 實在不知道怎么翻譯了...) , 簡單來說有2種 :
1. getch() 刷新屏幕等待用戶敲擊一個鍵, 然后如果echo() 之前被調用過得話, 這個鍵將被顯示在屏幕上, 你也可以選擇具體設置一個坐標作為光標應該移動的位置.
2. getkey() 做基本相同的事除了它返回的是字符串而不是int, 單字符返回單字符字符串, 特殊字符比如功能鍵返回更長的字符串包含這個按鍵的名字比如 'KEY_UP'或者'^G'...
當然用戶可以調用 nodelay() 在調用 nodelay(True)之后, getch() 和 getkey() 在這個窗口變成了非堵塞, 如果沒有輸入字符已經准備好, 那么 getch() 將返回 curses.ERR(-1) getkey() 將拋出異常, 也有一個 halfdelay() 函數, 他可以作為一個計時器使用, 這使得getch()具有如下特征 : 如果在給定時間內沒有輸入的話, 拋出異常... (時間的單位是十分之一秒)... (我試驗的結果是返回-1而不是拋出異常), 而且這個東西是curses的一個方法而不是窗口對象的方法, 它其實是開啟了一種模式, 和cbreak一樣, 但是有一個等待時間.
getch()一般來講會返回一個0到255的整數這代表你按下的鍵相對於的ascii碼, 大於255的一般是上下左右的功能鍵(curses.KEY_PPAGE
, curses.KEY_HOME
, or curses.KEY_LEFT
.) :
while True: c = stdscr.getch() if c == ord('p'): PrintDocument() elif c == ord('q'): break # Exit the while loop elif c == curses.KEY_HOME: x = y = 0
還有一段關於curses.ascii的, 感覺不是很重要就不翻譯了, 原文 :
The
curses.ascii
module supplies ASCII class membership functions that take either integer or 1-character string arguments; these may be useful in writing more readable tests for such loops. It also supplies conversion functions that take either integer or 1-character-string arguments and return the same type. For example,curses.ascii.ctrl()
returns the control character corresponding to its argument.
然后還有一個區的字符串的方法是 getstr(), 用的不是很多, 因為他只支持退格和回車兩個編輯鍵位, 退格用來退格, 回車用來表示輸入完畢, 下例的意思是只能輸入一個小於等於15個字符的字符串, 光標從(0, 0)開始.
curses.echo() # Enable echoing of characters # Get a 15-character string, with the cursor on the top line s = stdscr.getstr(0,0, 15)
curses.textpad 模塊提供一個文本箱子, 支持形如emacs的鍵位綁定 :
import curses from curses.textpad import Textbox, rectangle def main(stdscr): curses.use_default_colors() stdscr.addstr(0, 0, "Enter IM message: (hit Ctrl-G to send)") editwin = curses.newwin(5,30, 2,1) rectangle(stdscr, 1,0, 1+5+1, 1+30+1) stdscr.refresh() box = Textbox(editwin) # Let the user edit until Ctrl-G is struck. box.edit() # Get resulting contents message = box.gather() curses.wrapper(main)
這只是python的HOWTO指南, 很加詳細的信息可以直接看 https://docs.python.org/3.5/library/curses.html#module-curses ... 看了幾個小時終於看完了...