From 2cb483c4812eee903295f76f30aaac0b429245d1 Mon Sep 17 00:00:00 2001 From: Ben Connors Date: Fri, 25 Oct 2019 13:24:41 -0400 Subject: Add preliminary chaser viewer --- interface/channelbank.py | 2 - interface/chaserview.py | 140 +++++++++++++++++++++++++++++++++++++++++++++++ interface/interface.py | 15 ++++- 3 files changed, 152 insertions(+), 5 deletions(-) create mode 100755 interface/chaserview.py diff --git a/interface/channelbank.py b/interface/channelbank.py index c7c7409..e3dec7b 100755 --- a/interface/channelbank.py +++ b/interface/channelbank.py @@ -125,8 +125,6 @@ class ChannelBank: self.win.erase() ncv = {} cols = (self._width-4)//ChannelView.width - with open("out.txt", 'w+') as f: - f.write(str(cols)+'\n') self._sscope = sorted(self.scope, key=lambda a: (a.f.id, a.id)) for n, c in enumerate(self._sscope): row = (n // cols)*ChannelView.height + 1 diff --git a/interface/chaserview.py b/interface/chaserview.py new file mode 100755 index 0000000..2c88343 --- /dev/null +++ b/interface/chaserview.py @@ -0,0 +1,140 @@ +#!/usr/bin/env python3 + +import curses +import math +import threading + +import blc2 +from blc2.constants import INFTY + +CURSES_LOCK = threading.RLock() + +def format_time(n): + if n == INFTY: + return "∞s" + elif n == 0: + return "0s" + postfixes = "mcisahkegtp" + idx = 0 + multiple = 10 + while n >= multiple: + idx += 1 + multiple *= 10 + + return str(n)[0]+postfixes[idx] + +class ChaserView: + def set_dim(self, height, width): + if height < 5 or width < 10: + raise ValueError("Size too small") + + with self._lock: + if (height, width) != (self._height, self._width): + self.win.erase() + self.win.noutrefresh() + + self.win.resize(height, width) + self.win.redrawwin() + + self._height = height + self._width = width + self._redraw() + + @staticmethod + def fit(s, l, pad=False): + ## TODO: Try shortening by words first? + if len(s) > l: + return s[:l-1] + '…' + elif len(s) < l and pad: + return s + ' '*(l-len(s)) + return s + + def set_pos(self, y, x): + with self._lock: + if (y, x) != (self._y, self._x): + self.win.mvwin(y, x) + self._y = y + self._x = x + self.win.noutrefresh() + + def _redraw(self): + self.win.erase() + self.win.border() + self.win.hline(2, 1, curses.ACS_HLINE, self._width-2) + self.win.addch(2, 0, curses.ACS_LTEE) + self.win.addch(2, self._width-1, curses.ACS_RTEE) + + if self._chaser is None: + self.win.refresh() + return + + c = self._chaser + w = self._width - 2 + self.win.addstr(1, 1, self.fit(("%d: "% c.id) + c.name, w-2)) + + maxsteps = self._height - 4 + if maxsteps < len(c.steps): + if self._selected is None: + steps = c.steps[:maxsteps] + else: + last = min(self._selected + (maxsteps // 2), len(c.steps)) + first = last - maxsteps + if first < 0: + last -= first + first = 0 + else: + first = 0 + last = len(c.steps) + steps = c.steps[first:last] + for n, s in enumerate(steps, 1): + if s.index == self._selected: + attrs = curses.A_REVERSE + else: + attrs = 0 + t = "%s:%s:%s" % (format_time(s.fade_in), format_time(s.duration), format_time(s.fade_out)) + self.win.addstr(n+2, 1, self.fit((self._numformat % (s.index+1)) + ": " + s.name, w-8, pad=True)+t, attrs) + + if first > 0: + self.win.addch(3, self._width//2, '⯅') + if last < len(c.steps): + self.win.addch(self._height-1, self._width//2, '⯆') + + self.win.refresh() + + @property + def selected(self): + with self._lock: + return self._selected + + @selected.setter + def selected(self, value): + with self._lock: + if value != self._selected: + self._selected = value + ## TODO: Clean this up if possible? + self._redraw() + + def set_chaser(self, chaser, selected=None): + with self._lock: + self._chaser = chaser + self._selected = selected + if chaser is not None and chaser.steps: + self._numformat = "%%%dd" % math.ceil(math.log10(len(chaser.steps))) + self._redraw() + + def __init__(self, y, x, height, width): + with CURSES_LOCK: + self.win = curses.newwin(height, width, y, x) + self.win.keypad(True) + self._lock = threading.RLock() + self._height = height + self._width = width + self._y = -1 + self._x = -1 + + self._chaser = None + self._numformat = "" + self._selected = -1 + + self.set_pos(y, x) + self.set_dim(height, width) diff --git a/interface/interface.py b/interface/interface.py index b838b08..65cd1d2 100644 --- a/interface/interface.py +++ b/interface/interface.py @@ -18,6 +18,7 @@ from blc2.workspace import Workspace from .globals import CURSES_LOCK from .input.tabcomp import Input from .channelbank import ChannelBank +from .chaserview import ChaserView from .pager import Pager def wrap_curses(f): @@ -40,21 +41,25 @@ class Interface: (ht, width), (0, 0), (4, wr), (height-4, wl+1), (hb - 4, wr), (ht, wl+1), + (hb, wl//2 - 1), (ht, 0), + (hb, wl - (wl//2)), (ht, wl//2) ) def _resize(self): - for a, f in zip(self._compute_sizes(*self.stdscr.getmaxyx()), (self.channel_bank.set_dim, self.channel_bank.set_pos, self.input.set_dim, self.input.set_pos, self.pager.set_dim, self.pager.set_pos)): + for a, f in zip(self._compute_sizes(*self.stdscr.getmaxyx()), (self.channel_bank.set_dim, self.channel_bank.set_pos, self.input.set_dim, self.input.set_pos, self.pager.set_dim, self.pager.set_pos, self.chaser_views[0].set_dim, self.chaser_views[0].set_pos, self.chaser_views[1].set_dim, self.chaser_views[1].set_pos)): f(*a) @wrap_curses def main(self, stdscr): height, width = stdscr.getmaxyx() self.stdscr = stdscr - cbd, cbp, ind, inp, pgd, pgp = self._compute_sizes(height, width) + cbd, cbp, ind, inp, pgd, pgp, cv0d, cv0p, cv1d, cv1p = self._compute_sizes(height, width) self.channel_bank = ChannelBank(*cbp, *cbd) self.input = Input(*inp, *ind) self.input.context = self.context_base self.pager = Pager(*pgp, *pgd) + self.chaser_views[0] = ChaserView(*cv0p, *cv0d) + self.chaser_views[1] = ChaserView(*cv1p, *cv1d) todisp = [ "=== Welcome to BLC2!", @@ -156,7 +161,9 @@ class Interface: with self.w_lock: td = ["FIXTURES:"] for f in sorted(self.w.fixtures.values(), key=lambda a: a.id): - td.append("- %03d; %3dc: %s" % (f.id, len(f.channels), f.name)) + td.append("- %03d: %s" % (f.id, f.name)) + for c in f.channels: + td.append(" %03d-%03d: %s" % (f.id, c.id, c.name)) self.pager.display_many(td, split=True) def list_functions(self, typ): @@ -214,6 +221,8 @@ class Interface: self.w_lock = threading.RLock() + self.chaser_views = [None, None] + self.context_base = Input.parse_context(( ("edit $num", self.base_edit), ("delete $num", self.base_delete), -- cgit v1.2.3