def on_output(self, value):
self.output_count += 1
if self.head != -1 and self.output_count > self.head:
self.on_finish()
return
if self.silent:
return
if not self.debug or self.compat_debug:
print(value, end='', flush=True)
else:
self.logging_pad.addstr(self.logging_loc, self.logging_x, str(value))
self.logging_pad.refresh(self.logging_loc - min(self.logging_loc, curses.LINES -
self.debug_lines - 1), 0, self.debug_lines, 0,
curses.LINES - 1, curses.COLS - 1)
# FIXME: This should count the number of newlines instead
if str(value).endswith('\n'):
self.logging_loc += 1
self.logging_x = 1
else:
self.logging_x += len(value)
python类COLS的实例源码
def _start_ui(self, stdscr):
"""TODO docs"""
rogue_height = 26
rogue_width = 84
# using a pad instead of the default win, it's safer
self.stdscr = curses.newpad(rogue_height, rogue_width)
self.stdscr.nodelay(True)
curses.curs_set(False)
self.draw_from_rogue()
minlogsize = 4
if curses.LINES - 1 >= rogue_height + minlogsize:
# there's enough space to show the logs
self.logpad = curses.newpad(curses.LINES - 1, curses.COLS - 1)
while True:
if self.timer_callback:
self.timer_callback()
time.sleep(self.sleep_time)
if self.keypress_callback:
try:
key = self.stdscr.getkey()
event = Event()
event.char = key
self.keypress_callback(event)
except curses.error:
pass
def draw_log(self, string):
"""Draw some logs on the screen"""
if self.logpad is not None and not self.rb.game_over():
limit = curses.LINES - self.startlog - 3
self.logpad.addstr(0, 0, " LOGS")
self.logpad.hline(1,0, "-", curses.COLS - 1)
self.logpad.hline(limit + 1,0, "-", curses.COLS - 1)
if self.loglines > limit:
self.logpad.move(2, 0)
self.logpad.deleteln()
self.logpad.move(self.loglines - 1, 0)
self.logpad.clrtoeol()
self.logpad.addstr(self.loglines - 1, 0, string)
self.logpad.hline(limit + 1,0, "-", curses.COLS - 1)
else:
self.logpad.addstr(self.loglines, 0, string)
if self.loglines <= limit:
self.loglines += 1
self.logpad.refresh(0,0, self.startlog, 0, curses.LINES - 1, curses.COLS - 1)
def draw_footer(stdscr, game):
"""Displays the controls in the last line."""
if game.is_lost() or game.is_solved():
controls = [("Press any key to continue", "")]
else:
controls = [
("Navigate:", "\u2190 \u2192 \u2191 \u2193"),
("Reveal:", "Space \u2423"),
("Toggle Mark:", "Enter \u23CE"),
("Menu:", "Escape"),
]
offset = 0
for name, control in controls:
stdscr.addstr(curses.LINES-1, offset, name, curses.A_REVERSE)
offset += len(name)
stdscr.addstr(curses.LINES-1, offset, " " + control + " ")
offset += len(control) + 2
return Rect(0, curses.LINES-1, curses.COLS-1, 1)
def _wrapped_run(self, stdscr):
self.left_width = 60
self.right_width = curses.COLS - self.left_width
# Setup windows
self.top = curses.newwin(2, curses.COLS, 0, 0)
self.left = curses.newpad(curses.LINES * 2, self.left_width)
self.right = curses.newwin(curses.LINES - 4, self.right_width, 2, self.left_width)
self.bottom = curses.newwin(2, curses.COLS, curses.LINES - 2, 0)
Color.setup_palette()
# Load some data and redraw
self.fetch_next()
self.selected = 0
self.full_redraw()
self.loop()
def curses_input(self, stdscr, row, col, prompt_string, ascii_mode=False):
"""
Get an input string with curses.
Row and col are the start position ot the prompt_string.
"""
curses.echo()
stdscr.addstr(row, col, str(prompt_string), curses.A_REVERSE)
stdscr.addstr(row + 1, col, " " * (curses.COLS - 1))
stdscr.refresh()
input_val = ""
while len(input_val) <= 0:
if ascii_mode:
input_val = chr(stdscr.getch())
break
else:
input_val = stdscr.getstr(row + 1, col, 20)
return input_val
def _get_column_widths(self):
"""
returns a dict keyed by column name, values are column widths
"""
column_widths = {}
# Calculate widths for columns
# TODO: cache these values if records don't change between renders
for column in self.columns:
record_max_width = 0
for record in self.records:
if column in record:
r_value = record[column]
if column in self.translations:
r_value = self.translations[column](r_value)
else:
r_value = str(r_value)
record_max_width = max(record_max_width, len(r_value))
record_max_width += 3
# len(column) + 3:
# len(column): space for column header
# +2: left border + space
# +1: space on right of header
column_width = max(record_max_width, len(column) + 3)
column_widths[column] = column_width
# Shrink columns until all fits on screen
# TODO: handling when there's too many columns to render happily
if sum(column_widths.values()) >= curses.COLS:
while sum(column_widths.values()) >= curses.COLS:
key_largest = max(column_widths, key=column_widths.get)
column_widths[key_largest] -= 1
return column_widths
def _render_title(self):
"""
rendering of title bar on first line (space to display modal info,
and even keybinding hints)
"""
title = "{title bar placeholder}"
title_bar = title + (" " * (curses.COLS - len(title)))
self.screen.addstr(0,0, title_bar, curses.A_REVERSE)
def mainloop(self):
"""
Called after HUD has been set up. Handles rendering and user input.
"""
# Disable cursor display by default
curses.curs_set(0)
# Display initial state
self.render()
while True:
# Render before fetching input
self.render()
# note: call is non-blocking, per __init__ calling nodelay(True)
c = self.screen.getch()
if c == curses.KEY_RESIZE:
# Terminal has been resized
# must be called so that curses.LINES, curses.COLS will change
curses.update_lines_cols()
# in case old data won't be redrawn after resize
self.screen.clear()
if c == curses.KEY_UP:
# Move up as far as the 0th record
self.selectpos = max(self.selectpos - 1, 0)
if self.selectpos < self.scrollpos:
# Handle scrolling if we were at the first record on screen
self.scrollpos -= 1
if c == curses.KEY_DOWN:
# Move down as far as the Nth record
self.selectpos = min(self.selectpos + 1, len(self.records) - 1)
if self.selectpos >= (self.scrollpos + curses.LINES - 2 - self.bottom_panel_height) :
# Handle scrolling if we were at the last record on screen
self.scrollpos += 1
def test_menu_textpad_mod_rectangle_exception(self):
"""Test that curses.error is not raised when drawing outside the bounds of the window."""
def test_function(stdscr):
stdscr.clear()
stdscr = curses.initscr()
ec2rlcore.menu_textpad_mod.rectangle(stdscr, curses.LINES + 1, curses.COLS + 1, 0, 0)
curses.wrapper(test_function)
def _draw_menu(self, screen):
"""
Given a menu window, draw the rows including the header and the scrollable rows representing menu items.
Parameters:
screen (WindowObject): the window that will be drawn to
"""
# Add the header
screen.addstr(0, int((curses.COLS - 6) / 2 - len(self.header) / 2),
self.header,
curses.A_BOLD)
# Add each item to the menu
for row in range(1 + (self.max_displayed_rows * (self.current_page - 1)),
self.max_displayed_rows + 1 +
(self.max_displayed_rows * (self.current_page - 1))):
# Pad or truncate the module name to 40 characters
row_item_name = "{:40}".format(str(self._items[row - 1])[:40])
# Truncate the row's string to the drawable width
display_str = str(row_item_name + " " + self._items[row - 1].row_right)[:curses.COLS - 6]
# Draw the row
if row + (self.max_displayed_rows * (self.current_page - 1)) == \
self.current_row + \
(self.max_displayed_rows * (self.current_page - 1)):
# Highlight the item that is currently selected
screen.addstr(row - (self.max_displayed_rows * (self.current_page - 1)),
1,
display_str.rstrip(),
curses.color_pair(1) | curses.A_BOLD)
else:
screen.addstr(row - (self.max_displayed_rows * (self.current_page - 1)),
1,
display_str)
# Stop printing items when the end of the drawable space is reached
if row == self.num_rows:
break
def __init__(self, ticks, silent, debug, compat_debug, debug_lines, autostep_debug, head):
super().__init__()
self.ticks = ticks
self.silent = silent
self.debug = debug
self.compat_debug = compat_debug
self.debug_lines = debug_lines
self.autostep_debug = autostep_debug
self.head = head
self.tick_number = 0
self.output_count = 0
if self.debug and not self.compat_debug:
self.logging_loc = 0
self.logging_x = 1
self.stdscr = curses.initscr()
curses.start_color()
curses.init_pair(1, curses.COLOR_RED, curses.COLOR_BLACK)
curses.init_pair(2, curses.COLOR_GREEN, curses.COLOR_BLACK)
curses.init_pair(3, curses.COLOR_YELLOW, curses.COLOR_BLACK)
curses.init_pair(4, curses.COLOR_BLUE, curses.COLOR_BLACK)
curses.noecho()
curses.curs_set(False)
self.win_program = curses.newwin(self.debug_lines, curses.COLS - 1, 0, 0)
self.logging_pad = curses.newpad(1000, curses.COLS - 1)
def signal_handler(signal, frame):
self.on_finish()
sys.exit(0)
signal.signal(signal.SIGINT, signal_handler)
def curses_input(self, stdscr, r, c, prompt_string):
curses.echo()
stdscr.addstr(r, c, str(prompt_string), curses.A_REVERSE)
stdscr.addstr(r + 1, c, " " * (curses.COLS - 1))
stdscr.refresh()
input_val = ""
while len(input_val) <= 0:
input_val = stdscr.getstr(r + 1, c, 20)
return input_val
def draw_from_rogue(self):
"""Draw on the screen whats on rogue"""
screen = self.rb.get_screen()
for y, line in enumerate(screen, 2):
self.stdscr.addstr(y, 0, line)
self.stdscr.refresh(2,0, 0, 0, curses.LINES - 1, curses.COLS - 1)
def draw_header(stdscr, game):
"""Draws some info about in the first line."""
if game.is_solved():
text = "Congratulations, You Won!"
elif game.is_lost():
text = "You Lost!"
else:
remaining = game.mines.count(True) - game.flags.count(minesweeper.Flags.Marked)
text = "Remaining Mines: {0}".format(remaining)
stdscr.addstr(0, 0, text, curses.A_REVERSE)
return Rect(0, 0, curses.COLS-1, 1)
def draw_screen(stdscr, game):
"""Draws the complete screen including header, game and footer."""
header_rect = draw_header(stdscr, game)
footer_rect = draw_footer(stdscr, game)
game_rect = Rect(0, header_rect.height, curses.COLS-1, curses.LINES-1-header_rect.height-footer_rect.height)
game_rect = draw_game(stdscr, game_rect, game)
return game_rect
def open_menu(stdscr, items):
"""Opens a menu containing items and returns the selected item.
Blocks until the user selected an item.
"""
width = max(map(len, items)) + 20
height = len(items*2)-1 + 4 # +2 for frame, +2 for padding
curses.curs_set(False)
selected = 0
while True:
center = (curses.COLS//2, curses.LINES//2)
menu_rect = Rect(center[0]-width//2, center[1]-height//2, width, height)
menu_rect = draw_frame(stdscr, menu_rect, thick_border=True)
for i, item in enumerate(items):
attr = curses.A_NORMAL
if i == selected:
attr = curses.A_STANDOUT
stdscr.addstr(menu_rect.y + 1 + i*2, center[0] - len(item)//2, item, attr)
c = stdscr.getch()
if c == curses.KEY_UP:
selected -= 1
if c == curses.KEY_DOWN:
selected += 1
if c == curses.KEY_ENTER or c == 10:
break
selected = clamp(selected, 0, len(items)-1)
curses.curs_set(True)
return items[selected]
def main(stdscr):
while True:
selected = open_menu(stdscr, items=("New Game", "Exit"))
if selected == "Exit":
return
if selected == "New Game":
columns, rows, num_mines = open_difficulty_menu(stdscr)
columns = clamp(columns, 0, curses.COLS-3) # 2 for frame
rows = clamp(rows, 0, curses.LINES-5) # 2 for frame, 2 for header+footer
game_loop(stdscr, columns, rows, num_mines)
def init_display():
"""
Inits the display GUI
"""
if not GUI.gui_stopped:
curses.noecho()
curses.cbreak()
curses.start_color()
GUI.screen.keypad(1)
curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_CYAN)
GUI.high_light_text = curses.color_pair(1)
GUI.normal_text = curses.A_NORMAL
curses.curs_set(0)
GUI.refresh_values()
GUI.position = 1
GUI.page = 1
GUI.box = curses.newwin(GUI.max_row + 3, curses.COLS, 0, 0)
GUI.box.addstr(1, 1, GUI.status, GUI.high_light_text)
GUI.add_bottom_menus()
GUI.screen.refresh()
GUI.box.refresh()
def add_bottom_menus():
"""
Adds the bottom menu Exit and Download
"""
GUI.box.addstr(curses.LINES - 1, 0, "ESC:Exit", GUI.high_light_text)
GUI.box.addstr(curses.LINES - 1, curses.COLS // 2, "ENTR:Download", GUI.high_light_text)
def my_raw_input(r, c, prompt_string):
"""
Gets input on the screen
:param r: y coordinate
:param c: x coordinate
:param prompt_string: The prompt string
:return: The input string
"""
curses.echo()
GUI.box.addstr(r, c, prompt_string, GUI.high_light_text)
GUI.box.refresh()
input_str = GUI.box.getstr(r + 2, c, curses.COLS)
return input_str.decode('UTF-8')
def input_n(cursor, scr_bottom, max_stock_range, stock_list, scr_dim):
stock_input = None
curses.start_color()
curses.init_pair(5,curses.COLOR_WHITE,curses.COLOR_BLUE)
stock_win = curses.newwin(1, 10, scr_dim[0]-1, 0)
stock_win.bkgd(curses.color_pair(5))
stock_box = textpad.Textbox(stock_win)
stock_win.refresh()
scr_bottom.addstr(0, curses.COLS-20, " [Enter]Save/Exit")
scr_bottom.refresh()
stock_input = stock_box.edit()
stock_input = stock_input.upper()
if str(stock_input) != "" and str(stock_input) not in stock_list:
stocks.add_stock_code(str(stock_input))
total_stocks = len(stock_list) + 1
if total_stocks > scr_dim[0] - 6:
cursor[1] = total_stocks
cursor[2] = max_stock_range
else:
cursor[1] = max_stock_range + 1
cursor[2] = cursor[1]
elif str(stock_input) or ((str(stock_input)[0:(len(str(stock_input)) - 2)] and str(stock_input)[len(str(stock_input))])) in stock_list:
total_stocks = len(stock_list)
stock_pos = stock_list.index(str(stock_input)) + 1
cursor[1] = stock_pos
if total_stocks > max_stock_range:
cursor[2] = 1
else:
cursor[2] = cursor[1]
return cursor
def center(stdscr, string, font, color_pair, oldwin):
out = toilet(string, font)
out_cols = max([len(line) for line in out])
out_lines = len(out)
win = curses.newwin(out_lines, out_cols,
(curses.LINES - out_lines)//2,
(curses.COLS - out_cols)//2)
if oldwin is not None:
oldwin.clear()
oldwin.refresh()
for li, line in enumerate(out):
win.addstr(li, 0, line, color_pair)
win.refresh()
return win
def welcome(self):
"""Displays a welcome message."""
if not self.curses:
if not self.test:
print('Welcome to Google Py Music!')
return
try:
self.main.addstr(
5, int(crs.COLS / 2) - 13, 'Welcome to Google Py Music!'
)
self.main.refresh()
except: # If this errors for some reason, just don't display anything.
pass
def get_windows():
"""
Initialize the curses windows.
Returns: Curses windows.
"""
main = crs.initscr() # For the bulk of output.
main.resize(crs.LINES - 3, crs.COLS)
inbar = crs.newwin(1, crs.COLS, crs.LINES - 1, 0) # For user input.
infobar = crs.newwin(1, crs.COLS, crs.LINES - 2, 0) # For 'now playing'.
outbar = crs.newwin(1, crs.COLS, crs.LINES - 3, 0) # For notices.
return main, inbar, infobar, outbar
def on_output(self, value):
value = str(value)
self.outputs_left -= 1
# maximum output reached, we quit the prog
if self.outputs_left == 0:
raise DotsExit
# no printing mode
if self.silent:
return
if not self.debug:
print(value, end='', flush=True)
elif self.compat_debug:
# we add the ouput to the buffer
self.compat_logging_buffer += value
# and we keep the maximum number of line to compat_logging_buffer_lines
self.compat_logging_buffer = '\n'.join(
self.compat_logging_buffer.split('\n')[-self.compat_logging_buffer_lines:])
else:
# add the output string to the pad
self.logging_pad.addstr(self.logging_loc, self.logging_x, str(value))
self.logging_pad.refresh(self.logging_loc - min(self.logging_loc, curses.LINES -
self.debug_lines - 1),
0, self.debug_lines, 0, curses.LINES - 1, curses.COLS - 1)
# FIXME: This should count the number of newlines instead
if str(value).endswith('\n'):
self.logging_loc += 1
self.logging_x = 1
else:
self.logging_x += len(value)
def main(screen):
screen.clear()
screen.keypad(True)
curses.curs_set(False)
width = curses.COLS
height = curses.LINES
if(height < 20 or width < 50):
raise RuntimeError("This terminal is too damn small!")
if not (curses.has_colors()):
raise RuntimeError("This terminal does not support colors!")
if not (curses.can_change_color()):
raise RuntimeError("This terminal does not support changing color definitions!")
conf = configs.nice_conf
menu.confmenu(conf, screen)
screen.nodelay(True)
screen.clear()
screen.refresh()
mainwin = curses.newwin(height-7, width, 0, 0)
statuswin = curses.newwin(7, width, height-7, 0)
while(1):
world = World(mainwin, conf)
activeplayer = 0
n_turns = 1
for p in itertools.cycle(world.players):
if(p.isdead):
continue
world.wind = randint(max(-conf['wind_max'], world.wind-conf['wind_change']),
min( conf['wind_max'], world.wind+conf['wind_change']))
p.isactive = True
p.active_shots = 0
while ((p.isactive or p.active_shots > 0) and not len([p for p in world.players if not p.isdead]) <= 1 ):
gamestep(screen, mainwin, statuswin, p, world, conf, n_turns)
if (len([p for p in world.players if not p.isdead]) == 1):
gameover(screen, [p for p in world.players if not p.isdead][0])
break
if (len([p for p in world.players if not p.isdead]) == 0):
gameover(screen, None)
break
n_turns += 1
def _draw_input(self, stdscr, header, message):
"""
Draw an input window with the provided message.
Parameters:
stdscr (WindowObject): the screen; handled by curses.wrapper
header (str): header message displayed above the text entry box
message (str): the message to the user displayed between the header and the text entry
Returns:
(Textbox): the Textbox's edit() returns a string representing the user's input
"""
stdscr.clear()
# Setup the title
stdscr.addstr("ec2rl module configurator", curses.A_REVERSE)
stdscr.chgat(-1, curses.A_REVERSE)
curses.curs_set(0)
num_columns = 30
num_lines = 1
uly = 3
ulx = 3
main_window = curses.newwin(curses.LINES - 1, curses.COLS, 1, 0)
screen = main_window.subwin(curses.LINES - 7, curses.COLS - 4, 4, 2)
# Setup background colors
main_window.bkgd(" ", curses.color_pair(1))
screen.bkgd(" ", curses.color_pair(2))
# Draw borders around the screen subwindow
screen.box()
input_screen = main_window.subwin(num_lines, num_columns, uly + 5, ulx + 3)
ec2rlcore.menu_textpad_mod.rectangle(screen, uly, ulx, uly + 1 + num_lines, ulx + 1 + num_columns)
screen.addstr(1, 2, header, curses.A_UNDERLINE)
# Truncate the string, if needed
display_str = message[:curses.COLS - 10]
screen.addstr(2, 5, display_str)
# Draw the pieces of the overall screen (order matters)
stdscr.refresh()
main_window.noutrefresh()
screen.noutrefresh()
input_screen.noutrefresh()
stdscr.noutrefresh()
curses.doupdate()
return ec2rlcore.menu_textpad_mod.Textbox(input_screen, bkgd_color=curses.color_pair(2)).edit()
def draw_menu(self, stdscr):
# Setup the title
# bitwise OR the color_pair and A_BOLD ints since addstr can only take one attr int
stdscr.addstr(0, 0, "ec2rl module configurator", curses.color_pair(2) | curses.A_BOLD)
stdscr.chgat(-1, curses.color_pair(2))
curses.curs_set(0)
# Configure a main window to hold the subwindows
main_window = curses.newwin(curses.LINES - 1, curses.COLS, 1, 0)
tmp_str = ""
x_pos = 0
for item in self.key_bind_help:
if len(tmp_str) + len(item) < curses.COLS - 6:
if not tmp_str:
tmp_str += item
else:
tmp_str = " ".join((tmp_str, item))
else:
main_window.addstr(x_pos, 3, tmp_str)
tmp_str = ""
tmp_str += item
x_pos += 1
main_window.addstr(x_pos, 3, tmp_str)
# Create subwindows for displaying dict items and a footer for select/exit
screen = main_window.subwin(curses.LINES - 7, curses.COLS - 4, 4, 2)
footer = main_window.subwin(3, curses.COLS - 4, curses.LINES - 3, 2)
# Setup background colors
main_window.bkgd(" ", curses.color_pair(1))
screen.bkgd(" ", curses.color_pair(2))
footer.bkgd(" ", curses.color_pair(2))
# Draw borders around the subwindows
screen.box()
footer.box()
# Erase the screen so it can be cleanly redrawn
screen.erase()
screen.border(0)
# Draw the initial screen for the user prior to entering the user input handling loop
self._draw_menu(screen)
# Add the footer
self._draw_footer(footer)
# Update the pieces
stdscr.noutrefresh()
main_window.noutrefresh()
screen.noutrefresh()
footer.noutrefresh()
curses.doupdate()
return main_window, screen, footer
def _draw_notification(stdscr, message):
"""
Draw a notification window with the provided message.
Parameters:
stdscr (WindowObject): the screen; handled by curses.wrapper
message (str): the message to the user
Returns:
True (bool)
"""
stdscr.clear()
# Setup the title
stdscr.addstr("ec2rl module configurator", curses.color_pair(2) | curses.A_BOLD)
stdscr.chgat(-1, curses.color_pair(2))
curses.curs_set(0)
message_list = [message.rstrip() for message in message.split(os.linesep)]
current_row = 1
main_window = curses.newwin(curses.LINES - 1, curses.COLS, 1, 0)
screen = main_window.subwin(curses.LINES - 7, curses.COLS - 4, 4, 2)
footer = main_window.subwin(3, curses.COLS - 4, curses.LINES - 3, 2)
# Setup background colors
main_window.bkgd(" ", curses.color_pair(1))
screen.bkgd(" ", curses.color_pair(2))
footer.bkgd(" ", curses.color_pair(2))
# Draw borders around the subwindows
screen.box()
footer.box()
footer.addstr(1, 1, "Exit", curses.color_pair(1) | curses.A_BOLD)
for message in message_list:
if current_row < curses.LINES - 7:
# Truncate the string, if needed
display_str = message[:curses.COLS - 8]
screen.addstr(current_row, 3, display_str)
current_row += 1
else:
break
# Draw the pieces of the overall screen (order matters)
stdscr.noutrefresh()
main_window.noutrefresh()
screen.noutrefresh()
curses.doupdate()
while True:
# Get a character from the keyboard
key = stdscr.getch()
# The user can exit via the enter key
if key == ord("\n"):
return True