try:
import curses as _curses
except:
_curses = None
import itertools, os, queue, weakref
from . driver_base import DriverBase
[docs]class Curses(DriverBase):
"""
A driver that uses the "curses" library to get a colored display on a
regular terminal.
Note that this driver only works on some platforms.
The Linux default terminal probably works, but the exact details depend
on your specific OS and version.
The MacOS default (Terminal) works at low frame rates but the colors are
wrong (I delved into it and we can't work round it).
However, in the MacOS Term2 program, it works at framerates up to at least
10fps.
TODO: check how it works on the Rpi (#803)
"""
DRIVERS = weakref.WeakSet()
def __init__(self, *args, column_width=3, char='●', **kwds):
super().__init__(*args, **kwds)
if not (self.width or self.height):
self.width = self.numLEDs
self.height = 1
if list(Curses.DRIVERS):
raise ValueError('More than one Curses driver is not allowed')
Curses.DRIVERS.add(self)
self.column_width = column_width
self.offset = column_width // 2
self.char = chr(char) if isinstance(char, int) else char
self.queue = queue.Queue()
def _send_packet(self):
self.queue.put(True)
[docs] def stop(self):
super().stop()
self.queue.put(False)
[docs] @staticmethod
def main():
"""
If a project has a Curses driver, the section "main" in the section
"run" must be "bibliopixel.drivers.curses.Curses.main".
"""
if not _curses:
# https://stackoverflow.com/a/1325587/43839
if os.name == 'nt':
raise ValueError('curses is not supported under Windows')
raise ValueError('Your platform does not support curses.')
try:
driver = next(iter(Curses.DRIVERS))
except:
raise ValueError('No Curses driver in project')
_curses.wrapper(driver.run_in_curses)
[docs] def run_in_curses(self, stdscr):
self.set_curses_colors()
stdscr.clear()
stdscr.refresh()
cy, cx = stdscr.getmaxyx()
cx //= self.column_width
height, width = self.height, self.width
if width > cx:
# log.warning('Width %d too wide for screen %d', width, cx)
width = cx
if height > cx:
# log.warning('Height %d too tall for screen %d', height, cy)
height = cy
while self.queue.get():
for x in range(width):
for y in range(height):
self.write_color(stdscr, x, y)
stdscr.refresh()
[docs] def write_color(self, stdscr, x, y):
red, green, blue = self._colors[self._pos + x + self.width * y]
r, g, b = red >> 5, green >> 5, blue >> 6
index = r + 8 * (g + 8 * b)
pair = _curses.color_pair(index)
screen_x = self.column_width * x + self.offset
# Pair 0 always has foreground white; just draw a background if so.
char = self.char if index else ' '
stdscr.addch(y, screen_x, char, pair)
[docs] @staticmethod
def set_curses_colors():
s = 1000 / 255
for r, g, b in itertools.product(range(8), range(8), range(4)):
index = r + 8 * (g + 8 * b)
# Color 0 is black and can't be changed.
# Pair 0 is white on black and can't be changed.
if index:
r, g, b = r << 5, g << 5, b << 6
_curses.init_color(index, int(r * s), int(g * s), int(b * s))
_curses.init_pair(index, index, 0)