comparison light8/Lightboard.py @ 0:45b12307c695

Initial revision
author drewp
date Wed, 03 Jul 2002 09:37:57 +0000
parents
children 2af6698b0566
comparison
equal deleted inserted replaced
-1:000000000000 0:45b12307c695
1 from __future__ import nested_scopes,division
2
3 from Tix import *
4 from signal import signal, SIGINT
5 from time import time
6 import sys, cPickle, random
7
8 from uihelpers import *
9 from panels import *
10 from Xfader import *
11 from subediting import Subediting
12 from Fader import Fader
13 from ExternalInput import ExternalSliders
14 import io, stage, Subs, Patch, ExtSliderMapper
15 import dmxclient
16
17 class Pickles:
18 def __init__(self, scalelevels, subs=None, windowpos=None):
19 self.scalelevels = dict([(name, lev.get())
20 for name, lev in scalelevels.items()])
21 self.substate = dict([(name, subobj.get_state())
22 for name, subobj in subs])
23 self.windowpos = windowpos
24
25 class Lightboard:
26 def __init__(self, master, DUMMY):
27 self.master = master
28
29 self.DUMMY = DUMMY
30 self.jostle_mode = 0
31 self.lastline = None
32
33 self.channel_levels = [] # these are actually the labels for the
34 # channel levels, and should probably be moved
35 # to panels.Leveldisplay
36 self.scalelevels = {}
37
38 self.lastsublevels = {} # to determine which subs changed
39 self.unchangedeffect = {} # dict of levels for lights that didn't
40 # change last time
41 self.lastunchangedgroup = {}
42
43 # doesn't draw any UI yet-- look for self.xfader.setupwidget()
44 self.xfader = Xfader(self.scalelevels)
45 self.oldlevels = [None] * 68 # never replace this; just clear it
46 self.subediting = Subediting(currentoutputlevels=self.oldlevels)
47
48 self.windowpos = 0
49 self.get_data()
50 self.buildinterface()
51 self.load()
52
53 print "Light 8.8: Entering backgroundloop"
54 self.backgroundloop()
55 self.updatestagelevels()
56 self.rec_file = open('light9.log', 'a')
57 self.record_start()
58
59 def buildinterface(self):
60 print "Light 8.8: Constructing interface..."
61 for w in self.master.winfo_children():
62 w.destroy()
63
64 print "\tstage"
65 stage_tl = toplevelat('stage')
66 s = stage.Stage(stage_tl)
67 stage.createlights(s)
68 s.setsubediting(self.subediting)
69 s.pack()
70 self.stage = s # save it
71
72 sub_tl = toplevelat('sub')
73 scene_tl = toplevelat('scenes')
74 effect_tl = toplevelat('effect')
75
76 print "\tslider patching -- It can't be turned off!"
77 mapping_tl = toplevelat('mapping')
78 self.slidermapper = ExtSliderMapper.ExtSliderMapper(mapping_tl,
79 self.scalelevels,
80 ExternalSliders(),
81 self)
82 self.slidermapper.pack()
83
84 print "\tsubmaster control"
85 self.subpanels = Subpanels(sub_tl, effect_tl, scene_tl, self,
86 self.scalelevels, Subs, self.xfader,
87 self.changelevel, self.subediting,
88 Subs.longestsubname())
89
90 for n, lev in self.scalelevels.items():
91 self.lastsublevels[n] = lev.get()
92
93 print "\tlevel display"
94 leveldisplay_tl = toplevelat('leveldisplay')
95 leveldisplay_tl.bind('<Escape>', sys.exit)
96
97 self.leveldisplay = Leveldisplay(leveldisplay_tl, self.channel_levels)
98 # I don't think we need this
99 # for i in range(0,len(self.channel_levels)):
100 # self.channel_levels[i].config(text=self.oldlevels[i])
101 # colorlabel(self.channel_levels[i])
102
103 print "\tconsole"
104 Console(self)
105
106 # root frame
107 print "\tcontrol panel"
108 self.master.configure(bg='black')
109 controlpanel = Controlpanel(self.master, self.xfader, self.refresh,
110 self.quit, self.toggle_jostle, self.nonzerosubs)
111
112 print "\tcrossfader"
113 xf=Frame(self.master)
114 xf.pack(side='right')
115
116 self.master.bind('<q>', self.quit)
117 self.master.bind('<r>', self.refresh)
118 leveldisplay_tl.bind('<q>', self.quit)
119 leveldisplay_tl.bind('<r>', self.refresh)
120
121 self.xfader.setupwidget(xf)
122 controlpanel.pack()
123
124 print "\tcue fader (skipped)"
125 # cuefader_tl = toplevelat('cuefader')
126 # cuefader = Fader(cuefader_tl, Subs.cues, self.scalelevels)
127 # cuefader.pack()
128 print "Light 8.8: Everything's under control"
129
130
131 def get_data(self,*args):
132 Subs.reload_data(self.DUMMY)
133 Patch.reload_data(self.DUMMY)
134 print "Light 8.8:", len(Patch.patch), "dimmers patched"
135 print "Light 8.8:", len(Subs.subs), "submasters loaded"
136
137 def refresh(self, *args):
138 'rebuild interface, reload data'
139 print "Light 8.8: Refresh initiated. Cross your fingers."
140 self.get_data()
141 print "Light 8.8: Subediting refreshed"
142 self.subediting.refresh()
143 print "Light 8.8: Rebuilding interface..."
144 self.buildinterface()
145 bindkeys(self.master,'<Escape>', self.quit)
146 print "Light 8.8: Setting up slider patching..."
147 self.slidermapper.setup()
148 # self.master.tk_setPalette('gray40')
149 print "Light 8.8: Now back to your regularly scheduled Light 8"
150
151 def stageassub(self):
152 """returns the current onstage lighting as a levels
153 dictionary, skipping the zeros, and using names where
154 possible"""
155 levs=self.oldlevels
156
157 return dict([(Patch.get_channel_name(i),l) for i,l
158 in zip(range(1,len(levs)+1),levs)
159 if l>0])
160 def save_sub(self, name, levels, refresh=1):
161 if not name:
162 print "Enter sub name in console."
163 return
164
165 st = ''
166 linebuf = 'subs["%s"] = {' % name
167 for channame,lev in levels.items():
168 if len(linebuf) > 60:
169 st += linebuf + '\n '
170 linebuf = ''
171
172 linebuf += ' "%s" : %d,' % (channame, lev)
173 st += linebuf + '}\n'
174 if self.DUMMY:
175 filename = 'ConfigDummy.py'
176 else:
177 filename = 'Config.py'
178 f = open(filename, 'a')
179 f.write(st)
180 f.close()
181 print 'Added sub:', st
182 if refresh:
183 self.refresh()
184
185 # this is called on a loop, and ALSO by the Scales
186 def changelevel(self, *args):
187 'Amp trims slider'
188
189 # load levels from external sliders
190 extlevels = self.slidermapper.get_levels()
191 for name, val in extlevels.items():
192 if name in self.scalelevels:
193 sl = self.scalelevels[name]
194 sl.set(val)
195
196 # newstart = time()
197
198 # learn what changed
199 unchangedgroup = {}
200 changedgroup = {}
201 for name, lastlevel in self.lastsublevels.items():
202 newlevel = self.scalelevels[name].get()
203 if lastlevel != newlevel:
204 changedgroup[name] = newlevel
205 else:
206 unchangedgroup[name] = newlevel
207
208 changedeffect = {}
209 if not changedgroup:
210 # should load levels from last time
211 pass
212 else:
213 # calculate effect of new group. this should take no time if
214 # nothing changed
215 for name, level in changedgroup.items():
216 newlevels = Subs.subs[name].get_levels(level=level)
217 for (ch, fadelev) in newlevels.items():
218 changedeffect[ch-1] = \
219 max(changedeffect.get(ch-1, 0), fadelev)
220
221 if unchangedgroup != self.lastunchangedgroup:
222 # unchanged group changed! (confusing, huh?)
223 # this means: the static subs from the last time are not the same
224 # as they are this time, so we recalculate effect of unchanged group
225 self.unchangedeffect = {}
226 for name, level in unchangedgroup.items():
227 newlevels = Subs.subs[name].get_levels(level=level)
228 for (ch, fadelev) in newlevels.items():
229 self.unchangedeffect[ch-1] = \
230 max(self.unchangedeffect.get(ch-1, 0), fadelev)
231 self.lastunchangedgroup = unchangedgroup
232
233 # record sublevels for future generations (iterations, that is)
234 for name in self.lastsublevels:
235 self.lastsublevels[name] = self.scalelevels[name]
236
237 # merge effects together
238 levels = [0] * 68
239 if changedeffect:
240 levels = [int(max(changedeffect.get(ch, 0),
241 self.unchangedeffect.get(ch, 0)))
242 for ch in range(0, 68)]
243 else:
244 levels = [int(self.unchangedeffect.get(ch, 0))
245 for ch in range(0, 68)]
246
247 '''
248 newend = time()
249
250 levels_opt = levels
251
252 oldstart = time()
253 # i tried to optimize this to a dictionary, but there was no speed
254 # improvement
255 levels = [0] * 68
256 for name, s in Subs.subs.items():
257 sublevel = self.scalelevels[name].get()
258 newlevels = s.get_levels(level=sublevel)
259 self.lastsublevels[name] = sublevel # XXX remove
260 for (ch, fadelev) in newlevels.items():
261 levels[ch-1] = max(levels[ch-1], fadelev)
262 levels = [int(l) for l in levels]
263 oldend = time()
264
265 newtime = newend - newstart
266 oldtime = oldend - oldstart
267 print "new", newtime, 'old', (oldend - oldstart), 'sup', \
268 oldtime / newtime
269
270 if levels != levels_opt:
271 raise "not equal"
272 # for l, lo in zip(levels, levels_opt):
273 # print l, lo
274 '''
275
276 for lev,lab,oldlev,numlab in zip(levels, self.channel_levels,
277 self.oldlevels,
278 self.leveldisplay.number_labels):
279 if lev != oldlev:
280 lab.config(text="%d" % lev) # update labels in lev display
281 colorlabel(lab) # recolor labels
282 if lev < oldlev:
283 numlab['bg'] = 'blue'
284 else:
285 numlab['bg'] = 'red'
286 else:
287 numlab['bg'] = 'grey40'
288
289 # replace the elements in oldlevels - don't make a new list
290 # (Subediting is watching it)
291 self.oldlevels[:] = levels[:]
292
293 if self.jostle_mode:
294 delta = random.randrange(-1, 2, 1) # (-1, 0, or 1)
295 # print "delta", delta
296 levels = [min(100, max(x + delta, 0)) for x in levels]
297 # print "jostled", levels
298
299 dmxclient.outputlevels([l/100 for l in levels])
300 # self.parportdmx.sendlevels(levels)
301
302 def updatestagelevels(self):
303 self.master.after(100, self.updatestagelevels)
304 for lev, idx in zip(self.oldlevels, xrange(0, 68 + 1)):
305 self.stage.updatelightlevel(Patch.get_channel_name(idx + 1), lev)
306
307 def load(self):
308 try:
309 filename = '/tmp/light9.prefs'
310 if self.DUMMY:
311 filename += '.dummy'
312 print "Light 8.8: Loading from", filename
313 file = open(filename, 'r')
314 p = cPickle.load(file)
315 for s, v in p.scalelevels.items():
316 try:
317 self.scalelevels[s].set(v)
318 except Exception,e:
319 print "Couldn't set %s -> %s: %s" % (s, v,e)
320 for name, substate in p.substate.items():
321 try:
322 Subs.subs[name].set_state(substate)
323 except Exception, e:
324 print "Couldn't set sub %s state: %s" % (name,e)
325 except IOError, e:
326 print "IOError: Couldn't load prefs (%s): %s" % (filename,e)
327 except EOFError, e:
328 print "EOFrror: Couldn't load prefs (%s): %s" % (filename,e)
329 except Exception,e:
330 print "Couldn't load prefs (%s): %s" % (filename,e)
331 self.slidermapper.setup()
332
333 def backgroundloop(self, *args):
334 self.master.after(150, self.backgroundloop, ())
335 self.changelevel()
336 def quit(self, *args):
337 print "Light 8.8: And that's my cue to exit..."
338 self.save()
339 self.record_end()
340 self.master.destroy()
341 sys.exit()
342 def save(self, *args):
343 filename = '/tmp/light9.prefs'
344 if self.DUMMY:
345 filename += '.dummy'
346 print "Light 8.8: Saving to", filename
347 file = open(filename, 'w')
348
349 try:
350 cPickle.dump(Pickles(self.scalelevels, Subs.subs.items()), file)
351 except cPickle.UnpickleableError:
352 print "UnpickleableError! There's yer problem."
353 def toggle_jostle(self, *args):
354 self.jostle_mode = not self.jostle_mode
355 if self.jostle_mode:
356 print 'Light 8.8: Perhaps we can jost-le your memory?'
357 else:
358 print 'Light 8.8: He remembers! (jostle off)'
359 def nonzerosubs(self, *args):
360 print "Light 8.8: Active subs:"
361 for n, dv in self.scalelevels.items():
362 if dv.get() > 0:
363 print "%-.4f: %s" % (dv.get(), n)
364 def record_start(self):
365 print "Light 8.8: Recorder started"
366 self.rec_file.write("%s:\t%s\n" % (time(), "--- Start ---"))
367 self.record_stamp()
368 def record_end(self):
369 print "Light 8.8: Recorder shutdown"
370 self.rec_file.write("%s:\t%s\n" % (time(), "--- End ---"))
371 def record_stamp(self):
372 'Record the current submaster levels, continue this loop'
373 levels = []
374 for n, v in self.scalelevels.items():
375 lev = v.get()
376 if lev:
377 levels.append('%s\t%s' % (n, lev))
378
379
380 newdata = '\t'.join(levels)
381 if newdata!=self.lastline:
382 template = "%s:\t%s\n" % (time(), newdata)
383 self.rec_file.write(template)
384 self.lastline = newdata
385 self.master.after(100, self.record_stamp)
386 def highlight_sub(self, name, color):
387 self.subediting.colorsub(name, color)