| 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 |
channelbank.py (5981B) - raw
1 import curses 2 3 from .globals import CURSES_LOCK 4 5 _progress = [" "] 6 for i in range(8): 7 pos = i // 2 8 s = _progress[-1] 9 _progress.append(s[:pos] + ('\u2588' if i%2 else '\u258c') + s[pos+1:]) 10 11 def get_progress(v): 12 if v == 0: 13 return _progress[0] 14 elif v <= 36: 15 return _progress[1] 16 elif v <= 73: 17 return _progress[2] 18 elif v <= 109: 19 return _progress[3] 20 elif v <= 145: 21 return _progress[4] 22 elif v <= 181: 23 return _progress[5] 24 elif v <= 218: 25 return _progress[6] 26 elif v <= 254: 27 return _progress[7] 28 return _progress[8] 29 30 class ChannelView: 31 width = 9 32 height = 3 33 34 def _refresh_channel(self): 35 with CURSES_LOCK: 36 self.win.addstr(0, 0, "%03d.%03d" % (self.c.f.id, self.c.id), curses.A_UNDERLINE if self._active else 0) 37 self.win.refresh() 38 39 def _refresh_value(self): 40 with CURSES_LOCK: 41 self.win.addstr(1, 0, get_progress(self._value)) 42 self.win.addstr(1, 4, "%03d" % self._value, (curses.A_ITALIC|curses.A_BOLD if self._held else 0)) 43 self.win.refresh() 44 45 def refresh(self): 46 self._refresh_value() 47 self._refresh_channel() 48 49 @property 50 def active(self): 51 return self._active 52 53 @active.setter 54 def active(self, v): 55 if v != self._active: 56 self._active = v 57 self._refresh_channel() 58 59 @property 60 def value(self): 61 return self._value 62 63 @value.setter 64 def value(self, v): 65 if v != self._value: 66 self._value = v 67 self._refresh_value() 68 69 @property 70 def held(self): 71 return self._held 72 73 @held.setter 74 def held(self, v): 75 if v != self._held: 76 self._held = v 77 self._refresh_value() 78 79 def set_pos(self, y, x): 80 self.y = self.y 81 self.x = self.x 82 with CURSES_LOCK: 83 self.win.refresh() 84 self.win.mvwin(y, x) 85 self.refresh() 86 87 def __init__(self, root, c, y, x, value=0, held=False, active=False): 88 self._active = active 89 self._held = held 90 self._value = value 91 self.c = c 92 93 self.y = y 94 self.x = x 95 96 self.root = root 97 with CURSES_LOCK: 98 self.win = root.subpad(self.height, self.width, self.y, self.x) 99 self.win.leaveok(True) 100 101 self._refresh_value() 102 self._refresh_channel() 103 104 class ChannelBank: 105 def set_dim(self, height, width): 106 if ((height-2)//ChannelView.height) * ((width-4)//ChannelView.width) < len(self.scope): 107 raise NotImplementedError("Screen size to small") 108 109 110 with CURSES_LOCK: 111 if (height, width) != (self._height, self._width): 112 self.win.erase() 113 self.win.refresh() 114 115 self.win.resize(height, width) 116 self.win.redrawwin() 117 118 self._height = height 119 self._width = width 120 self._refresh_scope() 121 self._put_title() 122 self.win.refresh() 123 124 def _refresh_scope(self): 125 with CURSES_LOCK: 126 self.win.erase() 127 ncv = {} 128 cols = (self._width-4)//ChannelView.width 129 self._sscope = sorted(self.scope, key=lambda a: (a.f.id, a.id)) 130 for n, c in enumerate(self._sscope): 131 row = (n // cols)*ChannelView.height + 1 132 col = (n % cols)*ChannelView.width + 2 133 if c in self._cv: 134 active = self._cv[c].active 135 held = self._cv[c].held 136 value = self._cv[c].value 137 else: 138 active = False 139 held = False 140 value = 0 141 ncv[c] = ChannelView(self.win, c, row, col, value=value, active=active, held=held) 142 self._cv = ncv 143 self._put_title() 144 self.win.refresh() 145 146 def set_pos(self, y, x): 147 if (y, x) != (self._y, self._x): 148 with CURSES_LOCK: 149 self.win.mvwin(y, x) 150 self._put_title() 151 self.win.refresh() 152 153 def set_active(self, channels, v=True): 154 for c in channels: 155 self._cv[c].active = v 156 157 def set_held(self, channels, v=True): 158 for c in channels: 159 self._cv[c].held = v 160 161 def set_values(self, cv): 162 if isinstance(cv, dict): 163 cv = cv.items() 164 for c, v in cv: 165 self._cv[c].value = v 166 167 def set_scope(self, scope): 168 self.scope = frozenset(scope) 169 self._refresh_scope() 170 171 @property 172 def title(self): 173 return self._title 174 175 def _put_title(self): 176 self.win.border() 177 pos = min(self._width-2-len(self._title), (3*self._width)//4 - (len(self._title) // 2)) 178 self.win.addstr(self._height-1, pos, self._title, curses.A_REVERSE if self._highlight else 0) 179 180 @property 181 def highlight(self): 182 return self._highlight 183 184 @highlight.setter 185 def highlight(self, value): 186 if value != self._highlight: 187 self._highlight = value 188 with CURSES_LOCK: 189 self._put_title() 190 self.win.refresh() 191 192 @title.setter 193 def title(self, v): 194 if v != self._title: 195 with CURSES_LOCK: 196 self._title = v 197 self.win.border() 198 self._put_title() 199 self.win.refresh() 200 201 def __init__(self, y, x, height, width, scope=frozenset()): 202 with CURSES_LOCK: 203 self.win = curses.newwin(height, width, y, x) 204 self.win.keypad(True) 205 self._highlight = False 206 self.scope = frozenset(scope) 207 self._sscope = [] 208 self._title = "Channels" 209 self._cv = {} 210 self._height = height 211 self._width = width 212 self._y = -1 213 self._x = -1 214 self.set_pos(y, x) 215 self.set_dim(height, width)