Curses, Blurses. A simple framework to make Curses-based applications for the terminal. Features:
- Relative positioning by percentages
- Vertical and horizontal centering
- Word wrapping
- Automatic menu generation
- Modular application structure
To use Blurses, simply clone the repo, and place a copy of the blurses
directory in your project's directory. Import using:
from blurses import Blurses
An example application is included. Run the application for instructions, or view its source code for reference.
python example.py
or
python3 example.py
A Blurses application is very simple. It consists of functions that contain application behavior and also correspond to menu options that are automatically generated by Blurses. This is done by binding the functions to Blurses. For example, consider an imaginary application with three functions: list, sort, and write. First we will define our functions, including an additional menu function:
def main_menu(window, menu, state):
# Your code here
return menu_selection, None
def list_files(window, menu, state):
# Your code here
return 0, None
def sort_files(window, menu, state):
# Your code here
return 0, None
def write_files(window, menu, state):
# Your code here
return 0, None
Note that Blurses functions must be created with window, menu and state arguments. These are objects injected by blurses that provide access to menu selection and drawing functionality. State additionally allows you to persist data between functions. Blurses functions must also return an int, along with state for the next function. This int corresponds to the next function to execute. For example, all of the previous functions return to the main menu, except for the main menu function, which chooses a different function to return to. State can be as simple as None, or a complex data type of your choosing. It depends on how much data your application needs to persist between functions.
Then simply create a blurses object and bind your functions:
blurses = Blurses()
blurses.bind('', main_menu)
blurses.bind('List', list_files)
blurses.bind('Sort', sort_files)
blurses.bind('Write', write_files)
The first argument is the display name for the menu, the second is the function to bind. The display name for the first supplied function is ignored. Set it to anything. This is because the first function is always assumed to be the main menu, which is not displayed on the menu.
Binding functions automatically generates the menu object supplied to the functions, which can be rendered using the window.draw()
method.
To run, simply:
blurses.run()
The window object available in all Blurses functions has two draw methods:
- window.draw() This method accepts a list of strings to be drawn. Any element of the list can instead be a tuple consisting of the string and a render mode. for example, both of the following lists would be valid:
list1 = [
'Word1',
'Word2'
]
list2= [
'Word4',
('Sentence number ONE', curses.A_REVERSE),
'Word5',
'Sentence number TWO'
]
This is because the draw method automatically applies the render mode curses.A_NORMAL
to all strings without a specified mode. To use different render modes, you will have to first import curses:
import curses
To draw the menu, supply it to the draw function as a callable.
window.draw(menu(), bottom=5, left=5)
- window.wrap() This method accepts a string, or a tuple consisting of string and render mode. This mode draws a string that is word wrapped to fit the terminal window. Optionally supply a width argument to make the string to wrap to a percentage of terminal window width. Example:
window.wrap(my_string, width=90, left=5)
Both window.draw()
and window.wrap()
feature optional positioning arguments:
- Horizontal Positioning:
- left: % from the left edge of the terminal window.
- right: % from the right edge of the terminal window.
- hcenter: horizontal centering
- Vertical Positioning:
- top: % from the top of the terminal window.
- bottom: % from the bottom of the terminal window.
- vcenter: vertical centering
Examples:
window.wrap(my_string, width=90, left=5, vcenter=True)
window.draw(logo, top=5, right=5)
window.draw(menu(), hcenter=True, bottom=2)
Curses sends characters when the terminal window is resized. For Blurses to be responsive, drawing methods must be executed within update loops that wait for key entry. The window has three methods for input:
window.getch()
window.getkey()
window.get_wch()
Only getch() has been tested.
Before drawing within the update loop you need to window.clear()
drawn elements and window.update()
the window. This allows for repsonsive positioning. Here is a simple update loop:
key = 0
while key != 10:
window.clear()
window.update()
window.draw(greeting, top=2, hcenter=True)
key = window.getch()
To get output from the menu, we will pass our window.getch()
output to the menu.input()
method. Here is an example of an update loop that renders a functional menu and uses menu output:
menu_selection = 0
while menu_selection == 0:
window.clear()
window.update()
window.draw(title, top=0, left=2)
window.wrap(description, width=80, vcenter=True, left=10)
window.draw(menu(), bottom=5, left=5)
mode = menu.input(window.getch())
return mode, None
The return mode
statement is not part of the loop. If terminating the function this selects the next function for Blurses to execute.
The window object has methods to pause and resume the Blurses (and Curses) environment. To return to a normal terminal environment try the following:
window.pause()
user_input = input('Enter input')
# Do something with input
window.resume()