Changeset - a92b6d1ac072
[Not reviewed]
default
0 2 1
David McClosky - 20 years ago 2005-06-15 04:28:16
dmcc@bigasterisk.com
SubClient created, keyboardcomposer and gyrocontroller use it now
Plus some minor cleanups in keyboardcomposer and gyrocontroller
3 files changed with 30 insertions and 28 deletions:
0 comments (0 inline, 0 general)
bin/gyrocontroller
Show inline comments
 
#!/usr/bin/env python
 
# vi: syntax=python
 

	
 
from __future__ import division
 
import run_local
 
from light9.Submaster import combine_subdict
 
from light9 import dmxclient, showconfig
 
from light9.subclient import SubClient
 

	
 
class circcycle:
 
    """Like itertools.cycle, but with a prev() method too.  You lose
 
    all iterator benefits by using this, since it will store the whole
 
    sequence/iterator in memory.  Also, do not run this on an infinite
 
    iterator, as it tuple'ifies the input."""
 
    def __init__(self, sequence):
 
        self.sequence = tuple(sequence)
 
        self.index = 0
 
    def __iter__(self):
 
        return self
 
    def change_index(self, delta):
 
        self.index = (self.index + delta) % len(self.sequence)
 
    def next(self):
 
        ret = self.sequence[self.index]
 
        self._change_index(1)
 
        return ret
 
    def prev(self):
 
        ret = self.sequence[self.index]
 
        self._change_index(-1)
 
        return ret
 

	
 
class AbstractSimpleController:
 
class AbstractSimpleController(SubClient):
 
    """Simple controller with minimal input and output:
 
    
 
    Input is 4 directions and 3 buttons.
 
    Output is an integer and a color and maybe more.
 
    
 
    Left + B1/right + B1: prev/next sub
 
    Y-axis + B2: set current level
 
    B3: toggle keep/solo mode
 
    Double-B3: clear kept levels"""
 
    def __init__(self, subnames):
 
        SubClient.__init__(self)
 
        self.subnames = subnames
 
        self.refresh()
 
    def refresh(self):
 
        # reload subs from disk
 
        self.submasters = Submasters()
 
        self.all_subs = circcycle(self.subnames)
 
        self.current_sub = self.all_subs.next()
 
        self.current_level = 1.0
 
        self.kept_levels = {} # subname : level [0..1]
 
        self.keep_solo_mode = 'keep' # either 'keep' or 'solo'
 
    def clear_kept_levels(self):
 
        self.kept_levels.clear()
 
    def next(self):
 
        if self.keep_solo_mode == 'keep':
 
            self.kept_levels[self.current_sub] = self.current_level
 

	
 
        self.current_sub = self.submasters.get_sub_by_name(self.all_subs.next())
 
    def prev(self):
 
        if self.keep_solo_mode == 'keep':
 
            self.kept_levels[self.current_sub] = self.current_level
 

	
 
        self.current_sub = self.submasters.get_sub_by_name(self.all_subs.prev())
 

	
 
    def get_levels_as_sub(self):
 
        if self.keep_solo_mode == 'keep':
 
            # send all levels in self.kept_levels
 
            levels = combine_subdict(self.kept_levels)
 
            levelsub = combine_subdict(self.kept_levels)
 
        else:
 
            levels = {self.current_sub : self.current_level}
 
            levelsub = self.current_sub * self.current_level
 

	
 
        return levels
 
    def get_dmx_list(self):
 
        maxes = self.get_levels_as_sub()
 
        return maxes.get_dmx_list()
 
    def send_levels(self):
 
        levels = self.get_dmx_list()
 
        dmxclient.outputlevels(levels)
 
        return levelsub
 

	
 
if __name__ == "__main__":
 
    if 0:
 
        x = range(20)
 
        for z in circcycle(x):
 
            print z
bin/keyboardcomposer
Show inline comments
 
#!/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 pickle
 

	
 
import run_local
 
from light9.Fadable import Fadable
 
from light9.Submaster import Submasters, sub_maxes
 
from light9 import dmxclient, showconfig
 
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):
 
class KeyboardComposer(Frame, SubClient):
 
    def __init__(self, root, submasters, current_sub_levels=None):
 
        Frame.__init__(self, root, bg='black')
 
        SubClient.__init__(self)
 
        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()
 
@@ -205,76 +205,65 @@ class KeyboardComposer(Frame):
 
            for name, slidervar in self.slider_vars.items()])
 
    def get_levels_as_sub(self):
 
        # TODO this should be adapted to use Submaster.combine_subdict
 
        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 = KeyboardComposer(tl, s)
 
    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()
light9/subclient.py
Show inline comments
 
new file 100644
 
from light9 import dmxclient
 

	
 
# later, this stuff will talk to a SubServer
 
class SubClient:
 
    def get_levels_as_sub(self):
 
        """Subclasses must implement this method and return a Submaster
 
        object."""
 
    def get_dmx_list(self):
 
        maxes = self.get_levels_as_sub()
 
        return maxes.get_dmx_list()
 
    def send_levels(self):
 
        levels = self.get_dmx_list()
 
        dmxclient.outputlevels(levels)
 
    def send_levels_loop(self, delay=1000):
 
        """This function assumes that we are an instance of a Tk object
 
        (or at least that we have an 'after' method)"""
 
        self.send_levels()
 
        self.after(delay, self.send_levels_loop, delay)
0 comments (0 inline, 0 general)