Mercurial > code > home > repos > light9
comparison flax/CueFaders.py @ 167:79bc84310e80
changes from tonight's rehearsal:
changes from tonight's rehearsal:
- CueFader is closer to actually running the show, computes DMX levels
to send.
- KeyboardComposer is not a dummy. Use DMXDUMMY=1 to disable it.
- Submaster: subs can now be "temporary" -- i.e. they shouldn't be saved
or loaded. to save a temporary sub, make a copy of it with a proper name
since the computed name will be ugly.
Also, get_normalized_copy() and crossfade() methods added.
linear_fade helper (shouldn't be in Submaster, probably) added too.
- dmxchanedit: longer labels
- cuelist1 now has some bogus data in it and some crap removed
- dmxclient: now listens to the $DMXHOST and $DMXDUMMY env variables.
- patchdata: now up to date with this year's show
- danshow subs song{01..19}: removed. maybe we'll re-add them in an
archive directory.
author | dmcc |
---|---|
date | Tue, 08 Jul 2003 16:19:55 +0000 |
parents | e0c227168519 |
children | f8b5cb5fbeed |
comparison
equal
deleted
inserted
replaced
166:7ccf1d10804b | 167:79bc84310e80 |
---|---|
1 from __future__ import division, nested_scopes | 1 from __future__ import division, nested_scopes |
2 import Tix as Tk | 2 import Tix as Tk |
3 import time | 3 import time |
4 from TreeDict import TreeDict, allow_class_to_be_pickled | 4 from TreeDict import TreeDict, allow_class_to_be_pickled |
5 from TLUtility import enumerate | 5 from TLUtility import enumerate |
6 import Submaster | |
6 | 7 |
7 cue_state_indicator_colors = { | 8 cue_state_indicator_colors = { |
8 # bg fg | 9 # bg fg |
9 'prev' : ('blue', 'white'), | 10 'prev' : ('blue', 'white'), |
10 'cur' : ('yellow', 'black'), | 11 'cur' : ('yellow', 'black'), |
77 self.timer_entry = Tk.Control(self, step=0.5, min=0, integer=0) | 78 self.timer_entry = Tk.Control(self, step=0.5, min=0, integer=0) |
78 self.timer_entry.entry.configure(textvariable=self.timer_var, width=5, | 79 self.timer_entry.entry.configure(textvariable=self.timer_var, width=5, |
79 bg='black', fg='white') | 80 bg='black', fg='white') |
80 self.timer_entry.pack(fill='y', side='left') | 81 self.timer_entry.pack(fill='y', side='left') |
81 self.disabled = (self.button['state'] == 'disabled') | 82 self.disabled = (self.button['state'] == 'disabled') |
83 self.fading = 0 | |
82 def start_fade(self, end_level=1): | 84 def start_fade(self, end_level=1): |
83 try: | 85 try: |
84 fade_time = float(self.timer_var.get()) | 86 fade_time = float(self.timer_var.get()) |
85 except ValueError: | 87 except ValueError: |
86 # TODO figure out how to handle this | 88 # since we use a control now, i don't think we need to worry about |
89 # validation any more. | |
87 print "can't fade -- bad time" | 90 print "can't fade -- bad time" |
88 return | 91 return |
89 | 92 |
90 self.start_time = time.time() | 93 self.start_time = time.time() |
91 self.start_level = self.scale_to_fade.scale_var.get() | 94 self.start_level = self.scale_to_fade.scale_var.get() |
92 self.end_level = end_level | 95 self.end_level = end_level |
93 self.fade_length = fade_time | 96 self.fade_length = fade_time |
97 self.fading = 1 | |
94 self.do_fade() | 98 self.do_fade() |
95 def do_fade(self): | 99 def do_fade(self): |
96 diff = time.time() - self.start_time | 100 diff = time.time() - self.start_time |
97 if diff < self.fade_length: | 101 if diff < self.fade_length: |
98 percent = diff / self.fade_length | 102 percent = diff / self.fade_length |
100 (percent * (self.end_level - self.start_level)) | 104 (percent * (self.end_level - self.start_level)) |
101 self.scale_to_fade.scale_var.set(newlevel) | 105 self.scale_to_fade.scale_var.set(newlevel) |
102 | 106 |
103 if newlevel != self.end_level: | 107 if newlevel != self.end_level: |
104 self.after(10, self.do_fade) | 108 self.after(10, self.do_fade) |
109 else: | |
110 self.fading = 0 | |
105 else: | 111 else: |
106 self.scale_to_fade.scale_var.set(self.end_level) | 112 self.scale_to_fade.scale_var.set(self.end_level) |
113 self.fading = 0 | |
107 def disable(self): | 114 def disable(self): |
108 if not self.disabled: | 115 if not self.disabled: |
109 self.button['state'] = 'disabled' | 116 self.button['state'] = 'disabled' |
110 self.disabled = 1 | 117 self.disabled = 1 |
111 def enable(self): | 118 def enable(self): |
114 self.disabled = 0 | 121 self.disabled = 0 |
115 def set_time(self, time): | 122 def set_time(self, time): |
116 self.timer_var.set(time) | 123 self.timer_var.set(time) |
117 def get_time(self): | 124 def get_time(self): |
118 return self.timer_var.get() | 125 return self.timer_var.get() |
126 def is_fading(self): | |
127 return self.fading | |
119 | 128 |
120 class CueFader(Tk.Frame): | 129 class CueFader(Tk.Frame): |
121 def __init__(self, master, cuelist): | 130 def __init__(self, master, cuelist): |
122 Tk.Frame.__init__(self, master, bg='black') | 131 Tk.Frame.__init__(self, master, bg='black') |
123 self.cuelist = cuelist | 132 self.cuelist = cuelist |
187 self.autoshift(name, scale) | 196 self.autoshift(name, scale) |
188 | 197 |
189 scale.scale.bind("<ButtonPress>", button_press) | 198 scale.scale.bind("<ButtonPress>", button_press) |
190 scale.scale.bind("<ButtonRelease>", button_release) | 199 scale.scale.bind("<ButtonRelease>", button_release) |
191 faderframe.pack(side='bottom', fill='both', expand=1) | 200 faderframe.pack(side='bottom', fill='both', expand=1) |
201 self.cues_as_subs = {} | |
192 def get_scale_desc(self, val, name): | 202 def get_scale_desc(self, val, name): |
193 go_button = self.go_buttons.get(name) | 203 go_button = self.go_buttons.get(name) |
194 if go_button: | 204 if go_button: |
195 time = go_button.get_time() | 205 time = go_button.get_time() |
196 return "%0.2f%%, %0.1fs left" % (val, time - ((val / 100.0) * time)) | 206 return "%0.2f%%, %0.1fs left" % (val, time - ((val / 100.0) * time)) |
208 return | 218 return |
209 | 219 |
210 for scale_name, scale in self.scales.items(): | 220 for scale_name, scale in self.scales.items(): |
211 scale.scale.set(0) | 221 scale.scale.set(0) |
212 self.cuelist.shift((-1, 1)[name == 'Next']) | 222 self.cuelist.shift((-1, 1)[name == 'Next']) |
223 | |
224 # now load the subs to fade between | |
225 for cue, name in zip(self.cuelist.get_current_cues(), | |
226 ('Prev', 'Cur', 'Next')): | |
227 self.cues_as_subs[name] = cue.get_levels_as_sub() | |
228 print "cues_as_subs", self.cues_as_subs | |
213 def autoshift(self, name, scale): | 229 def autoshift(self, name, scale): |
214 scale_val = scale.scale_var.get() | 230 scale_val = scale.scale_var.get() |
215 | 231 |
216 if scale_val == 1: | 232 if scale_val == 1: |
217 if self.auto_shift.get(): | 233 if self.auto_shift.get(): |
235 self.go_buttons[d].disable() | 251 self.go_buttons[d].disable() |
236 else: | 252 else: |
237 # undo above work | 253 # undo above work |
238 self.scales[d].enable() | 254 self.scales[d].enable() |
239 self.go_buttons[d].enable() | 255 self.go_buttons[d].enable() |
256 | |
257 cur_sub = self.cues_as_subs.get('Cur') | |
258 if cur_sub: | |
259 other_sub = self.cues_as_subs[name] | |
260 # print 'fade between %s and %s (%.2f)' % (cur_sub, other_sub, scale_val) | |
261 self.current_levels_as_sub = cur_sub.crossfade(other_sub, scale_val) | |
262 print "current levels", self.current_levels_as_sub.get_dmx_list() | |
263 # print | |
240 def opposite_direction(self, d): | 264 def opposite_direction(self, d): |
241 if d == 'Next': | 265 if d == 'Next': |
242 return 'Prev' | 266 return 'Prev' |
243 else: | 267 else: |
244 return 'Next' | 268 return 'Next' |
245 | 269 |
246 class Cue: | 270 class Cue: |
247 """A Cue has a name, a time, and any number of other attributes.""" | 271 """A Cue has a name, a time, and any number of other attributes.""" |
248 def __init__(self, name, time=3, **attrs): | 272 def __init__(self, name, time=3, sub_levels='', **attrs): |
249 self.name = name | 273 self.name = name |
250 self.time = time | 274 self.time = time |
275 self.sub_levels = sub_levels | |
251 self.__dict__.update(attrs) | 276 self.__dict__.update(attrs) |
252 def __repr__(self): | 277 def __repr__(self): |
253 return "<Cue %s, length %s>" % (self.name, self.time) | 278 return "<Cue %s, length %s>" % (self.name, self.time) |
279 def get_levels_as_sub(self): | |
280 """Get this Cue as a combined Submaster, normalized. This method | |
281 should not be called constantly, since it is somewhat expensive. It | |
282 will reload the submasters from disk, combine all subs together, and | |
283 then compute the normalized form.""" | |
284 subdict = {} | |
285 try: | |
286 print self, self.sub_levels | |
287 for line in self.sub_levels.split(','): | |
288 line = line.strip() | |
289 # print 'line', line | |
290 sub, scale = line.split(':') | |
291 # print 'sub', sub, 'scale', scale | |
292 sub = sub.strip() | |
293 scale = float(scale) | |
294 subdict[sub] = scale | |
295 # print 'subdict', subdict | |
296 except (ValueError, AttributeError): | |
297 print "parsing error when computing sub for", self | |
298 | |
299 s = Submaster.Submasters() | |
300 newsub = Submaster.sub_maxes(*[s[sub] * scale | |
301 for sub, scale in subdict.items()]) | |
302 return newsub.get_normalized_copy() | |
254 | 303 |
255 empty_cue = Cue('empty') | 304 empty_cue = Cue('empty') |
256 | 305 |
257 allow_class_to_be_pickled(Cue) | 306 allow_class_to_be_pickled(Cue) |
258 | 307 |
264 try: | 313 try: |
265 self.treedict.load(filename) | 314 self.treedict.load(filename) |
266 except IOError: | 315 except IOError: |
267 self.treedict.cues = [] | 316 self.treedict.cues = [] |
268 self.cues = self.treedict.cues | 317 self.cues = self.treedict.cues |
269 self.current_cue_index = 0 | 318 self.current_cue_index = -1 |
270 self.next_pointer = None | 319 self.next_pointer = 0 |
271 self.prev_pointer = None | 320 self.prev_pointer = None |
272 | 321 |
273 import atexit | 322 import atexit |
274 atexit.register(self.save) | 323 atexit.register(self.save) |
275 def add_cue(self, cue, index=None): | 324 def add_cue(self, cue, index=None): |
463 self.master.title("Editing '%s'" % self.cue.name) | 512 self.master.title("Editing '%s'" % self.cue.name) |
464 except AttributeError: | 513 except AttributeError: |
465 pass | 514 pass |
466 def setup_editing_forms(self): | 515 def setup_editing_forms(self): |
467 self.variables = {} | 516 self.variables = {} |
468 for row, field in enumerate(('name', 'time', 'page', 'desc')): | 517 for row, field in enumerate(('name', 'time', 'page', 'desc', 'sub_levels')): |
469 lab = Tk.Label(self, text=field, fg='white', bg='black') | 518 lab = Tk.Label(self, text=field, fg='white', bg='black') |
470 lab.grid(row=row, column=0, sticky='nsew') | 519 lab.grid(row=row, column=0, sticky='nsew') |
471 | 520 |
472 entryvar = Tk.StringVar() | 521 entryvar = Tk.StringVar() |
473 entry = Tk.Entry(self, fg='white', bg='black', | 522 entry = Tk.Entry(self, fg='white', bg='black', |
486 | 535 |
487 entryvar.trace('w', field_changed) | 536 entryvar.trace('w', field_changed) |
488 self.columnconfigure(1, weight=1) | 537 self.columnconfigure(1, weight=1) |
489 def fill_in_cue_info(self): | 538 def fill_in_cue_info(self): |
490 self.enable_callbacks = 0 | 539 self.enable_callbacks = 0 |
491 for row, field in enumerate(('name', 'time', 'page', 'desc')): | 540 for row, field in enumerate(('name', 'time', 'page', 'desc', 'sub_levels')): |
492 text = '' | 541 text = '' |
493 if self.cue: | 542 if self.cue: |
494 try: | 543 try: |
495 text = getattr(self.cue, field) | 544 text = getattr(self.cue, field) |
496 except AttributeError: | 545 except AttributeError: |