我们从Python开源项目中,提取了以下32个代码示例,用于说明如何使用curses.LINES。
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)
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 full_redraw(self): """Perform a full redraw of the UI.""" self.left.clear() self.right.clear() self.top.clear() self.bottom.clear() self.left.box() self.right.box() self.top.addstr(" toot - your Mastodon command line interface\n", Color.yellow()) self.top.addstr(" https://github.com/ihabunek/toot") self.draw_statuses(self.left) self.draw_status_details(self.right, self.get_selected_status()) self.draw_usage(self.bottom) self.left.refresh(0, 0, 2, 0, curses.LINES - 4, self.left_width) self.right.refresh() self.top.refresh() self.bottom.refresh()
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 max_displayed_rows(self): # Return how many items can be displayed in the primary subwindow for displaying the dict items return curses.LINES - 9
def get_input(self): if not self.debug or self.compat_debug: if not sys.stdin.isatty(): return input('?: ') else: x = input('?: ') return x else: return self.curses_input(self.stdscr, curses.LINES - 3, 2, '?: ')
def saywhat(text): stdscr.move(curses.LINES - 1, 0) stdscr.clrtoeol() stdscr.addstr(text)
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_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 refresh_values(): """ Refresh the parameters when the size of list changes in display """ if not GUI.gui_stopped: GUI.max_row = curses.LINES - 3 GUI.row_num = len(GUI.strings) GUI.pages = int(ceil(GUI.row_num / GUI.max_row))
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 download_item(): """ It downloads the song from the url and saves it in the file. """ if GUI.run_download or GUI.gui_stopped: return GUI.run_download = True filename = GUI.strings[GUI.position - 1] url = GUI.url_dict[filename] # User agent is specified to prevent some websites from blocking the software req = request.Request(url, headers={'User-Agent': 'Mozilla/5.0'}) u = request.urlopen(req) f = open(filename, 'wb') file_size_dl = 0 block_sz = 8192 while GUI.run_download: buffer = u.read(block_sz) if not buffer: break file_size_dl += len(buffer) f.write(buffer) # The percentage downloaded status = "Downloading " + filename + " [%3.2f%%]" % (file_size_dl * 100. / GUI.size_dict[filename]) GUI.box.erase() GUI.box.addstr(1, 1, status, GUI.high_light_text) GUI.box.addstr(2, 1, 'Size: %.2fMB' % (GUI.size_dict[filename]/1024/1024)) # Cancel button to cancel the download GUI.box.addstr(curses.LINES - 1, 0, "C:Cancel Download", GUI.high_light_text) GUI.screen.refresh() GUI.box.refresh() f.close() GUI.run_download = False GUI.key = curses.KEY_DOWN GUI.update_screen()
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 draw_status_row(self, window, status, offset, highlight=False): width = window.getmaxyx()[1] color = Color.blue() if highlight else 0 date, time = status['created_at'] window.addstr(offset + 2, 2, date, color) window.addstr(offset + 3, 2, time, color) window.addstr(offset + 2, 15, status['account']['acct'], color) window.addstr(offset + 3, 15, status['account']['display_name'], color) window.addstr(offset + 4, 1, '?' * (width - 2)) window.refresh(0, 0, 2, 0, curses.LINES - 4, self.left_width)
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 get_input(self, ascii_char=False): """Get an input from the user.""" if not self.debug or self.compat_debug: if ascii_char: return getch() else: if not sys.stdin.isatty(): return input('') else: return input('?: ') else: return self.curses_input(self.stdscr, curses.LINES - 3, 2, '?: ', ascii_char)
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 __init__(self, awesome_title, awesome_blocks): """ Initialize the screen window Args awesome_title: Awesome topic title awesome_blocks: A list of formatted awesome content. It has a set of names and web links for crawled awesome content. Attributes window: A full curses screen window result_window: A window for showing the results search_window: A window for search bar y: Current y coordinates width: The width of `window` height: The height of `window` awesome_title: Awesome title awesome_blocks: It holds given `awesome_blocks` argument matched_blocks: A list of found awesome content max_lines: Maximum visible line count for `result_window` top: Available top line position for current page (used on scrolling) bottom: Available bottom line position for whole pages (as length of found lines) current: Current highlighted line number query: Query string for searching the awesome content Returns None """ self.window = None self.result_window = None self.search_window = None self.y = 0 self.width = 0 self.height = 0 self.awesome_title = awesome_title self.awesome_blocks = awesome_blocks self.matched_blocks = awesome_blocks self.init_curses() self.init_layout() self.max_lines = curses.LINES - 4 self.top = 0 self.bottom = self.max_lines self.current = 0 self.run()
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
def c_main(stdscr): cargo_cult_routine(stdscr) stdscr.nodelay(0) mydir = factory(start) mydir.expand() curidx = 3 pending_action = None pending_save = False while True: stdscr.clear() curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) line = 0 offset = max(0, curidx - curses.LINES + 3) for data, depth in mydir.traverse(): if line == curidx: stdscr.attrset(curses.color_pair(1) | curses.A_BOLD) if pending_action: getattr(data, pending_action)() pending_action = None elif pending_save: global result result = data.name return else: stdscr.attrset(curses.color_pair(0)) if 0 <= line - offset < curses.LINES - 1: stdscr.addstr(line - offset, 0, data.render(depth, curses.COLS)) line += 1 stdscr.refresh() ch = stdscr.getch() if ch == curses.KEY_UP: curidx -= 1 elif ch == curses.KEY_DOWN: curidx += 1 elif ch == curses.KEY_PPAGE: curidx -= curses.LINES if curidx < 0: curidx = 0 elif ch == curses.KEY_NPAGE: curidx += curses.LINES if curidx >= line: curidx = line - 1 elif ch == curses.KEY_RIGHT: pending_action = 'expand' elif ch == curses.KEY_LEFT: pending_action = 'collapse' elif ch == ESC: return elif ch == ord('\n'): pending_save = True curidx %= line ################################################################################
def update(self, browse, head, quote, position, incorrect, author, title, typed, wpm, average): cols = curses.COLS lengths = word_wrap(quote, cols - 1) sx, sy = screen_coords(lengths, position) h = len(lengths) # Show header self.window.addstr(0, 0, head + " "*(cols - len(head)), curses.color_pair(2)) if browse: # Display quote color = curses.color_pair(4 if browse == 1 else 3) for y, length in enumerate(lengths, 2): self.window.addstr(y, 0, quote[:length], color) quote = quote[1+length:] # Show author credit = u"— %s, %s" % (author, title) self.cheight = 4 + h + self.column(3+h, cols - 10, cols//2, credit, curses.color_pair(6), False) if browse >= 2: typed = "You scored %.1f wpm%s " % (wpm, "!" if wpm > average else ".") else: typed = "" typed += "Use arrows/space to browse, esc to quit, or start typing." elif position < len(quote): color = curses.color_pair(3 if incorrect == 0 else 1) typed = "> " + typed if position + incorrect < len(quote): sx, sy = screen_coords(lengths, position + incorrect - 1) self.window.chgat(2 + sy, max(sx, 0), 1, color) sx, sy = screen_coords(lengths, position + incorrect + 1) self.window.chgat(2 + sy, sx, curses.color_pair(4)) # Show typed text if self.cheight < curses.LINES: self.window.move(self.cheight, 0) self.window.clrtoeol() self.window.addstr(self.cheight, 0, typed, curses.color_pair(7)) if browse > 1: # If done, highlight score self.window.chgat(self.cheight, 11, len(str("%.1f" % wpm)), curses.color_pair(9)) # Move cursor to current position in text before refreshing if browse < 1: sx, sy = screen_coords(lengths, position + incorrect) self.window.move(2 + sy, min(sx, cols - 1)) else: self.window.move(2, 0) self.window.refresh()
def main(): """ The entry point for the app. Called when music-scraper is typed in terminal. Starts the GUI and starts the scraping process after the input is given """ curses.initscr() if curses.COLS < 80 or curses.LINES < 5: curses.endwin() print('Terminal\'s dimensions are too small') return process = CrawlerProcess({'LOG_ENABLED': False}) def gui_input(screen): GUI.screen = screen 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 GUI.box = curses.newwin(curses.LINES, curses.COLS, 0, 0) GUI.message = GUI.get_input() curses.wrapper(gui_input) s = request.quote(GUI.message) MusicSpider.start_urls = [ "http://www.google.com/search?q=" + s, ] process.crawl(MusicSpider) thread = GUIThread(process, start_gui) thread.start() process.start() if not GUI.gui_stopped: if len(GUI.strings) == 0: GUI.box.erase() GUI.box.addstr(1, 1, "No Results Found... Try with Some other keywords.", GUI.high_light_text) GUI.add_bottom_menus() GUI.screen.refresh() GUI.box.refresh() else: GUI.box.addstr(curses.LINES - 2, 1, "Completed Scraping !!", GUI.high_light_text) GUI.add_bottom_menus() GUI.screen.refresh() GUI.box.refresh()
def main(stdscr): config_file = args.config_file if args.config_file is not None else 'zmqchat.cfg' config = configparser.ConfigParser() config.read(config_file) config = config['default'] receiver = zmq.Context().instance().socket(zmq.PAIR) receiver.bind("inproc://clientchat") sender = zmq.Context().instance().socket(zmq.PAIR) sender.connect("inproc://clientchat") client = ClientChat(args.username, config['server_host'], config['chat_port'], receiver) client.run() display_receiver = zmq.Context().instance().socket(zmq.PAIR) display_receiver.bind("inproc://clientdisplay") display_sender = zmq.Context().instance().socket(zmq.PAIR) display_sender.connect("inproc://clientdisplay") display = ClientDisplay(config['server_host'], config['display_port'], display_sender) display.run() ### curses set up curses.init_pair(1, curses.COLOR_BLACK, curses.COLOR_WHITE) curses.init_pair(2, curses.COLOR_WHITE, curses.COLOR_BLACK) # ensure that user input is echoed to the screen curses.echo() curses.curs_set(0) window_height = curses.LINES window_width = curses.COLS division_line = int(window_height * 0.8) # instaniate two pads - one for displaying received messages # and one for showing the message the user is about to send off top_pad = stdscr.subpad(division_line, window_width, 0, 0) bottom_pad = stdscr.subpad(window_height - division_line, window_width, division_line, 0) top_thread = threading.Thread(target=start_top_window, args=(top_pad, display_receiver)) top_thread.daemon = True top_thread.start() bottom_thread = threading.Thread(target=start_bottom_window, args=(bottom_pad, sender)) bottom_thread.daemon = True bottom_thread.start() top_thread.join() bottom_thread.join()