Commit f43bf0ba authored by Fjen Undso's avatar Fjen Undso
Browse files

move gui elements into package

parent 458aec5a
__pycache__/*
__pycache__/
#!/usr/bin/env python3
import logging
from curses import wrapper
import curses
import time
import lights
class CursesHandler(logging.Handler):
def __init__(self, screen):
logging.Handler.__init__(self)
maxy, maxx = screen.getmaxyx()
win_log_border = screen.subwin(5, maxx, maxy - 5, 0)
win_log_border.border(0)
win_log = win_log_border.subwin(3, maxx - 2, maxy - 4, 1)
win_log.scrollok(True)
win_log.idlok(True)
win_log.leaveok(True)
self.screen = win_log
formatter = logging.Formatter(
'%(name) -10s | %(levelname) -5s | %(message)s')
self.setFormatter(formatter)
def emit(self, record):
try:
msg = self.format(record)
screen = self.screen
fs = "\n%s"
screen.addstr(fs % msg)
screen.refresh()
except (KeyboardInterrupt, SystemExit):
raise
except BaseException:
self.handleError(record)
class NumberRange:
def __init__(self, window, *, row=0, col=0, value=0, text='', attr=curses.A_STANDOUT, min_value=0, max_value=255, prev_key=None, next_key=None):
self.window = window
self.row = row
self.col = col
self.value = value
self.text = text
self.attr = attr
self.min = min_value
self.max = max_value
self.focus = False
self.prev_key = prev_key
self.next_key = next_key
self.draw()
def draw(self):
attr = curses.A_NORMAL
if self.focus:
attr = self.attr
self.window.move(self.row, self.col)
self.window.addstr(self.text + "<")
formatter = "{:>" + str(len(str(self.max))) + "}"
self.window.addstr(formatter.format(self.value), attr)
self.window.addstr(">")
self.window.refresh()
def send_key(self, key):
if self.focus:
if key == self.prev_key:
self.decr()
elif key == self.next_key:
self.incr()
elif key.isdigit():
if len(str(self.value)) == len(str(self.max)):
# reset number if field is full
self.set(int(key))
else:
# add number on right side
self.set(int(str(self.value) + key))
def set(self, value):
self.value = min(value, self.max)
self.value = max(self.value, self.min)
self.draw()
def incr(self):
self.set(self.value + 1)
def decr(self):
self.set(self.value - 1)
def toggle_focus(self):
self.focus = not self.focus
self.draw()
class CheckBox:
def __init__(self, window, *, row=0, col=0, text=None, toggle_key=None):
self.window = window
self.row = row
self.col = col
self.text = text
self.toggle_key = toggle_key
self.focus = False
self.active = False
self.draw()
def draw(self):
attr = curses.A_NORMAL
if self.focus:
attr = curses.A_STANDOUT
self.window.move(self.row, self.col)
if self.active:
self.window.addstr("(X) " + self.text, attr)
else:
self.window.addstr("( ) " + self.text, attr)
self.window.refresh()
def send_key(self, key):
if self.focus and key == self.toggle_key:
self.toggle()
def toggle(self):
self.active = not self.active
self.draw()
def toggle_focus(self):
self.focus = not self.focus
self.draw()
class FocusSwitcher:
def __init__(self, *, elements=[], prev_key=None, next_key=None):
self.elements = elements
self.prev_key = prev_key
self.next_key = next_key
self.focus = False
def toggle_focus(self):
if self.focus:
for e in self.elements:
if e.focus:
e.toggle_focus()
else:
self.elements[0].toggle_focus()
self.focus = not self.focus
def add(self, element):
self.elements.append(element)
def send_key(self, key):
if self.focus:
if key == self.prev_key:
self.prev()
elif key == self.next_key:
self.next()
else:
for e in self.elements:
e.send_key(key)
def next(self):
for i in range(len(self.elements)):
if self.elements[i].focus:
self.elements[i].toggle_focus()
if i == len(self.elements) - 1:
self.elements[0].toggle_focus()
else:
self.elements[i + 1].toggle_focus()
break
def prev(self):
for i in range(len(self.elements)):
if self.elements[i].focus:
self.elements[i].toggle_focus()
self.elements[i - 1].toggle_focus()
break
class NumberProgressContainer(FocusSwitcher):
def __init__(self, *, elements=[], prev_key=None, next_key=None):
super().__init__(elements=elements, prev_key=None, next_key=None)
def send_key(self, key):
"""Same as FocusSwitcher, but sets ProgressBar.value to NumberRange.value"""
super().send_key(key)
val = 0
for e in self.elements:
if isinstance(e, NumberRange):
val = e.value
for e in self.elements:
if isinstance(e, ProgressBar):
e.set(val)
class ProgressBar:
def __init__(self, window, *, row=0, col=0, width=0, height=0, text='',
max_value=100, attr=curses.A_STANDOUT, prev_key=None, next_key=None):
self.row = row
self.col = col
self.width = width
self.height = height
self.max = max_value
self.value = 0
self.attr = attr
self.focus = True
self.text = text
self.prev_key = prev_key
self.next_key = next_key
self.window = window.subwin(height, width, row, col)
self.draw()
def toggle_focus(self):
pass
def draw(self):
# draw frame
self.window.erase()
self.window.border(0)
if len(self.text) > self.width:
self.window.addstr(self.text)
else:
self.window.addstr(
0, (self.width - len(self.text)) // 2, self.text)
# draw bar
top = self.height - 1 - int(self.value / self.max * (self.height - 2))
bottom = self.height - 1
for i in range(top, bottom):
self.window.addstr(i, 1, ' ' * (self.width - 2), self.attr)
self.window.refresh()
def set(self, value):
self.value = min(value, self.max)
self.value = max(self.value, 0)
self.draw()
def incr(self):
self.set(self.value + 1)
def decr(self):
self.set(self.value - 1)
def send_key(self, key):
if key == self.prev_key:
self.decr()
elif key == self.next_key:
self.incr()
def main(stdscr):
stdscr.clear()
# hide cursor
curses.curs_set(0)
# init colors
curses.start_color()
curses.use_default_colors()
curses.init_pair(curses.COLOR_RED, 0, curses.COLOR_RED)
curses.init_pair(curses.COLOR_GREEN, 0, curses.COLOR_GREEN)
curses.init_pair(curses.COLOR_BLUE, 0, curses.COLOR_BLUE)
curses.init_pair(curses.COLOR_WHITE, 0, curses.COLOR_WHITE)
# logging
logger = logging.getLogger(__name__)
logger.addHandler(CursesHandler(stdscr))
logger.setLevel(logging.INFO)
numbers_text = [
('R', curses.COLOR_RED),
('G', curses.COLOR_GREEN),
('B', curses.COLOR_BLUE),
('W', curses.COLOR_WHITE)
]
progress_bars = [
ProgressBar(
stdscr,
row=0,
col=i * 8,
width=5,
height=12,
max_value=255,
attr=curses.color_pair(numbers_text[i][1]),
text=numbers_text[i][0]) for i in range(len(numbers_text))]
numbers = [
NumberRange(
stdscr,
col=i * 8,
row=12,
value=i,
prev_key='KEY_DOWN',
next_key='KEY_UP') for i in range(len(numbers_text))]
fs_pbn = [
NumberProgressContainer(
elements=i) for i in zip(numbers, progress_bars)]
fs_n = FocusSwitcher(
elements=fs_pbn,
prev_key='KEY_LEFT',
next_key='KEY_RIGHT')
checkboxes = [
CheckBox(
stdscr,
col=0,
row=i + 13,
toggle_key=' ',
text='ESP' +
str(i)) for i in range(1, 9)]
fs_cb = FocusSwitcher(
elements=checkboxes,
prev_key='KEY_UP',
next_key='KEY_DOWN')
fs_main = FocusSwitcher(elements=[fs_n, fs_cb], prev_key='\t')
fs_main.toggle_focus()
stdscr.refresh()
while True:
k = stdscr.getkey()
colors = lights.make_stripe_data(
[numbers[1].value, numbers[0].value, numbers[2].value, numbers[3].value] * 42)
lights.send(colors)
logger.info(k)
fs_main.send_key(k)
if __name__ == '__main__':
wrapper(main)
from abc import ABC, abstractmethod
from .focusable import Focusable
class Drawable(Focusable, ABC):
def __init__(self, *, window, row=0, col=0):
Focusable.__init__(self)
self.window = window
self.row = row
self.col = col
self.draw()
@abstractmethod
def draw(self):
pass
def toggle_focus(self):
Focusable.toggle_focus(self)
self.draw()
from abc import ABC, abstractmethod
class Focusable(ABC):
def __init__(self):
self.focus = False
@abstractmethod
def toggle_focus(self):
self.focus = not self.focus
import curses
from .abstracts.drawable import Drawable
class CheckBox(Drawable):
def __init__(self, *, window, row=0, col=0, text=None, toggle_key=None):
self.text = text
self.toggle_key = toggle_key
self.active = False
Drawable.__init__(self, window=window, row=row, col=col)
def draw(self):
attr = curses.A_NORMAL
if self.focus:
attr = curses.A_STANDOUT
self.window.move(self.row, self.col)
if self.active:
self.window.addstr("(X) " + self.text, attr)
else:
self.window.addstr("( ) " + self.text, attr)
self.window.refresh()
def send_key(self, key):
if self.focus and key == self.toggle_key:
self.toggle()
def toggle(self):
self.active = not self.active
self.draw()
from ..abstracts.focusable import Focusable
class FocusSwitcher(Focusable):
def __init__(self, *, elements=[], prev_key=None, next_key=None):
Focusable.__init__(self)
self.elements = elements
self.prev_key = prev_key
self.next_key = next_key
def toggle_focus(self):
if self.focus:
# we lose our focus, unfocus active element
for e in self.elements:
if e.focus:
e.toggle_focus()
else:
# set focus on first element
self.elements[0].toggle_focus()
Focusable.toggle_focus(self)
def add(self, element):
self.elements.append(element)
def send_key(self, key):
if self.focus:
if key == self.prev_key:
self.prev()
elif key == self.next_key:
self.next()
else:
for e in self.elements:
e.send_key(key)
def next(self):
for i in range(len(self.elements)):
if self.elements[i].focus:
self.elements[i].toggle_focus()
if i == len(self.elements) - 1:
self.elements[0].toggle_focus()
else:
self.elements[i + 1].toggle_focus()
break
def prev(self):
for i in range(len(self.elements)):
if self.elements[i].focus:
self.elements[i].toggle_focus()
self.elements[i - 1].toggle_focus()
break
from ..numberrange import NumberRange
from ..progressbar import ProgressBar
from ..abstracts.focusable import Focusable
from .focusswitcher import FocusSwitcher
class NumberBarContainer(FocusSwitcher):
def __init__(self, *, elements=[], prev_key=None, next_key=None):
super().__init__(elements=elements, prev_key=None, next_key=None)
def send_key(self, key):
"""Same as FocusSwitcher, but sets ProgressBar.value to NumberRange.value"""
super().send_key(key)
val = 0
for e in self.elements:
if isinstance(e, NumberRange):
val = e.value
for e in self.elements:
if isinstance(e, ProgressBar):
e.set(val)
def toggle_focus(self):
Focusable.toggle_focus(self)
# all elements share our focus state
for e in self.elements:
e.toggle_focus()
import logging
class CursesHandler(logging.Handler):
def __init__(self, screen):
logging.Handler.__init__(self)
maxy, maxx = screen.getmaxyx()
win_log_border = screen.subwin(5, maxx, maxy - 5, 0)
win_log_border.border(0)
win_log = win_log_border.subwin(3, maxx - 2, maxy - 4, 1)
win_log.scrollok(True)
win_log.idlok(True)
win_log.leaveok(True)
self.screen = win_log
formatter = logging.Formatter(
'%(name) -10s | %(levelname) -5s | %(message)s')
self.setFormatter(formatter)
def emit(self, record):
try:
msg = self.format(record)
screen = self.screen
fs = "\n%s"
screen.addstr(fs % msg)
screen.refresh()
except (KeyboardInterrupt, SystemExit):
raise
except BaseException:
self.handleError(record)
import curses
from .abstracts.drawable import Drawable
class NumberRange(Drawable):
def __init__(self, *, window, row=0, col=0, value=0, text='',
attr=curses.A_STANDOUT, min_value=0, max_value=255,
prev_key=None, next_key=None):
self.value = value
self.text = text
self.attr = attr
self.min = min_value
self.max = max_value
self.prev_key = prev_key
self.next_key = next_key
Drawable.__init__(self, window=window, row=row, col=col)
def draw(self):
attr = curses.A_NORMAL
if self.focus:
attr = self.attr
self.window.move(self.row, self.col)
self.window.addstr(self.text + "<")
formatter = "{:>" + str(len(str(self.max))) + "}"
self.window.addstr(formatter.format(self.value), attr)
self.window.addstr(">")
self.window.refresh()
def send_key(self, key):
if self.focus:
if key == self.prev_key:
self.decr()
elif key == self.next_key:
self.incr()
elif key.isdigit():
if len(str(self.value)) == len(str(self.max)):
# reset number if field is full
self.set(int(key))
else:
# add number on right side
self.set(int(str(self.value) + key))
def set(self, value):
self.value = min(value, self.max)
self.value = max(self.value, self.min)
self.draw()
def incr(self):
self.set(self.value + 1)
def decr(self):
self.set(self.value - 1)
import curses
from .abstracts.drawable import Drawable
class ProgressBar(Drawable):
def __init__(self, *, window, row=0, col=0, width=0, height=0, text='',
max_value=100, attr=curses.A_STANDOUT, prev_key=None,
next_key=None):
self.width = width
self.height = height
self.max = max_value
self.value = 0
self.attr = attr
self.text = text
self.prev_key = prev_key
self.next_key = next_key
Drawable.__init__(self, window=window.subwin(height, width, row, col), row=row, col=col)
def draw(self):
# draw frame
self.window.erase()
self.window.border(0)
if len(self.text) > self.width:
self.window.addstr(self.text)
else:
self.window.addstr(
0, (self.width - len(self.text)) // 2, self.text)
# draw bar
top = self.height - 1 - int(self.value / self.max * (self.height - 2))
bottom = self.height - 1
for i in range(top, bottom):
self.window.addstr(i, 1, ' ' * (self.width - 2), self.attr)
self.window.refresh()
def set(self, value):
self.value = min(value, self.max)
self.value = max(self.value, 0)
self.draw()
def incr(self):
self.set(self.value + 1)
def decr(self):
self.set(self.value - 1)
def send_key(self, key):
if key == self.prev_key:
self.decr()
elif key == self.next_key:
self.incr()
#!/usr/bin/env python3
import logging
import time
import curses
<