From 755d1dda2a1eb1c26fa7bc12328e2bca25256257 Mon Sep 17 00:00:00 2001 From: Ben Connors Date: Fri, 18 Oct 2019 23:02:12 -0400 Subject: Get a decent start on the actual interface - Can edit, create, delete scenes - Basic saving --- interface/interface.py | 223 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 223 insertions(+) create mode 100644 interface/interface.py (limited to 'interface/interface.py') diff --git a/interface/interface.py b/interface/interface.py new file mode 100644 index 0000000..c9f2a6e --- /dev/null +++ b/interface/interface.py @@ -0,0 +1,223 @@ +import curses +import os +import datetime as dt +import threading + +import blc2 + +from blc2.functions.audio import Audio +from blc2.functions.scene import Scene +from blc2.functions.chaser import Chaser +from blc2.functions.chaserstep import ChaserStep +from blc2.constants import SCENE + +from blc2.topology import Fixture + +from blc2.workspace import Workspace + +from .globals import CURSES_LOCK +from .input.tabcomp import Input +from .channelbank import ChannelBank +from .pager import Pager + +def wrap_curses(f): + def inner(*args, **kwargs): + return curses.wrapper(lambda stdscr: f(*args, stdscr, **kwargs)) + return inner + +__version__ = "v0.0.1" + +class Interface: + @staticmethod + def _compute_sizes(height, width): + hb = height // 2 + ht = height - hb + + wr = width // 2 + wl = width - wr - 1 + + return ( + (ht, width), (0, 0), + (4, wr), (height-4, wl+1), + (hb - 4, wr), (ht, wl+1), + ) + + 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)): + 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) + self.channel_bank = ChannelBank(*cbp, *cbd) + self.input = Input(*inp, *ind) + self.input.context = self.context_base + self.pager = Pager(*pgp, *pgd) + + todisp = [ + "=== Welcome to BLC2!", + "=== Currently running lib %s, int %s" % (blc2.__version__, __version__), + "", + ] + + if self._w_created: + todisp.append("Created a new workspace") + else: + todisp.append("Loaded workspace \"%s\" from \"%s\"" % (self.w.name, self.path)) + todisp.append("Authored by %s, last modified at %s" % (self.w.author, self.w.modified.strftime("%Y-%m-%d %H:%M:%S"))) + + self.pager.display_many(todisp) + self.input.main(self._resize) + + def base_edit(self, fid): + with self.w_lock: + if fid not in self.w.functions: + return "No such function" + f = self.w.functions[fid] + if f.type != SCENE: + ## FIXME + return "Can only edit scenes so far" + with CURSES_LOCK: + self.primitive = f + self.channel_bank.set_scope(f.scope) + self.channel_bank.set_values(f.values.items()) + self.channel_bank.title = f.type + ' "%s"' % f.name + self.input.context = self.context_scene + + def base_delete(self, fid): + with self.w_lock: + if fid not in self.w.functions: + return "No such function" + f = self.w.functions[fid] + f.delete() + + def base_new_scene(self, name): + with self.w_lock: + f = Scene(self.w, name=name) + self.base_edit(f.id) + + def _gather_channels(self, cr): + with self.w_lock: + ## Gather the affected channels + channels = [] + for f in self.w.fixtures.values(): + for s, e in cr[0]: + if (s <= f.id <= e) or (e == -1 and f.id >= s): + break + else: + continue + + for n, c in enumerate(f.channels, 1): + for s, e in cr[1]: + if (s <= n <= e) or (e == -1 and n >= s): + channels.append(c) + break + + return channels + + def scene_set(self, cr, v): + with self.w_lock: + channels = self._gather_channels(cr) + + ## Set the values + self.primitive.update({c: v for c in channels}) + + ## Update the display + self.channel_bank.set_scope(self.primitive.scope) + if v is not None: + self.channel_bank.set_values(((c, v) for c in channels)) + + def scene_clear(self, cr): + self.scene_set(cr, None) + + def scene_exit(self): + self.input.context = self.context_base + self.primitive = None + self.channel_bank.set_scope(()) + self.channel_bank.title = "Channels" + + def base_save(self, path=None): + with self.w_lock: + if path is not None: + self.path = path + if self.path is None: + return "No path set" + self.w.modified = dt.datetime.now() + self.w.save(self.path) + + return "Saved to "+self.path + + def base_quit(self): + quit() + + def list_fixtures(self): + 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)) + self.pager.display_many(td, split=True) + + def list_functions(self, typ): + with self.w_lock: + td = [typ.upper()+"S:"] + for f in sorted(self.w.functions.values(), key=lambda a: a.id): + if f.type == typ: + td.append("- %03d: %s" % (f.id, f.name)) + self.pager.display_many(td, split=True) + + def page(self): + self.pager.user_page() + + def page_clear(self): + self.pager.clear() + + def __init__(self, path): + ## Have to do most of the actual initialization in the main method, as curses isn't + ## ready yet. + self.channel_bank = None + self.input = None + self.pager = None + self.stdscr = None + + self.primitive = None + self.chasers = [] + + self.path = path + self._w_created = False + if path is None or not os.path.isfile(path): + self.w = Workspace("", "", 0, dt.datetime.now()) + self._w_created = True + else: + self.w = Workspace.load(path) + + self.w_lock = threading.RLock() + + self.context_base = Input.parse_context(( + ("edit $num", self.base_edit), + ("delete $num", self.base_delete), + ("new scene $quoted_string", self.base_new_scene), + + ("save", self.base_save), + ("save $quoted_string", self.base_save), + + ("list fixtures", self.list_fixtures), + ("list scenes", lambda: self.list_functions(SCENE)), + + ("page", self.page), + ("clrpage", self.page_clear), + ("quit", self.base_quit), + )) + + self.context_scene = Input.parse_context(( + ("set $channel_range to $value", self.scene_set), + ("reset $channel_range", self.scene_clear), + + ("list fixtures", self.list_fixtures), + ("list scenes", lambda: self.list_functions(SCENE)), + + ("page", self.page), + ("clrpage", self.page_clear), + ("quit", self.scene_exit), + )) -- cgit v1.2.3