#!/usr/bin/python from __future__ import division, nested_scopes import sys,os,time,atexit from optparse import OptionParser import Tkinter as tk try: from dispatch import dispatcher except ImportError: import louie as dispatcher import run_local from light9.dmxchanedit import Levelbox from light9 import dmxclient, Patch, Submaster class Subcomposer(tk.Frame): def __init__(self, master, levelboxopts=None, dmxdummy=0, numchannels=68, use_persistentlevels=0): tk.Frame.__init__(self, master, bg='black') self.dmxdummy = dmxdummy self.numchannels = numchannels self.levels = [0]*numchannels # levels should never get overwritten, just edited self.levelbox = Levelbox(self, num_channels=numchannels) self.levelbox.pack(side='top') # the dmx levels we edit and output, range is 0..1 (dmx chan 1 is # the 0 element) self.levelbox.setlevels(self.levels) self.savebox = EntryCommand(self, cmd=self.savenewsub) self.savebox.pack(side='top') self.loadbox = EntryCommand(self, verb="Load", cmd=self.loadsub) self.loadbox.pack(side='top') def alltozero(): self.set_levels([0] * self.numchannels) dispatcher.send("levelchanged") tk.Button(self, text="all to zero", command=alltozero).pack(side='top') dispatcher.connect(self.levelchanged,"levelchanged") dispatcher.connect(self.sendupdate,"levelchanged") if use_persistentlevels: self.persistentlevels() self.lastupdate=0 # time we last sent to dmx self.lastsent=[] # copy of levels def fill_both_boxes(self, subname): for box in [self.savebox, self.loadbox]: box.set(subname) def persistentlevels(self): """adjusts levels from subcomposer.savedlevels, if possible; and arranges to save the levels in that file upon exit""" self.load_levels() atexit.register(self.save_levels) def save_levels(self, *args): levelfile = file("subcomposer.savedlevels","w") levelfile.write(" ".join(map(str, self.levels))) def load_levels(self): try: levelfile = file("subcomposer.savedlevels","r") levels = map(float, levelfile.read().split()) self.set_levels(levels) except IOError: pass def levelchanged(self, channel=None, newlevel=None): if channel is not None and newlevel is not None: if channel>len(self.levels): return self.levels[channel-1]=max(0,min(1,float(newlevel))) self.levelbox.setlevels(self.levels) def savenewsub(self, subname): leveldict={} for i,lev in zip(range(len(self.levels)),self.levels): if lev!=0: leveldict[Patch.get_channel_name(i+1)]=lev s=Submaster.Submaster(subname,leveldict=leveldict) s.save() def loadsub(self, subname): """puts a sub into the levels, replacing old level values""" s=Submaster.Submasters().get_sub_by_name(subname) self.set_levels(s.get_dmx_list()) dispatcher.send("levelchanged") def sendupdate(self): if not self.dmxdummy: dmxclient.outputlevels(self.levels) self.lastupdate = time.time() self.lastsent = self.levels[:] def considersendupdate(self, use_after_loop=0): """If use_after_loop is true, it is the period of the after loop.""" if self.lastsent != self.levels or time.time() > self.lastupdate + 1: self.sendupdate() if use_after_loop: self.after(use_after_loop, self.considersendupdate, use_after_loop) def set_levels(self, levels): oldLen = len(self.levels) self.levels[:] = levels + [0] * (oldLen - len(levels)) dispatcher.send("levelchanged") class EntryCommand(tk.Frame): def __init__(self, master, verb="Save", cmd=None): tk.Frame.__init__(self, master, bd=2, relief='raised') tk.Label(self, text="Sub name:").pack(side='left') self.cmd = cmd self.entry = tk.Entry(self) self.entry.pack(side='left', expand=True, fill='x') self.entry.bind("", self.action) tk.Button(self, text=verb, command=self.action).pack(side='left') def action(self, *args): subname = self.entry.get() self.cmd(subname) print "sub", self.cmd, subname def set(self, text): self.entry.delete(0, 'end') self.entry.insert(0, text) def open_sub_editing_window(subname, use_mainloop=1, dmxdummy=0): if use_mainloop: toplevel = tk.Tk() else: toplevel = tk.Toplevel() if dmxdummy: dummy_str = ' (dummy)' else: dummy_str = '' toplevel.title("Subcomposer: %s%s" % (subname, dummy_str)) sc = Subcomposer(toplevel, use_persistentlevels=0, dmxdummy=dmxdummy) sc.pack(fill='both', expand=1) sc.loadsub(subname) sc.considersendupdate(use_after_loop=10) if use_mainloop: tk.mainloop() ############################# if __name__ == "__main__": parser = OptionParser(usage="%prog [subname]") opts, args = parser.parse_args() root=tk.Tk() root.config(bg='black') root.wm_title("subcomposer") root.tk_setPalette("#004633") sc = Subcomposer(root, dmxdummy=0, numchannels=276) sc.pack() tk.Label(root,text="Bindings: B1 adjust level; B2 set full; B3 instant bump", font="Helvetica -12 italic",anchor='w').pack(side='top',fill='x') if len(args) == 1: sc.loadsub(args[0]) sc.fill_both_boxes(args[0]) while 1: root.update() sc.considersendupdate() time.sleep(.01)