diff bin/keyboardcomposer @ 214:38c1ccfb6820

keyboardcomposer works
author drewp@bigasterisk.com
date Mon, 11 Apr 2005 02:21:26 +0000
parents flax/KeyboardComposer.py@3905d3c92aaa
children 233ee9b1e561
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/bin/keyboardcomposer	Mon Apr 11 02:21:26 2005 +0000
@@ -0,0 +1,279 @@
+#!/usr/bin/python
+
+from __future__ import division,nested_scopes
+import sys, time
+
+from twisted.internet import reactor,tksupport
+from twisted.web import xmlrpc, server
+from Tix import *
+import math, atexit, pickle
+
+import run_local
+from light9.Fadable import Fadable
+from light9.Submaster import Submasters, sub_maxes
+from light9 import dmxclient
+from light9.uihelpers import toplevelat
+
+nudge_keys = {
+    'up' : list('qwertyuiop'),
+    'down' : list('asdfghjkl')
+}
+nudge_keys['down'].append('semicolon')
+
+class SubScale(Scale, Fadable):
+    def __init__(self, master, *args, **kw):
+        self.scale_var = kw.get('variable') or DoubleVar()
+        kw.update({'variable' : self.scale_var,
+                   'from' : 1, 'to' : 0, 'showvalue' : 0,
+                   'sliderlength' : 15, 'res' : 0.01,
+                   'width' : 40, 'troughcolor' : 'black', 'bg' : 'grey40',
+                   'highlightthickness' : 1, 'bd' : 1,
+                   'highlightcolor' : 'red', 'highlightbackground' : 'black',
+                   'activebackground' : 'red'})
+        Scale.__init__(self, master, *args, **kw)
+        Fadable.__init__(self, var=self.scale_var, wheel_step=0.05)
+        self.draw_indicator_colors()
+    def draw_indicator_colors(self):
+        if self.scale_var.get() == 0:
+            self['troughcolor'] = 'black'
+        else:
+            self['troughcolor'] = 'blue'
+
+class SubmasterTk(Frame):
+    def __init__(self, master, name, current_level):
+        Frame.__init__(self, master, bd=1, relief='raised', bg='black')
+        self.slider_var = DoubleVar()
+        self.slider_var.set(current_level)
+        self.scale = SubScale(self, variable=self.slider_var, width=20)
+        namelabel = Label(self, text=name, font="Arial 11", bg='black',
+            fg='white')
+        namelabel.pack(side=TOP)
+        levellabel = Label(self, textvariable=self.slider_var, font="Arial 11",
+            bg='black', fg='white')
+        levellabel.pack(side=TOP)
+        self.scale.pack(side=BOTTOM, expand=1, fill=BOTH)
+
+class KeyboardComposer(Frame):
+    def __init__(self, root, submasters, current_sub_levels=None, dmxdummy=0):
+        Frame.__init__(self, root, bg='black')
+        self.submasters = submasters
+        self.dmxdummy = dmxdummy
+
+        self.current_sub_levels = {}
+        if current_sub_levels:
+            self.current_sub_levels = current_sub_levels
+        else:
+            try:
+                self.current_sub_levels = \
+                    pickle.load(file('.keyboardcomposer.savedlevels'))
+            except IOError:
+                pass
+
+        self.draw_ui()
+        self.send_levels_loop()
+    def draw_ui(self):
+        self.rows = [] # this holds Tk Frames for each row
+        self.slider_vars = {} # this holds subname:sub Tk vars
+        self.slider_table = {} # this holds coords:sub Tk vars
+        self.name_to_subtk = {} # subname : SubmasterTk instance
+        self.current_row = 0
+        
+        self.make_key_hints()
+        self.draw_sliders()
+        self.highlight_row(self.current_row)
+        self.rows[self.current_row].focus()
+
+        self.buttonframe = Frame(self, bg='black')
+        self.buttonframe.pack(side=BOTTOM)
+        self.refreshbutton = Button(self.buttonframe, text="Refresh", 
+            command=self.refresh, bg='black', fg='white')
+        self.refreshbutton.pack(side=LEFT)
+        self.save_stage_button = Button(self.buttonframe, text="Save", 
+            command=lambda: self.save_current_stage(self.sub_name.get()), 
+            bg='black', fg='white')
+        self.save_stage_button.pack(side=LEFT)
+        self.sub_name = Entry(self.buttonframe, bg='black', fg='white')
+        self.sub_name.pack(side=LEFT)
+        self.stop_frequent_update_time = 0
+    def make_key_hints(self):
+        keyhintrow = Frame(self)
+
+        col = 0
+        for upkey, downkey in zip(nudge_keys['up'],
+                                  nudge_keys['down']):
+            # what a hack!
+            downkey = downkey.replace('semicolon', ';')
+            upkey, downkey = (upkey.upper(), downkey.upper())
+
+            # another what a hack!
+            keylabel = Label(keyhintrow, text='%s\n%s' % (upkey, downkey), 
+                width=1, font=('Arial', 10), bg='red', fg='white', anchor='c')
+            keylabel.pack(side=LEFT, expand=1, fill=X)
+            col += 1
+
+        keyhintrow.pack(fill=X, expand=0)
+        self.keyhints = keyhintrow
+    def setup_key_nudgers(self, tkobject):
+        for d, keys in nudge_keys.items():
+            for key in keys:
+                # lowercase makes full=0
+                keysym = "<KeyPress-%s>" % key
+                tkobject.bind(keysym, \
+                    lambda evt, num=keys.index(key), d=d: \
+                        self.got_nudger(num, d))
+
+                # uppercase makes full=1
+                keysym = "<KeyPress-%s>" % key.upper()
+                keysym = keysym.replace('SEMICOLON', 'colon')
+                tkobject.bind(keysym, \
+                    lambda evt, num=keys.index(key), d=d: \
+                        self.got_nudger(num, d, full=1))
+
+        # Row changing:
+        # Page dn, C-n, and ] do down
+        # Page up, C-p, and ' do up
+        for key in '<Prior> <Next> <Control-n> <Control-p> ' \
+                   '<Key-bracketright> <Key-apostrophe>'.split():
+            tkobject.bind(key, self.change_row)
+
+    def change_row(self, event):
+        diff = 1
+        if event.keysym in ('Prior', 'p', 'bracketright'):
+            diff = -1
+        old_row = self.current_row
+        self.current_row += diff
+        self.current_row = max(0, self.current_row)
+        self.current_row = min(len(self.rows) - 1, self.current_row)
+        self.unhighlight_row(old_row)
+        self.highlight_row(self.current_row)
+        row = self.rows[self.current_row]
+        self.keyhints.pack_configure(before=row)
+    def got_nudger(self, number, direction, full=0):
+        subtk = self.slider_table[(self.current_row, number)]
+        if direction == 'up':
+            if full:
+                subtk.scale.fade(1)
+            else:
+                subtk.scale.increase()
+        else:
+            if full:
+                subtk.scale.fade(0)
+            else:
+                subtk.scale.decrease()
+    def draw_sliders(self):
+        self.tk_focusFollowsMouse()
+
+        rowcount = -1
+        col = 0
+        for sub in self.submasters.get_all_subs():
+            if col == 0: # make new row
+                row = self.make_row()
+                rowcount += 1
+            current_level = self.current_sub_levels.get(sub.name, 0)
+            subtk = self.draw_sub_slider(row, col, sub.name, current_level)
+            self.slider_table[(rowcount, col)] = subtk
+            self.name_to_subtk[sub.name] = subtk
+            col += 1
+            col %= 10
+
+            def slider_changed(x, y, z, subtk=subtk):
+                subtk.scale.draw_indicator_colors()
+                self.send_levels()
+
+            subtk.slider_var.trace('w', slider_changed)
+    def make_row(self):
+        row = Frame(self, bd=2, bg='black')
+        row.pack(expand=1, fill=BOTH)
+        self.setup_key_nudgers(row)
+        self.rows.append(row)
+        return row
+    def draw_sub_slider(self, row, col, name, current_level):
+        subtk = SubmasterTk(row, name, current_level)
+        subtk.place(relx=col * 0.1, rely=0, relwidth=0.1, relheight=1)
+        self.setup_key_nudgers(subtk.scale)
+
+        self.slider_vars[name] = subtk.slider_var
+        return subtk
+    def highlight_row(self, row):
+        row = self.rows[row]
+        row['bg'] = 'red'
+    def unhighlight_row(self, row):
+        row = self.rows[row]
+        row['bg'] = 'black'
+    def get_levels(self):
+        return dict([(name, slidervar.get()) 
+            for name, slidervar in self.slider_vars.items()])
+    def get_levels_as_sub(self):
+        scaledsubs = [self.submasters.get_sub_by_name(sub) * level \
+            for sub, level in self.get_levels().items()]
+
+        maxes = sub_maxes(*scaledsubs)
+        return maxes
+    def save_current_stage(self, subname):
+        print "saving current levels as", subname
+        sub = self.get_levels_as_sub()
+        sub.name = subname
+        sub.temporary = 0
+        sub.save()
+
+    def save(self):
+        pickle.dump(self.get_levels(), 
+                    file('.keyboardcomposer.savedlevels', 'w'))
+    def send_frequent_updates(self):
+        """called when we get a fade -- send events as quickly as possible"""
+        if time.time() <= self.stop_frequent_update_time:
+            self.send_levels()
+            self.after(10, self.send_frequent_updates)
+
+    def get_dmx_list(self):
+        maxes = self.get_levels_as_sub()
+        return maxes.get_dmx_list()
+    def send_levels(self):
+        if not self.dmxdummy:
+            levels = self.get_dmx_list()
+            dmxclient.outputlevels(levels)
+        # print "sending levels", levels
+    def send_levels_loop(self):
+        self.send_levels()
+        self.after(1000, self.send_levels_loop)
+    def refresh(self):
+        self.save()
+        self.submasters = Submasters()
+        self.current_sub_levels = \
+            pickle.load(file('.keyboardcomposer.savedlevels'))
+        for r in self.rows:
+            r.destroy()
+        self.keyhints.destroy()
+        self.buttonframe.destroy()
+        self.draw_ui()
+
+class LevelServer(xmlrpc.XMLRPC):
+    def __init__(self,name_to_subtk):
+        self.name_to_subtk = name_to_subtk
+        
+    def xmlrpc_fadesub(self,subname,level,secs):
+        """submaster will fade to level in secs"""
+        try:
+            self.name_to_subtk[subname].scale.fade(level,secs)
+            ret='ok'
+        except Exception,e:
+            ret=str(e)
+        return ret
+
+
+if __name__ == "__main__":
+    s = Submasters()
+
+    root = Tk()
+    tl = toplevelat("Keyboard Composer", existingtoplevel=root)
+    kc = KeyboardComposer(tl, s, dmxdummy=0)
+    kc.pack(fill=BOTH, expand=1)
+
+    ls = LevelServer(kc.name_to_subtk)
+    reactor.listenTCP(8050, server.Site(ls))
+
+    root.bind("<Destroy>",reactor.stop)
+    root.protocol('WM_DELETE_WINDOW', reactor.stop)
+    reactor.addSystemEventTrigger('after','shutdown',kc.save)
+    tksupport.install(root,ms=10)
+    reactor.run()