diff options
author | Ben Connors <benconnors@outlook.com> | 2019-10-26 21:57:45 -0400 |
---|---|---|
committer | Ben Connors <benconnors@outlook.com> | 2019-10-26 21:57:45 -0400 |
commit | 0e845a0dca629b40166f95cf6d266181af605555 (patch) | |
tree | 9d2abdfe3ff95fda06f844128d04c6192985898f /interface | |
parent | cce9d77ec8a412e413b799768625f407b3280eb2 (diff) |
Basically finish editing functionality
- Can create, view, delete, edit audio functions
o Integrated with chaser and base
- Can rename functions
- Can bind chaser steps to functions
- Now confirms on exit
Diffstat (limited to 'interface')
-rw-r--r-- | interface/audioview.py | 109 | ||||
-rw-r--r-- | interface/interface.py | 121 |
2 files changed, 217 insertions, 13 deletions
diff --git a/interface/audioview.py b/interface/audioview.py new file mode 100644 index 0000000..ebfe228 --- /dev/null +++ b/interface/audioview.py @@ -0,0 +1,109 @@ +import curses +import threading + +from .globals import CURSES_LOCK + +def format_time(t): + t = int(t/1000 + 0.5) + h = (t // 3600) + m = t % 3600 + + s = m % 60 + m = m // 60 + + if not m and not h: + return "%2ds" % s + elif not h: + return "%2dm%2ds" % (m, s) + return "%dh%2dm%2ds" % (h, m, s) + +class AudioView: + def set_dim(self, height, width): + with CURSES_LOCK, self._lock: + if (height, width) != (self._height, self._width): + self.win.erase() + self.win.refresh() + + self.win.resize(height, width) + self.win.redrawwin() + + self._height = height + self._width = width + self._refresh() + self.win.refresh() + + def set_pos(self, y, x): + with self._lock: + if (y, x) != (self._y, self._x): + with CURSES_LOCK: + self.win.mvwin(y, x) + self._put_title() + self.win.refresh() + + @property + def audio(self): + return self._audio + + @audio.setter + def audio(self, v): + with self._lock: + self._audio = v + self._refresh() + + @property + def title(self): + return self._title + + @title.setter + def title(self, value): + with self._lock: + self._title = value + self._put_title() + self.win.refresh() + + @property + def highlight(self): + return self._highlight + + @highlight.setter + def highlight(self, value): + with self._lock: + self._highlight = value + self._put_title() + self.win.refresh() + + def _put_title(self): + self.win.border() + pos = min(self._width-2-len(self._title), (3*self._width)//4 - (len(self._title) // 2)) + self.win.addstr(self._height-1, pos, self._title, curses.A_REVERSE if self._highlight else 0) + + def _refresh(self): + with CURSES_LOCK: + self.win.erase() + + if self._audio is not None: + self.win.addstr(1, 1, "Filename: "+str(self._audio.filename)) + self.win.addstr(2, 1, " Fade in: "+str(self._audio.fade_in)+"ms") + self.win.addstr(3, 1, "Duration: "+str(format_time(self._audio.duration))) + self.win.addstr(4, 1, "Fade out: "+str(self._audio.fade_out)+"ms") + + self._put_title() + self.win.refresh() + + 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._highlight = False + self._title = "Audio" + self._height = height + self._width = width + self._y = -1 + self._x = -1 + + self._audio = None + + self.set_pos(y, x) + self.set_dim(height, width) diff --git a/interface/interface.py b/interface/interface.py index d7d3de4..32d9060 100644 --- a/interface/interface.py +++ b/interface/interface.py @@ -21,6 +21,7 @@ from .channelbank import ChannelBank from .chaserview import ChaserView from .pager import Pager from .dialog import askyesnocancel +from .audioview import AudioView def wrap_curses(f): def inner(*args, **kwargs): @@ -74,8 +75,8 @@ class Interface: self.pager = Pager(*pgp, *pgd) todisp = [ - "=== Welcome to BLC2!", - "=== Currently running library %s, interface %s" % (blc2.__version__, __version__), + "Welcome to BLC2!", + "Currently running library %s, interface %s" % (blc2.__version__, __version__), "", ] @@ -91,9 +92,17 @@ class Interface: def handle_show(self, f): with self.w_lock: if f.type == SCENE: + if not isinstance(self.channel_bank, ChannelBank): + (hw, yx), *_ = self._compute_sizes(*self.stdscr.getmaxyx()) + self.channel_bank = ChannelBank(*yx, *hw) self.channel_bank.set_scope(f.scope) self.channel_bank.set_values(f.values.items()) - self.channel_bank.title = f.type + ' "%s"' % f.name + elif f.type == AUDIO: + if not isinstance(self.channel_bank, AudioView): + (hw, yx), *_ = self._compute_sizes(*self.stdscr.getmaxyx()) + self.channel_bank = AudioView(*yx, *hw) + self.channel_bank.audio = f + self.channel_bank.title = f.type + ' "%s"' % f.name def handle_enter(self, fid): with self.w_lock: @@ -128,6 +137,11 @@ class Interface: self.current_cv = cv self.current_cv.highlight = True self.input.context = self.context_chaser + elif f.type == AUDIO: + self.primitive = f + self.handle_show(f) + self.channel_bank.highlight = True + self.input.context = self.context_audio else: ## FIXME return "No other types allowed yet" @@ -173,9 +187,9 @@ class Interface: f = self.w.functions[fid] f.delete() - def base_new_scene(self, name): + def base_new(self, name, cls): with self.w_lock: - f = Scene(self.w, name=name) + f = cls(self.w, name=name) self.handle_enter(f.id) def _gather_channels(self, cr): @@ -217,6 +231,11 @@ class Interface: self.handle_exit() + def audio_exit(self): + self.primitive = None + + self.handle_exit() + def handle_exit(self): for c in self.chaser_views: c.highlight = False @@ -252,6 +271,9 @@ class Interface: self.input.context = self.context_chaser self.chaser = c else: + if isinstance(self.channel_bank, AudioView): + (hw, yx), *_ = self._compute_sizes(*self.stdscr.getmaxyx()) + self.channel_bank = ChannelBank(*yx, *hw) self.channel_bank.set_scope(()) self.channel_bank.title = "Channels" @@ -271,7 +293,10 @@ class Interface: return "Saved to "+self.path def base_quit(self): - quit() + if askyesnocancel(self.stdscr, "Really exit?"): + quit() + else: + self._resize() def list_fixtures(self): with self.w_lock: @@ -320,12 +345,9 @@ class Interface: if num < 1 or num > len(self.current_cv.chaser.steps): return "Out of range" - self.channel_bank.set_scope(()) - self.channel_bank.title = "Channels" - self.current_cv.selected = num - 1 s = self.current_cv.chaser.steps[num-1] - if s.function is not None and s.function.type in (SCENE,): + if s.function is not None and s.function.type in (SCENE, AUDIO,): self.handle_show(s.function) def chaser_edit(self, num=None): @@ -404,7 +426,7 @@ class Interface: return "No such function" f = self.w.functions[fid] if f.type not in (SCENE, AUDIO, CHASER): - raise ValueError("Invalid function") + return "Invalid function" else: f = None @@ -429,6 +451,24 @@ class Interface: s.name = name self.current_cv.set_chaser(c, s.index) + + def chaser_bind(self, fid): + with self.w_lock: + if self.current_cv.selected is None: + return "No step selected" + c = self.current_cv.chaser + s = c.steps[self.current_cv.selected] + + if fid not in self.w.functions: + return "No such function" + f = self.w.functions[fid] + if f.type not in (SCENE, AUDIO, CHASER): + return "Invalid function" + + s.function = f + + self.current_cv.set_chaser(c, s.index) + self.chaser_select(s.index+1) def chaser_move(self, a, b = None): with self.w_lock: @@ -453,6 +493,31 @@ class Interface: self.current_cv.set_chaser(c, cursel.index if cursel is not None else None) + def primitive_rename(self, name): + with self.w_lock: + self.primitive.name = name + self.channel_bank.title = self.primitive.type + ' "%s"' % name + + def chaser_rename_self(self, name): + with self.w_lock: + c = self.current_cv.chaser + c.name = name + + self.current_cv.set_chaser(c, self.current_cv.selected) + + def audio_fade(self, t, out=False): + with self.w_lock: + if out: + self.primitive.fade_out = t + else: + self.primitive.fade_in = t + self.channel_bank.audio = self.primitive + + def audio_filename(self, fname): + with self.w_lock: + self.primitive.filename = fname + self.channel_bank.audio = self.primitive + def __init__(self, path): ## Have to do most of the actual initialization in the main method, as curses isn't ## ready yet. @@ -482,7 +547,9 @@ class Interface: self.context_base = Input.parse_context(( ("edit $num", self.handle_enter), ("delete $num", self.base_delete), - ("new scene $quoted_string", self.base_new_scene), + ("new scene $quoted_string", lambda n: self.base_new(n, Scene)), + ("new chaser $quoted_string", lambda n: self.base_new(n, Chaser)), + ("new audio $quoted_string", lambda n: self.base_new(n, Audio)), ("add $num", self.base_add), ("remove $letter", lambda n: self.base_remove(n, True)), @@ -494,6 +561,7 @@ class Interface: ("list fixtures", self.list_fixtures), ("list scenes", lambda: self.list_functions(SCENE)), ("list chasers", lambda: self.list_functions(CHASER)), + ("list audio", lambda: self.list_functions(AUDIO)), ("pager page", self.page), ("pager clear", self.page_clear), @@ -521,20 +589,29 @@ class Interface: ("delete $num", self.chaser_delete), ("append $quoted_string", lambda n: self.chaser_new(-1, n)), - ("append $quoted_string from $num", lambda n, s: self.chaser_new(-1, n)), + ("append $quoted_string from $num", lambda n, s: self.chaser_new(-1, n, s)), ("append $quoted_string from new scene $quoted_string", lambda n, s: self.chaser_new_new(-1, n, s, Scene)), + ("append $quoted_string from new audio $quoted_string", lambda n, s: self.chaser_new_new(-1, n, s, Audio)), ("new $num $quoted_string", self.chaser_new), ("new $num $quoted_string from $num", self.chaser_new), ("new $num $quoted_string from new scene $quoted_string", lambda i, n, s: self.chaser_new_new(i, n, s, Scene)), + ("new $num $quoted_string from new audio $quoted_string", lambda i, n, s: self.chaser_new_new(i, n, s, Audio)), ("rename $quoted_string", self.chaser_rename), + ("rename chaser $quoted_string", self.chaser_rename_self), ("move $num to $num", self.chaser_move), ("move $num", self.chaser_move), ("fade in $time", self.chaser_fade), ("fade out $time", lambda t: self.chaser_fade(t, True)), ("length $time", self.chaser_duration), ("unset", self.chaser_unset), + ("bind $num", self.chaser_bind), + + ("list fixtures", self.list_fixtures), + ("list scenes", lambda: self.list_functions(SCENE)), + ("list chasers", lambda: self.list_functions(CHASER)), + ("list audio", lambda: self.list_functions(AUDIO)), ("pager page", self.page), ("pager clear", self.page_clear), @@ -552,6 +629,7 @@ class Interface: "unset": "Unset the duration of the selected step, inheriting the step's duration from its function.", "pager": "Control the pager. In page mode, arrow keys, page up/down, home/end, and j/k can be used to scroll, q exits.", "move": "Move the given step to the given position", + "bind": "Bind the step to the given function.", "quit": "Return to the previous mode.", }, self.help) @@ -562,6 +640,9 @@ class Interface: ("list fixtures", self.list_fixtures), ("list scenes", lambda: self.list_functions(SCENE)), ("list chasers", lambda: self.list_functions(CHASER)), + ("list audio", lambda: self.list_functions(AUDIO)), + + ("rename $quoted_string", self.primitive_rename), ("pager page", self.page), ("pager clear", self.page_clear), @@ -574,3 +655,17 @@ class Interface: "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_audio = Input.parse_context(( + ("list fixtures", self.list_fixtures), + ("list scenes", lambda: self.list_functions(SCENE)), + ("list chasers", lambda: self.list_functions(CHASER)), + + ("rename $quoted_string", self.primitive_rename), + + ("filename $quoted_string", self.audio_filename), + ("fade in $time", self.audio_fade), + ("fade out $time", lambda t: self.audio_fade(t, True)), + + ("quit", self.audio_exit), + )) |