summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--interface/interface.py128
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)