diff light8/Lightboard.py @ 0:45b12307c695

Initial revision
author drewp
date Wed, 03 Jul 2002 09:37:57 +0000
parents
children 2af6698b0566
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/light8/Lightboard.py	Wed Jul 03 09:37:57 2002 +0000
@@ -0,0 +1,387 @@
+from __future__ import nested_scopes,division
+
+from Tix import *
+from signal import signal, SIGINT
+from time import time
+import sys, cPickle, random
+
+from uihelpers import *
+from panels import *
+from Xfader import *
+from subediting import Subediting
+from Fader import Fader
+from ExternalInput import ExternalSliders
+import io, stage, Subs, Patch, ExtSliderMapper
+import dmxclient
+
+class Pickles:
+    def __init__(self, scalelevels, subs=None, windowpos=None):
+        self.scalelevels = dict([(name, lev.get()) 
+            for name, lev in scalelevels.items()])
+        self.substate = dict([(name, subobj.get_state())
+            for name, subobj in subs])
+        self.windowpos = windowpos
+
+class Lightboard:
+    def __init__(self, master, DUMMY):
+        self.master = master
+
+        self.DUMMY = DUMMY
+        self.jostle_mode = 0
+        self.lastline = None
+
+        self.channel_levels = [] # these are actually the labels for the
+                                 # channel levels, and should probably be moved
+                                 # to panels.Leveldisplay
+        self.scalelevels = {}
+
+        self.lastsublevels = {}  # to determine which subs changed
+        self.unchangedeffect = {} # dict of levels for lights that didn't 
+                                  # change last time
+        self.lastunchangedgroup = {}
+
+        # doesn't draw any UI yet-- look for self.xfader.setupwidget()
+        self.xfader = Xfader(self.scalelevels) 
+        self.oldlevels = [None] * 68 # never replace this; just clear it
+        self.subediting = Subediting(currentoutputlevels=self.oldlevels)
+
+        self.windowpos = 0
+        self.get_data()
+        self.buildinterface()
+        self.load()
+
+        print "Light 8.8: Entering backgroundloop"
+        self.backgroundloop()
+        self.updatestagelevels()
+        self.rec_file = open('light9.log', 'a')
+        self.record_start()
+        
+    def buildinterface(self):
+        print "Light 8.8: Constructing interface..."
+        for w in self.master.winfo_children():
+            w.destroy()
+
+        print "\tstage"
+        stage_tl = toplevelat('stage')
+        s = stage.Stage(stage_tl)
+        stage.createlights(s)
+        s.setsubediting(self.subediting)
+        s.pack()
+        self.stage = s # save it
+
+        sub_tl = toplevelat('sub')
+        scene_tl = toplevelat('scenes')
+        effect_tl = toplevelat('effect')
+
+        print "\tslider patching -- It can't be turned off!"
+        mapping_tl = toplevelat('mapping')
+        self.slidermapper = ExtSliderMapper.ExtSliderMapper(mapping_tl, 
+                                                            self.scalelevels, 
+                                                            ExternalSliders(),
+                                                            self)
+        self.slidermapper.pack()
+
+        print "\tsubmaster control"
+        self.subpanels = Subpanels(sub_tl, effect_tl, scene_tl, self, 
+                                   self.scalelevels, Subs, self.xfader, 
+                                   self.changelevel, self.subediting, 
+                                   Subs.longestsubname())
+
+        for n, lev in self.scalelevels.items():
+            self.lastsublevels[n] = lev.get()
+
+        print "\tlevel display"
+        leveldisplay_tl = toplevelat('leveldisplay')
+        leveldisplay_tl.bind('<Escape>', sys.exit)
+
+        self.leveldisplay = Leveldisplay(leveldisplay_tl, self.channel_levels)
+        # I don't think we need this
+        # for i in range(0,len(self.channel_levels)):
+            # self.channel_levels[i].config(text=self.oldlevels[i])
+            # colorlabel(self.channel_levels[i])
+
+        print "\tconsole"
+        Console(self)
+
+        # root frame
+        print "\tcontrol panel"
+        self.master.configure(bg='black')
+        controlpanel = Controlpanel(self.master, self.xfader, self.refresh, 
+            self.quit, self.toggle_jostle, self.nonzerosubs)
+        
+        print "\tcrossfader"
+        xf=Frame(self.master)
+        xf.pack(side='right')
+
+        self.master.bind('<q>', self.quit)
+        self.master.bind('<r>', self.refresh)
+        leveldisplay_tl.bind('<q>', self.quit)
+        leveldisplay_tl.bind('<r>', self.refresh)
+
+        self.xfader.setupwidget(xf)
+        controlpanel.pack()
+
+        print "\tcue fader (skipped)"
+        # cuefader_tl = toplevelat('cuefader')
+        # cuefader = Fader(cuefader_tl, Subs.cues, self.scalelevels)
+        # cuefader.pack()
+        print "Light 8.8: Everything's under control"
+
+
+    def get_data(self,*args):
+        Subs.reload_data(self.DUMMY)
+        Patch.reload_data(self.DUMMY)
+        print "Light 8.8:", len(Patch.patch), "dimmers patched"
+        print "Light 8.8:", len(Subs.subs), "submasters loaded"
+
+    def refresh(self, *args):
+        'rebuild interface, reload data'
+        print "Light 8.8: Refresh initiated.  Cross your fingers."
+        self.get_data()
+        print "Light 8.8: Subediting refreshed"
+        self.subediting.refresh()
+        print "Light 8.8: Rebuilding interface..."
+        self.buildinterface()
+        bindkeys(self.master,'<Escape>', self.quit)
+        print "Light 8.8: Setting up slider patching..."
+        self.slidermapper.setup()
+        # self.master.tk_setPalette('gray40')
+        print "Light 8.8: Now back to your regularly scheduled Light 8"
+
+    def stageassub(self):
+        """returns the current onstage lighting as a levels
+        dictionary, skipping the zeros, and using names where
+        possible"""
+        levs=self.oldlevels
+        
+        return dict([(Patch.get_channel_name(i),l) for i,l
+                     in zip(range(1,len(levs)+1),levs)
+                     if l>0])
+    def save_sub(self, name, levels, refresh=1):
+        if not name:
+            print "Enter sub name in console."
+            return
+
+        st = ''
+        linebuf = 'subs["%s"] = {' % name
+        for channame,lev in levels.items():
+            if len(linebuf) > 60: 
+                st += linebuf + '\n   '
+                linebuf = ''
+
+            linebuf += ' "%s" : %d,' % (channame, lev)
+        st += linebuf + '}\n'
+        if self.DUMMY:
+            filename = 'ConfigDummy.py'
+        else:
+            filename = 'Config.py'
+        f = open(filename, 'a')
+        f.write(st)
+        f.close()
+        print 'Added sub:', st
+        if refresh:
+            self.refresh()
+
+    # this is called on a loop, and ALSO by the Scales
+    def changelevel(self, *args):
+        'Amp trims slider'
+
+        # load levels from external sliders
+        extlevels = self.slidermapper.get_levels()
+        for name, val in extlevels.items():
+            if name in self.scalelevels:
+                sl = self.scalelevels[name]
+                sl.set(val)
+        
+        # newstart = time()
+
+        # learn what changed
+        unchangedgroup = {}
+        changedgroup = {}
+        for name, lastlevel in self.lastsublevels.items():
+            newlevel = self.scalelevels[name].get()
+            if lastlevel != newlevel:
+                changedgroup[name] = newlevel
+            else:
+                unchangedgroup[name] = newlevel
+
+        changedeffect = {}
+        if not changedgroup:
+            # should load levels from last time
+            pass
+        else:
+            # calculate effect of new group.  this should take no time if 
+            # nothing changed
+            for name, level in changedgroup.items():
+                newlevels = Subs.subs[name].get_levels(level=level)
+                for (ch, fadelev) in newlevels.items():
+                    changedeffect[ch-1] = \
+                        max(changedeffect.get(ch-1, 0), fadelev)
+
+        if unchangedgroup != self.lastunchangedgroup:
+            # unchanged group changed! (confusing, huh?)
+            # this means: the static subs from the last time are not the same 
+            # as they are this time, so we recalculate effect of unchanged group
+            self.unchangedeffect = {}
+            for name, level in unchangedgroup.items():
+                newlevels = Subs.subs[name].get_levels(level=level)
+                for (ch, fadelev) in newlevels.items():
+                    self.unchangedeffect[ch-1] = \
+                        max(self.unchangedeffect.get(ch-1, 0), fadelev)
+            self.lastunchangedgroup = unchangedgroup
+                
+        # record sublevels for future generations (iterations, that is)
+        for name in self.lastsublevels:
+            self.lastsublevels[name] = self.scalelevels[name]
+
+        # merge effects together
+        levels = [0] * 68
+        if changedeffect:
+            levels = [int(max(changedeffect.get(ch, 0), 
+                              self.unchangedeffect.get(ch, 0)))
+                        for ch in range(0, 68)]
+        else:
+            levels = [int(self.unchangedeffect.get(ch, 0))
+                        for ch in range(0, 68)]
+
+        '''
+        newend = time()
+
+        levels_opt = levels
+        
+        oldstart = time()
+        # i tried to optimize this to a dictionary, but there was no speed
+        # improvement
+        levels = [0] * 68
+        for name, s in Subs.subs.items():
+            sublevel = self.scalelevels[name].get()
+            newlevels = s.get_levels(level=sublevel)
+            self.lastsublevels[name] = sublevel # XXX remove
+            for (ch, fadelev) in newlevels.items():
+                levels[ch-1] = max(levels[ch-1], fadelev)
+        levels = [int(l) for l in levels]
+        oldend = time()
+
+        newtime = newend - newstart
+        oldtime = oldend - oldstart
+        print "new", newtime, 'old', (oldend - oldstart), 'sup', \
+               oldtime / newtime
+        
+        if levels != levels_opt: 
+            raise "not equal"
+            # for l, lo in zip(levels, levels_opt):
+                # print l, lo
+        '''
+        
+        for lev,lab,oldlev,numlab in zip(levels, self.channel_levels, 
+                                         self.oldlevels, 
+                                         self.leveldisplay.number_labels):
+            if lev != oldlev:
+                lab.config(text="%d" % lev) # update labels in lev display
+                colorlabel(lab)             # recolor labels
+                if lev < oldlev:
+                    numlab['bg'] = 'blue'
+                else:
+                    numlab['bg'] = 'red'
+            else:
+                numlab['bg'] = 'grey40'
+
+        # replace the elements in oldlevels - don't make a new list 
+        # (Subediting is watching it)
+        self.oldlevels[:] = levels[:]
+            
+        if self.jostle_mode:
+            delta = random.randrange(-1, 2, 1) # (-1, 0, or 1)
+            # print "delta", delta
+            levels = [min(100, max(x + delta, 0)) for x in levels]
+            # print "jostled", levels
+
+        dmxclient.outputlevels([l/100 for l in levels])
+#        self.parportdmx.sendlevels(levels)
+
+    def updatestagelevels(self):
+        self.master.after(100, self.updatestagelevels)
+        for lev, idx in zip(self.oldlevels, xrange(0, 68 + 1)):
+            self.stage.updatelightlevel(Patch.get_channel_name(idx + 1), lev)
+
+    def load(self):
+        try:
+            filename = '/tmp/light9.prefs'
+            if self.DUMMY:
+                filename += '.dummy'
+            print "Light 8.8: Loading from", filename
+            file = open(filename, 'r')
+            p = cPickle.load(file)
+            for s, v in p.scalelevels.items():
+                try:
+                    self.scalelevels[s].set(v)
+                except Exception,e:
+                    print "Couldn't set %s -> %s: %s" % (s, v,e)
+            for name, substate in p.substate.items():
+                try:
+                    Subs.subs[name].set_state(substate)
+                except Exception, e:
+                    print "Couldn't set sub %s state: %s" % (name,e)
+        except IOError, e:
+            print "IOError: Couldn't load prefs (%s): %s" % (filename,e)
+        except EOFError, e:
+            print "EOFrror: Couldn't load prefs (%s): %s" % (filename,e)
+        except Exception,e:
+            print "Couldn't load prefs (%s): %s" % (filename,e)
+        self.slidermapper.setup()
+
+    def backgroundloop(self, *args):
+        self.master.after(150, self.backgroundloop, ())
+        self.changelevel()
+    def quit(self, *args):
+        print "Light 8.8: And that's my cue to exit..."
+        self.save()
+        self.record_end()
+        self.master.destroy()
+        sys.exit()
+    def save(self, *args):
+        filename = '/tmp/light9.prefs'
+        if self.DUMMY:
+            filename += '.dummy'
+        print "Light 8.8: Saving to", filename
+        file = open(filename, 'w')
+
+        try:
+            cPickle.dump(Pickles(self.scalelevels, Subs.subs.items()), file)
+        except cPickle.UnpickleableError:
+            print "UnpickleableError!  There's yer problem."
+    def toggle_jostle(self, *args):
+        self.jostle_mode = not self.jostle_mode
+        if self.jostle_mode:
+            print 'Light 8.8: Perhaps we can jost-le your memory?'
+        else:
+            print 'Light 8.8: He remembers! (jostle off)'
+    def nonzerosubs(self, *args):
+        print "Light 8.8: Active subs:"
+        for n, dv in self.scalelevels.items():
+            if dv.get() > 0:
+                print "%-.4f: %s" % (dv.get(), n)
+    def record_start(self):
+        print "Light 8.8: Recorder started"
+        self.rec_file.write("%s:\t%s\n" % (time(), "--- Start ---"))
+        self.record_stamp()
+    def record_end(self):
+        print "Light 8.8: Recorder shutdown"
+        self.rec_file.write("%s:\t%s\n" % (time(), "--- End ---"))
+    def record_stamp(self):
+        'Record the current submaster levels, continue this loop'
+        levels = []
+        for n, v in self.scalelevels.items():
+            lev = v.get()
+            if lev:
+                levels.append('%s\t%s' % (n, lev))
+
+
+        newdata = '\t'.join(levels) 
+        if newdata!=self.lastline:
+            template = "%s:\t%s\n" % (time(), newdata)
+            self.rec_file.write(template)
+            self.lastline = newdata
+        self.master.after(100, self.record_stamp)
+    def highlight_sub(self, name, color):
+        self.subediting.colorsub(name, color)