| blc2 - Python library and frontend for running theatrical lighting and SFX. Written for the English 2041F fall 2019 theatre production of "The Cenci".
git clone https://benconnors.ca/git-repos/blc2 |
chaserview.py (5313B) - raw
1 #!/usr/bin/env python3 2 3 import curses 4 import math 5 import threading 6 7 from blc2.constants import INFTY, MANUAL, JOIN, CHASERSTEP 8 9 CURSES_LOCK = threading.RLock() 10 11 def format_time(n): 12 if n == INFTY: 13 return "∞s" 14 elif n == 0: 15 return "0s" 16 postfixes = "mcisahkegtp" 17 idx = 0 18 multiple = 10 19 while n >= multiple: 20 idx += 1 21 multiple *= 10 22 23 return str(n)[0]+postfixes[idx] 24 25 def format_long(n): 26 if n == INFTY: 27 return " ∞s" 28 elif n == 0: 29 return " 0s" 30 elif n < 10000: 31 return "%4d" % n 32 else: 33 n = str(n/1000)[:3] 34 if n[-1] == '.': 35 n = n[:-1] 36 return "%4s" % n 37 38 class ChaserView: 39 def set_dim(self, height, width): 40 if height < 5 or width < 10: 41 raise ValueError("Size too small") 42 43 with self._lock: 44 if (height, width) != (self._height, self._width): 45 self.win.erase() 46 self.win.noutrefresh() 47 48 self.win.resize(height, width) 49 self.win.redrawwin() 50 51 self._height = height 52 self._width = width 53 self._redraw() 54 55 @staticmethod 56 def fit(s, l, pad=False): 57 ## TODO: Try shortening by words first? 58 if len(s) > l: 59 return s[:l-1] + '…' 60 elif len(s) < l and pad: 61 return s + ' '*(l-len(s)) 62 return s 63 64 def set_pos(self, y, x): 65 with self._lock: 66 if (y, x) != (self._y, self._x): 67 self.win.mvwin(y, x) 68 self._y = y 69 self._x = x 70 self.win.noutrefresh() 71 72 @property 73 def highlight(self): 74 return self._highlight 75 76 @highlight.setter 77 def highlight(self, value): 78 with self._lock: 79 if self._highlight != value: 80 self._highlight = value 81 self._redraw() 82 83 def _redraw(self): 84 self.win.erase() 85 self.win.border() 86 self.win.hline(2, 1, curses.ACS_HLINE, self._width-2) 87 self.win.addch(2, 0, curses.ACS_LTEE) 88 self.win.addch(2, self._width-1, curses.ACS_RTEE) 89 90 if self._chaser is None: 91 self.win.refresh() 92 return 93 94 c = self._chaser 95 w = self._width - 2 96 self.win.addstr(1, 1, self.fit(("%d: "% c.id) + c.name + " (%s)" % ("Join" if c.type == JOIN else c.advance_mode), w, True), curses.A_REVERSE if self._highlight else 0) 97 98 maxsteps = self._height - 4 99 first = 0 100 if maxsteps < len(c.steps): 101 if self._selected is None: 102 first = 0 103 last = maxsteps 104 else: 105 last = min(self._selected + (maxsteps // 2), len(c.steps)) 106 first = last - maxsteps 107 if first < 0: 108 last -= first 109 first = 0 110 else: 111 first = 0 112 last = len(c.steps) 113 steps = c.steps[first:last] 114 for n, s in enumerate(steps, 1): 115 if first+n-1 == self._selected: 116 attrs = curses.A_REVERSE 117 else: 118 attrs = 0 119 if s.type == CHASERSTEP and s.function is not None: 120 ft = s.function.type[0].upper() 121 fid = str(s.function.id) 122 elif s.type != CHASERSTEP: 123 ft = s.type[0].upper() 124 fid = str(s.id) 125 else: 126 ft = "-" 127 fid = "---" 128 129 t = "%s%3s%s|%s:%s:%s" % (ft, fid, '*' if (s.type == CHASERSTEP and s.duration_mode == MANUAL) else ' ', format_long(s.fade_in), format_long(s.duration if s.type != CHASERSTEP else s.length), format_long(s.fade_out)) 130 self.win.addstr(n+2, 1, self.fit((self._numformat % (first+n)) + ": " + s.name, w-len(t), pad=True)+t, attrs) 131 132 if first > 0: 133 self.win.addch(2, self._width//2, '⯅') 134 if last < len(c.steps): 135 self.win.addch(self._height-1, self._width//2, '⯆') 136 137 self.win.refresh() 138 139 @property 140 def selected(self): 141 with self._lock: 142 return self._selected 143 144 @selected.setter 145 def selected(self, value): 146 with self._lock: 147 if value != self._selected: 148 self._selected = value 149 ## TODO: Clean this up if possible? 150 self._redraw() 151 152 def set_chaser(self, chaser, selected=None): 153 with self._lock: 154 self._chaser = chaser 155 self._selected = selected 156 if chaser is not None and chaser.steps: 157 self._numformat = "%%%dd" % math.ceil(math.log10(len(chaser.steps))) 158 self._redraw() 159 160 @property 161 def chaser(self): 162 with self._lock: 163 return self._chaser 164 165 def __init__(self, y, x, height, width): 166 with CURSES_LOCK: 167 self.win = curses.newwin(height, width, y, x) 168 self.win.leaveok(True) 169 self.win.keypad(True) 170 self._lock = threading.RLock() 171 self._height = height 172 self._width = width 173 self._y = -1 174 self._x = -1 175 176 self._chaser = None 177 self._highlight = False 178 self._numformat = "" 179 self._selected = -1 180 181 self.set_pos(y, x) 182 self.set_dim(height, width)