0
|
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)
|