diff options
Diffstat (limited to 'interface/interface.py')
-rw-r--r-- | interface/interface.py | 128 |
1 files changed, 117 insertions, 11 deletions
diff --git a/interface/interface.py b/interface/interface.py index 08fd17d..3ce9aa4 100644 --- a/interface/interface.py +++ b/interface/interface.py @@ -31,7 +31,7 @@ def wrap_curses(f): return curses.wrapper(lambda stdscr: f(*args, stdscr, **kwargs)) return inner -__version__ = "v0.0.1" +__version__ = "v0.0.2" class Interface: def _compute_sizes(self, height, width): @@ -98,10 +98,12 @@ class Interface: def handle_show(self, f): with self.w_lock: - if f.type == SCENE: + if f is None or f.type == SCENE: if not isinstance(self.channel_bank, ChannelBank): (hw, yx), *_ = self._compute_sizes(*self.stdscr.getmaxyx()) self.channel_bank = ChannelBank(*yx, *hw) + if f is None: + return self.channel_bank.set_scope(f.scope) self.channel_bank.set_values(f.values.items()) elif f.type == AUDIO: @@ -111,7 +113,8 @@ class Interface: self.channel_bank.audio = f self.channel_bank.title = f.type + ' "%s"' % f.name self.renderer.clear_hold() - self.renderer.hold(f.values) + if f.type == SCENE: + self.renderer.hold(f.values) def handle_enter(self, fid): with self.w_lock: @@ -239,6 +242,62 @@ class Interface: self.renderer.clear_hold() self.renderer.hold(self.primitive.values) + def scene_edit(self, cr, force = False): + with self.w_lock: + + channels = self._gather_channels(cr) + if not force: + channels = [c for c in channels if c in self.primitive.scope] + else: + self.primitive.update({c: 0 for c in channels if c not in self.primitive.scope}) + + if not channels: + return "No channels given" + + curses.curs_set(0) + values = self.primitive.values + + self.channel_bank.set_active(channels, True) + + while True: + self.channel_bank.set_scope(self.primitive.scope) + self.channel_bank.set_values(((c, v) for c, v in values.items())) + self.renderer.clear_hold() + self.renderer.hold(self.primitive.values) + + l = self.pager.win.getch() + delta = 0 + + if l == curses.KEY_RESIZE: + self._resize() + elif l == ord('q'): + break + elif l in (ord('j'), curses.KEY_DOWN): + delta = -5 + elif l in (ord('k'), curses.KEY_UP): + delta = 5 + elif l == ord('h'): + delta = -1 + elif l == ord('l'): + delta = 1 + elif l == curses.KEY_NPAGE: + delta = 25 + elif l == curses.KEY_PPAGE: + delta = -25 + elif l == curses.KEY_HOME: + delta = 255 + elif l == curses.KEY_END: + delta = -255 + + if delta != 0: + for c in channels: + values[c] = max(0, min(255, values[c]+delta)) + self.primitive.update(values) + + self.channel_bank.set_active(channels, False) + + curses.curs_set(1) + def scene_clear(self, cr): self.scene_set(cr, None) @@ -558,13 +617,19 @@ class Interface: self.channel_bank.highlight = True self.rendering = True - self.renderer.set_functions(*((cv.chaser, cv.chaser.get_data()) for cv in self.chaser_views)) + self.renderer.set_functions(*((cv.chaser, cv.chaser.get_data(cv.selected)) for cv in self.chaser_views)) self.renderer.start() self.input.context = self.context_run return "Started running" + def chaser_run(self): + self.current_cv.highlight = False + self.chaser_stack.append([c.selected for c in self.chaser_views]) + self.handle_show(None) + self.base_run() + def run_exit(self): with self.w_lock: self.rendering = False @@ -574,10 +639,19 @@ class Interface: self.channel_bank.highlight = False self.channel_bank.set_scope(()) - self.input.context = self.context_base + if self.chaser_stack: + for i, c in zip(self.chaser_stack.pop(-1), self.chaser_views): + c.selected = i + if self.current_cv.selected is not None: + s = self.current_cv.chaser.steps[self.current_cv.selected] + if s.function is not None and s.function.type in (SCENE, AUDIO,): + self.handle_show(s.function) + self.input.context = self.context_chaser + else: + self.input.context = self.context_base - for cv in self.chaser_views: - cv.selected = None + for cv in self.chaser_views: + cv.selected = None def run_jump(self, n, p, index): if not index: @@ -591,6 +665,10 @@ class Interface: return "Step index out of range" self.renderer.advance((n, (p-1) if p is not None else p)) + def run_advance_all(self): + for c in self.chaser_views: + self.renderer.advance((c.chaser.id, None)) + def current_status(self): self.pager.display_many(( "CURRENT STATUS", @@ -645,7 +723,7 @@ class Interface: ("list chasers", lambda: self.list_functions(CHASER)), ("list audio", lambda: self.list_functions(AUDIO)), - ("currentstatus", lambda: self.current_status()), + ("currentstatus", self.current_status), ("pager page", self.page), ("pager clear", self.page_clear), @@ -655,12 +733,13 @@ class Interface: "edit": "Edit the specified function.", "delete": "Delete the specified function.", "new": "Create a new function of the given type.", - "save": "Save the workspace. The path is implicitly the one loaded from if not given.", + "write": "Save the workspace. The path is implicitly the one loaded from if not given.", "list": "List available fixtures or functions.", "pager": "Control the pager. In page mode, arrow keys, page up/down, home/end, and j/k can be used to scroll, q exits.", "quit": "Exit BLC.", "remove": "Remove the specified chaser from the display. If a letter is used, remove the chaser at the given position, where 'a' is the left-most chaser and so forth. If a number is used, treat it as a chaser ID.", "add": "Add a chaser to the display. Currently a limit of 2 visible.", + "currentstatus" : "Display information about the system's current status." }, self.help) self.context_chaser = Input.parse_context(( @@ -697,6 +776,8 @@ class Interface: ("list chasers", lambda: self.list_functions(CHASER)), ("list audio", lambda: self.list_functions(AUDIO)), + ("trailer", self.chaser_run), + ("pager page", self.page), ("pager clear", self.page_clear), ("quit", self.handle_exit), @@ -715,12 +796,16 @@ class Interface: "move": "Move the given step to the given position", "bind": "Bind the step to the given function.", "quit": "Return to the previous mode.", + "trailer": "Preview the chaser in run mode.", }, self.help) self.context_scene = Input.parse_context(( ("set $channel_range to $value", self.scene_set), ("reset $channel_range", self.scene_clear), + ("edit $channel_range", self.scene_edit), + ("edit $channel_range force", lambda cr: self.scene_edit(cr, True)), + ("list fixtures", self.list_fixtures), ("list scenes", lambda: self.list_functions(SCENE)), ("list chasers", lambda: self.list_functions(CHASER)), @@ -737,6 +822,7 @@ class Interface: "reset": "Remove the given channel range from the scene", "list": "List available fixtures or functions.", "pager": "Control the pager. In page mode, arrow keys, page up/down, home/end, and j/k can be used to scroll, q exits.", + "edit": "Live edit scenes using the arrow keys, page up/down, home/end, and h/j/k/l. If \"force\" is used, all matching channels are edited, instead of just the matching ones already in the scope.", "quit": "Return to the previous mode.", }, self.help) @@ -751,8 +837,18 @@ class Interface: ("fade in $time", self.audio_fade), ("fade out $time", lambda t: self.audio_fade(t, True)), + ("pager page", self.page), + ("pager clear", self.page_clear), ("quit", self.audio_exit), - )) + ), { + None: "This mode is for editing audio primitives for single audio files.", + "list": "List available fixtures or functions.", + "rename": "Rename the function.", + "filename": "Set the filename for the function.", + "fade": "Set fade times for the function. Note that unlike lighting, fade out is done during the audio's run.", + "pager": "Control the pager. In page mode, arrow keys, page up/down, home/end, and j/k can be used to scroll, q exits.", + "quit": "Return to the previous mode.", + }, self.help) self.context_run = Input.parse_context(( ("quit", self.run_exit), @@ -765,8 +861,18 @@ class Interface: ("advance", lambda: self.run_jump(0, None, True)), ("badvance", lambda: self.run_jump(1, None, True)), + ("everythingadvance", self.run_advance_all()), + ("currentstatus", self.current_status), - )) + ), { + None: "This mode is for running a show or previewing a chaser.", + "quit": "Return to the previous mode.", + "jump": "Jump the given chaser to the given location.", + "advance": "Advance a chaser a single step. If no letter is given, this is the first from the left.", + "badvance": "Advance the second chaser from the left a single step.", + "everythingadvance": "Advance all chasers a single step each.", + "currentstatus" : "Display information about the system's current status." + }, self.help) self.output = output self.renderer = Renderer(self.w, self.w_lock, self.output, self._render_callback) |