import curses import time ############################################ # 1. how to start curses # ############################################ if False: # some dummy code... # makes terminal go into curses mode, # `stdscr` is a curses window. stdscr = curses.initscr() # makes text not be echoed in the window curses.noecho() # makes curses respond to character input # immediately (ie. don't have to press ENTER) curses.cbreak() curses.nocbreak() ### also there is stuff that has to do with ### restarting the terminal in non-curses mode ### after the program is finished (because the ### terminal will be all messed-up otherwise). ### ### but... we don't have to worry about any of ### that because we are using `curses.wrapper` ############################################ # 2. a curses program # ############################################ # this function definition contains an actual curses program. def the_program(stdscr): """ intended to be used as Callable argument to curses.wrapper() """ curses.noecho() curses.cbreak() # making the cursor invisible CURSOR_VISIBILITY = { "invisible": 0, "visibile": 1, "very_visible": 2 } curses.curs_set(CURSOR_VISIBILITY["invisible"]) # not that most of the curses attributes are globals: # when we set them, they will stay that way until # we change them to something else. # means that the window will be erased # and re-drawn the next time that `stdscr.refresh()` # is called. stdscr.clear() # placing a string at the top-left corner of # the window. #y #x #the str stdscr.addstr(0, 0, "hi bud") # actually printing this screen stdscr.refresh() # getting the dimensions of the window. max_y, max_x = stdscr.getmaxyx() # adding a string listing the dimensions # to the center of the window xy_str = "current window is {} chars wide"\ "and {} chars tall.".format(max_x, max_y) #floor division! offset_x = (max_x - len(xy_str)) // 2 offset_y = max_y // 2 #y #x #the str stdscr.addstr(offset_y, offset_x, xy_str) ## printing "#" at the corner of the window OPPOSITE to (0,0) stdscr.addstr(max_y - 1, max_x - 2, "#") ################################################################# ### interesting fact: i don't know why, but curses will throw an ### error if we tried to print a character at `max_x - 1` ### (probably 79, for a standard 80x24 char terminal). ### ### it does this, even though we made the cursor invisible. ################################################################# # actually printing the updates stdscr.refresh() ################################################################# # # P ### ### ### # A ### ### ### # R ### ### ### # T ### ### ### # ################################################################# # now we will take some user input. # first, print another message so the user # knows what to do. # it will be printed at the bottom of the screen, # like the message line in GNU emacs. exit_msg = "press any key to exit" stdscr.addstr( max_y - 1, # y (max_x - len(exit_msg)) // 2, # x exit_msg # string ) stdscr.refresh() # the program will block here, until a button is pressed. character = stdscr.getch() # PLAYING A TRICK ON THE USER!!! # # erasing the screen stdscr.clear() # show the user input curses.echo() trick_msg = "please enter the name of your operating system" trick_msg_pos = { "y": max_y // 2, "x": (max_x - len(trick_msg)) // 2 } stdscr.addstr( trick_msg_pos["y"], trick_msg_pos["x"], trick_msg ) # now we will use `stdscr.getstr` to get a # "line-delimited" string, and put the # cursor is a visually pleasing central position # first we will make the cursor nice and visible again curses.curs_set(CURSOR_VISIBILITY["very_visible"]) user_os_bytes = stdscr.getstr( # `y` and `x` position cursor trick_msg_pos["y"] + 2, # 2 lines below the message trick_msg_pos["x"], # same justification as message ) ################################################################# ### HOW TO BYTES->STRING ######################################## ### for some reason, the string is read as bytes. ### we can fix this easily by using utf8 encoding. ################################################################# user_os = user_os_bytes.decode("utf-8") # updating stdscr.refresh() # erase again stdscr.clear() curses.noecho() you_said_msg = "you said: {}".format(user_os) you_said_msg_pos = { "y": max_y // 2, "x": (max_x - len(you_said_msg)) // 2 } stdscr.addstr( you_said_msg_pos["y"], you_said_msg_pos["x"], you_said_msg ) stdscr.refresh() # NOW the CLOWNING begins... # suspend execution for comedic timing time.sleep(1.5) ms_msg_1 = "MICRO$UX WINBLOWZ" ms_msg_2 = " detected" total_len = len(ms_msg_1) + len(ms_msg_2) ms_msg_x = (max_x - total_len) // 2 stdscr.addstr( you_said_msg_pos["y"] + 2, ms_msg_x, ms_msg_1, # `addstr` has a fourth, optional parameter that accepts a # SINGLE attribute. curses.A_BOLD, # this text will be bold ) # this part of the message will immediately # follow the first part, on the same line. stdscr.addstr( you_said_msg_pos["y"] + 2, ms_msg_x + len(ms_msg_1), ms_msg_2, curses.A_ITALIC # also italic ) # make cursor invisible again curses.curs_set(CURSOR_VISIBILITY["invisible"]) stdscr.refresh() # more timing time.sleep(1) update_msg = "press any key to do windoze updates" stdscr.addstr( you_said_msg_pos["y"] + 4, (max_x - len(update_msg)) // 2, update_msg ) stdscr.getch() for i in range(100): stdscr.clear() progress_msg = "your windoze are {}% updated".format(i) doing_msg = "doing WINDOZE UPDATES" stdscr.addstr( max_y // 2, (max_x - len(progress_msg)) // 2, progress_msg, curses.A_BOLD ) stdscr.addstr( (max_y // 2) - 2, (max_x - len(doing_msg)) // 2, doing_msg, curses.A_BLINK # it blinks! ) stdscr.refresh() # make it nice and slow :] time.sleep(2) # we actually run the program in our # `__name__ == ...` block if __name__ == "__main__": curses.wrapper(the_program) #Attribute Meanings # A_ALTCHARSET # Alternate character set mode # A_BLINK # # # Blink mode # # A_BOLD # # # Bold mode # # A_DIM # # # Dim mode # # A_INVIS # # # Invisible or blank mode # # A_ITALIC # # # Italic mode # # A_NORMAL # # # Normal attribute # # A_PROTECT # # # Protected mode # # A_REVERSE # # # Reverse background and foreground colors # # A_STANDOUT # # # Standout mode # # A_UNDERLINE # # # Underline mode # # A_HORIZONTAL # # # Horizontal highlight # # A_LEFT # # # Left highlight # # A_LOW # # # Low highlight # # A_RIGHT # # # Right highlight # # A_TOP # # # Top highlight # # A_VERTICAL # # # Vertical highlight # # A_CHARTEXT # # # Bit-mask to extract a character