def addstr(self, xxx_todo_changeme6, string, color=None): (y, x) = xxx_todo_changeme6 string = string.encode('ascii','ignore') try: if color: meta = curses.color_pair(color) self.__pad.addstr(y, x, string, meta) else: self.__pad.addstr(y, x, string) except curses.error as e: if y >= self.__h: raise curses.error('%s: %s' % (e.message, 'Y bounds exceeded')) if x + len(string) >= self.__w: raise curses.error('%s: %s' % (e.message, 'X bounds exceeded'))
def tparm(*args): raise curses.error("unexpected error in tparm()")
def tparm(*args): raise curses.error("tparm() returned NULL")
def example(window): t0 = time() tot = 0 mid, mx, my, mz, mbstate = 0, 0, 0, 0, 0 ymax, xmax = window.getmaxyx() # Win1 Drawing text once win1 = window.subwin(0, 0) try: win1.addch(ymax-10, 24, curses.ACS_LARROW) except: win1.move(0, 0) win1.hline(ymax-10, 25, curses.ACS_HLINE, 10) win1.addstr(ymax-10, 36, "click this box, type, or press H, or Q") win1.vline(ymax-9, 54, curses.ACS_VLINE, 2) win1.addch(ymax-7, 54, curses.ACS_DARROW) # Window 2 win2 = curses.newwin(12, 21, ymax-13, 1) win2.bkgd(' ', curses.A_BOLD | curses.A_REVERSE) win2_inv = True win2.box() win2.keypad(1) win2.nodelay(1) win2.border() win2.leaveok(0) win2.scrollok(0) # Window 3 win3 = curses.newwin(3, 21, ymax-6, 36) win3.bkgd(' ', curses.A_BOLD) win3.box() win3.keypad(1) win3.nodelay(1) win3.border() win3.leaveok(0) win3.scrollok(0) win3_s = '' # Mainloop while True: t1 = time() tot += 1 ups = tot/(t1-t0) x = randint(1, randint(1, xmax - 2)) y = randint(1, randint(1, ymax - 2 - 12)) c = ord('#') # window.erase() # IF YOU WANT TO REDRAW DON'T USE CLEAR! # Input getch = window.getch() if getch == curses.KEY_MOUSE or getch == ord('H'): tmid, tmx, tmy, tmz, tmbstate = curses.getmouse() if getch == ord('H') or tmbstate == 2 and win2.enclose(tmy, tmx): mid, mx, my, mz, mbstate = tmid, tmx, tmy, tmz, tmbstate if win2_inv: win2.bkgdset(' ', curses.A_BOLD) else: win2.bkgdset(' ', curses.A_BOLD | curses.A_REVERSE) win2_inv = not win2_inv elif getch == ord('Q'): raise SystemExit(None) # Window 1 try: win1.addch(y, x, c) # Totally unbuffered print except: win1.move(0, 0) win1.noutrefresh() # Mark for update # Window 2 ylst, xlst = curses.getsyx() win2.addstr(12-11, 1, "tot :%12i" % tot) win2.addstr(12-10, 1, "#/s :%12.3f" % ups) win2.addstr(12-9, 1, "t tot :%12.3f" % (t1-t0)) win2.addstr(12-8, 1, "y lst :%12i" % ylst) win2.addstr(12-7, 1, "x lst :%12i" % xlst) win2.addstr(12-6, 1, "m id :%12i" % mid) win2.addstr(12-5, 1, "m x :%12i" % mx) win2.addstr(12-4, 1, "m y :%12i" % my) win2.addstr(12-3, 1, "m z :%12i" % mz) win2.addstr(12-2, 1, "m st :%12i" % mbstate) win2.noutrefresh() # Window 3 ylst, xlst = curses.getsyx() if getch >= 0x20 and getch < 0x7f and chr(getch) not in uppercase: try: win3_s += chr(getch) win3.addstr(1, 1, win3_s) if len(win3_s) > 18: raise curses.error('String too long') except: win3_s = chr(getch) win3.move(0, 0) win3.noutrefresh() sleep(MIN_SLEEP) curses.doupdate() # Perform refreshes
def main(stdscr): """Draw a simple TUI, grab keypresses and let the user manipulate the DSP parameters. """ stdscr.clear() stdscr.nodelay(True) # Create small visual display stdscr.addstr( "======================\n" "Generate Tone Example.\n" "======================\n" "\n" "Press 1 to play a sine wave\n" "Press 2 to play a sqaure wave\n" "Press 3 to play a saw wave\n" "Press 4 to play a triangle wave\n" "Press SPACE to stop the channel\n" "Press q to quit\n" "Press k and j to change volume\n" "Press h and l to change frequency" ) channel = None while True: if channel: playing = "playing" if channel.is_playing else "stopped" volume = channel.volume frequency = channel.frequency else: playing = "stopped" volume = 0 frequency = 0 stdscr.move(13, 0) stdscr.clrtoeol() stdscr.addstr(f"Channel is {playing}") stdscr.move(14, 0) stdscr.clrtoeol() stdscr.addstr(f"Volume {volume:.2f}") stdscr.move(15, 0) stdscr.clrtoeol() stdscr.addstr(f"Frequency {frequency}") # Listen to the user try: keypress = stdscr.getkey() if keypress == "1": if channel: channel.stop() channel = system.play_dsp(dsp, paused=True) channel.volume = 0.5 dsp.set_parameter_int(DSP_OSCILLATOR.TYPE, 0) channel.paused = False elif keypress == "2": if channel: channel.stop() channel = system.play_dsp(dsp, paused=True) channel.volume = 0.125 dsp.set_parameter_int(DSP_OSCILLATOR.TYPE, 1) channel.paused = False elif keypress == "3": if channel: channel.stop() channel = system.play_dsp(dsp, paused=True) channel.volume = 0.125 dsp.set_parameter_int(DSP_OSCILLATOR.TYPE, 2) channel.paused = False elif keypress == "4": if channel: channel.stop() channel = system.play_dsp(dsp, paused=True) channel.volume = 0.5 dsp.set_parameter_int(DSP_OSCILLATOR.TYPE, 4) channel.paused = False elif keypress == " ": if channel: channel.stop() channel = None elif keypress == "q": break if not channel: raise curses.error("no input") if keypress == "h": channel.frequency = max(channel.frequency - 500, 0) elif keypress == "j": channel.volume = max(channel.volume - 0.1, 0) elif keypress == "k": channel.volume = min(channel.volume + 0.1, 1) elif keypress == "l": channel.frequency = channel.frequency + 500 except curses.error as cerr: if cerr.args[0] != "no input": raise cerr system.update() time.sleep(50 / 1000)
def curses_error(): raise curses.error()
def setup(self, stdscr): """ Draw the menu and handle keyboard input. Parameters: stdscr (WindowObject): the screen; handled by curses.wrapper Returns: the string representing the currently selected row """ # Initialize the WindowObject stdscr.clear() stdscr = curses.initscr() curses.noecho() curses.cbreak() # Start color support if supported if curses.has_colors(): curses.start_color() # Initialize color pairs # Main window curses.init_pair(1, curses.COLOR_WHITE, curses.COLOR_BLUE) # Sub-windows curses.init_pair(2, curses.COLOR_BLACK, curses.COLOR_WHITE) # Verify this is a terminal that is at least both 80 columns and 24 lines if curses.LINES < 24 or curses.COLS < 80: raise curses.error( "Required minimum terminal dimensions of 80x24 > {}x{}.". format(curses.COLS, curses.LINES)) main_window, screen, footer = self.draw_menu(stdscr) # Menu loop until item is chosen with the <enter> key or the user quits with "q" while True: # Get a character from the keyboard key = stdscr.getch() # Enter selects the highlighted item if key == ord("\n"): footer_selection = self.footer_items[self.current_column] if footer_selection == "Select": if isinstance(self._items[self.current_row - 1], (ec2rlcore.menu_item.ExitItem, ec2rlcore.menu_item.RunItem)): self._items[self.current_row - 1]() self.current_row = 1 self.current_column = 0 self.current_page = 1 self.done = True return else: self.current_column = 0 return self._items[self.current_row - 1]() elif footer_selection == "Exit": self.current_row = 1 self.current_column = 0 self.current_page = 1 self.done = True return elif footer_selection == "Help": self.current_column = 0 return self.show_item_help(self._items[self.current_row - 1]) elif footer_selection == "Clear": if isinstance(self._items[self.current_row - 1], ec2rlcore.menu_item.TextEntryItem): self._items[self.current_row - 1].row_right = "" else: raise MenuUnsupportedFooterOptionError(footer_selection) elif key == ord(" "): if isinstance(self._items[self.current_row - 1], ec2rlcore.menu_item.ToggleItem): self._items[self.current_row - 1]() elif key in (78, "N"): # Select/deselect all ToggleItems in current menu for item in self._items: if isinstance(item, ec2rlcore.menu_item.ToggleItem) \ and ((self.toggle_state and item.toggled) or (not self.toggle_state and not item.toggled)): item() self.toggle_state = not self.toggle_state elif key in (260, curses.KEY_LEFT): if self.current_column > 0: self.current_column -= 1 # The right arrow key selects the footer option to the right of the currently selected item elif key in (261, curses.KEY_RIGHT): if self.current_column < self.num_columns - 1: self.current_column += 1 # The down arrow key selects the next option in the menu window and increments the page as needed elif key in (65, 258, curses.KEY_DOWN): if self.current_page == 1: if self.current_row < self.max_displayed_rows and \ self.current_row < self.num_rows: self.current_row += 1 # Else + if num_pages > 1 elif self.num_pages > 1: self.current_page += 1 self.current_row = 1 + (self.max_displayed_rows * (self.current_page - 1)) elif self.current_page == self.num_pages: if self.current_row < self.num_rows: self.current_row += 1 else: if self.current_row < self.max_displayed_rows + \ (self.max_displayed_rows * (self.current_page - 1)): self.current_row += 1 else: self.current_page += 1 self.current_row = 1 + (self.max_displayed_rows * (self.current_page - 1)) # The up arrow key selects the previous option in the menu window and decrements the page as needed elif key in (66, 259, curses.KEY_UP): if self.current_page == 1: if self.current_row > 1: self.current_row -= 1 else: if self.current_row > (1 + (self.max_displayed_rows * (self.current_page - 1))): self.current_row -= 1 else: self.current_page -= 1 self.current_row = \ self.max_displayed_rows + (self.max_displayed_rows * (self.current_page - 1)) # The page down key increments the page and selects the row in the same position in the menu on that page # or the first row if the next page doesn't have an equivalent row # Ignore page down key presses on the last page elif key in (338, curses.KEY_NPAGE ) and self.current_page < self.num_pages: self.current_page += 1 # Bounds check to ensure there are enough items on the next page to select an item in the same position # This will be true for all pages but the last page which will likely be a partial page of items if self.current_row + self.max_displayed_rows < self.num_rows: self.current_row += self.max_displayed_rows # If the bounds check fails then select the first item on the next (last) page else: self.current_row = ( (self.current_page - 1) * self.max_displayed_rows) + 1 # The page up key decrements the page and selects the row in the same position in the menu on that page # Ignore page up key presses when on the first page elif key in (339, curses.KEY_PPAGE) and self.current_page > 1: self.current_page -= 1 # Shift the currently selected row to the same place on the new page self.current_row -= self.max_displayed_rows elif key in (410, curses.KEY_RESIZE): # Get the new terminal dimensions and resize the terminal curses.resizeterm(*stdscr.getmaxyx()) # Verify the terminal is still at least 80x24 if curses.LINES < 24 or curses.COLS < 80: raise curses.error( "Required minimum terminal dimensions of 80x24 > {}x{}." .format(curses.COLS, curses.LINES)) # Initialize a new WindowObject stdscr = curses.initscr() curses.noecho() curses.cbreak() main_window, screen, footer = self.draw_menu(stdscr) # Recalculate the current page # current_selected_row - 1 is needed because the row in the window is offset by 1 self.current_page = int( math.ceil(self.current_row / self.max_displayed_rows)) # For some reason, another getch() call is required to pull the KEY_RESIZE out of the buffer stdscr.getch() # Erase the screen so it can be cleanly redrawn screen.erase() screen.border(0) self._draw_menu(screen) # Draw the navigation items in the footer self._draw_footer(footer) # Draw the pieces of the overall screen (order matters) stdscr.noutrefresh() main_window.noutrefresh() screen.noutrefresh() footer.noutrefresh() curses.doupdate()