Location
If you just want to move the location of the cursor before writing text, and aren’t worried about returning, do something like this:
>>> print(term.home + term.clear, end='')
>>> print(term.move_down(2) + term.move_right(20) + term.bright_red('fire!'), end='')
>>> print(term.move_xy(20, 7) + term.bold('Direct hit!'), end='')
>>> print(term.move_y(term.height - 3), end='')
Note our use of end='' to the built-in print() function, this is
because the default end='\n' value causes the cursor to move to the first
column of the next row.
There are four direct movement capabilities:
move_xy(x, y)Position cursor at given x, y.
move_x(x)Position cursor at column x.
move_y(y)Position cursor at row y.
homePosition cursor at (0, 0).
And four relative capabilities:
move_upormove_up(y)Position cursor 1 or y row cells above the current position.
move_down(y)Position cursor 1 or y row cells below the current position.
Note
move_down()(no arguments) is often valued as\\n, which additionally returns the carriage to column 0 in the default terminal mode, (when not inTerminal.raworTerminal.cbreak), and, depending on your terminal emulator, may also destroy remaining characters at current location to the end of the line.move_down(1)is always a safe, non-destructive, one-notch movement in the downward direction and should be preferred.move_leftormove_left(x)Position cursor 1 or x column cells left of the current position.
move_rightormove_right(x)Position cursor 1 or x column cells right of the current position.
Example
The source code of bounce.py combines a small bit of Keyboard Input input with many of the
Terminal location capabilities, home, width, height, and move_xy are used to create
a classic game of tennis:
# std imports
from math import floor
# local
from blessed import Terminal
def roundxy(x, y):
return int(floor(x)), int(floor(y))
term = Terminal()
x, y, xs, ys = 2, 2, 0.4, 0.3
with term.cbreak(), term.hidden_cursor():
# clear the screen
print(term.home + term.black_on_olivedrab4 + term.clear)
# loop every 20ms
while term.inkey(timeout=0.02) != 'q':
# erase,
txt_erase = term.move_xy(*roundxy(x, y)) + ' '
# bounce,
if x >= (term.width - 1) or x <= 0:
xs *= -1
if y >= term.height or y <= 0:
ys *= -1
# move,
x, y = x + xs, y + ys
# draw !
txt_ball = term.move_xy(*roundxy(x, y)) + '█'
print(txt_erase + txt_ball, end='', flush=True)
Context Manager
A contextlib.contextmanager(), location() is provided to move the cursor to
an (x, y) screen position and restore the previous position on exit:
with term.location(0, term.height - 1):
print('Here is the bottom.')
print('This is back where I came from.')
All parameters to location() are optional, we can use
it without any arguments at all to restore the cursor location:
with term.location():
print(term.move_xy(1, 1) + 'Hi Mom!' + term.clear_eol)
Note
calls to location() may not be nested.
Finding The Cursor
We can determine the cursor’s current position at anytime using
get_location(). The default timeout value of 1 second will return
coordinates (-1, -1) if no response was received in time.
This uses a kind of “answer back” sequence that all terminal emulators automatically respond to.
>>> term.get_location()
(32, 0)
The return value of get_location() mirrors the arguments of
location():
with term.location(12, 12):
val = term.get_location()
print(val)
Produces output, (12, 12)
Although this wouldn’t be suggested in most applications because of its latency, it certainly simplifies many applications, and, can also be timed, to make a determination of the round-trip time, perhaps even the bandwidth constraints, of a remote terminal!
CPR_RESPONSE
The get_location() method is blocking, sending a cursor position report sequence
and awaiting the response. It may sometimes be necessary send and capture Cursor Position Report
responses asynchronously as keyboard input events.
Print Cursor Position Report sequence,
term.u7 or '\x1b[6n'Call
inkey()withcapture_cpr=TrueWhen return
nameisCPR_RESPONSE, use thecpr_yx()orcpr_xy()methods to receive the coordinates.
Although inkey() is capable of receiving most Cursor Position Report (CPR)
sequences as Keystroke of name CPR_RESPONSE, Some coordinate responses are in conflict with
the “F3” key of the DEC vt220, but few very terminals transmit them in this conflicting form. If not
bound or legacy vt220 key compatibility is not required, but asynchronous receipt of CPR Responses
with correct coordinates are, use inkey() argument capture_cpr=True.
In the following example, keyboard input is rapidly polled for and displayed, CPR Sequences are sent at a fixed interval, and the response or round-trip time is displayed.
"""Example of Terminal.inkey(capture_cpr=True)."""
import blessed
import time
term = blessed.Terminal()
def msg(term, txt):
print(term.move_yx(term.height // 2, 0) + term.center(txt))
with term.cbreak(), term.fullscreen():
print(term.home + term.clear)
interval = 0.5
cpr_last_sent = time.time() - interval
while True:
stime = time.time()
if stime - cpr_last_sent > interval:
print(term.u7 or '\x1b[6n', flush=True, end='')
cpr_last_sent = time.time()
inp = term.inkey(timeout=0.1, capture_cpr=True)
print(inp)
if inp.name == 'CPR_RESPONSE':
elapsed_ms = (time.time() - cpr_last_sent) * 1000
msg(term, f'CPR {elapsed_ms:3.2f}ms rtt; yx={inp.cpr_yx}')
elif inp:
msg(term, f"Keystroke: {inp!r} !")
Scroll Region
A scroll region restricts scrolling to a portion of the screen. When text scrolls within the region, content outside the region remains fixed. This is the mechanism behind interfaces like less(1), or irc chat clients, where new messages scroll while an input area stays in place.
A contextlib.contextmanager(), scroll_region() is provided to set a scrolling
region on entry and restore to full screen region on exit:
"""Example of Terminal.scroll_region()."""
from blessed import Terminal
term = Terminal()
with term.fullscreen(), term.cbreak():
# Draw header
print(term.home + term.clear, end='')
print(term.reverse(term.center("Scroll region demo")), end='')
# Draw footer
print(term.move_yx(term.height - 1, 0), end='')
print(term.reverse(term.center("Press any key to stop")), end='')
# create scrolling region
with term.scroll_region(top=1, height=term.height - 2):
# Print text within scrolling region
print(term.move_yx(1, 0), end='')
for line_num in range(1000):
print(f'\n{line_num:4d}: Lorem ipsum dolor sit amet.', end='')
if term.inkey(0.01):
break