Changeset - 6906cacaa218
[Not reviewed]
default
0 4 0
drewp@bigasterisk.com - 9 years ago 2016-06-12 00:24:57
drewp@bigasterisk.com
KC and SEQ share inotify code to reload effect code
Ignore-this: 2ae8142fda67b2290f4d0af219781c69
4 files changed with 31 insertions and 21 deletions:
0 comments (0 inline, 0 general)
bin/keyboardcomposer
Show inline comments
 
@@ -10,24 +10,25 @@ from twisted.internet import reactor, tk
 
from twisted.web import resource
 
from rdflib import URIRef, Literal
 
import Tix as tk
 

	
 
from light9.Fadable import Fadable
 
from light9.subclient import SubClient
 
from light9 import showconfig, networking, prof
 
from light9.uihelpers import toplevelat
 
from light9.namespaces import L9, RDF
 
from light9.tkdnd import initTkdnd, dragSourceRegister, dropTargetRegister
 
from light9.rdfdb import clientsession
 
from light9.rdfdb.syncedgraph import SyncedGraph
 
from light9.effect.sequencer import CodeWatcher
 
import light9.effect.effecteval
 

	
 
from bcf2000 import BCF2000
 

	
 
nudge_keys = {
 
    'up'   : list('qwertyui'),
 
    'down' : list('asdfghjk')
 
}
 

	
 
class DummySliders:
 
    def valueOut(self, name, value):
 
        pass
 
@@ -154,25 +155,29 @@ class KeyboardComposer(tk.Frame, SubClie
 
        self.slider_table = {} # coords : SubmasterBox
 
        self.rows = [] # this holds Tk Frames for each row
 

	
 
        self.current_row = 0 # should come from session graph
 

	
 
        self.use_hw_sliders = hw_sliders
 
        self.connect_to_hw(hw_sliders)
 

	
 
        self.make_key_hints()
 
        self.make_buttons()
 

	
 
        self.graph.addHandler(self.redraw_sliders)
 
        self.send_levels_loop()
 

	
 
        self.codeWatcher = CodeWatcher(
 
            onChange=lambda: self.graph.addHandler(self.redraw_sliders))
 
        
 
        self.send_levels_loop(delay=.05)
 
        self.graph.addHandler(self.rowFromGraph)
 

	
 
    def make_buttons(self):
 
        self.buttonframe = tk.Frame(self, bg='black')
 
        self.buttonframe.pack(side=tk.BOTTOM)
 

	
 
        self.sliders_status_var = tk.IntVar()
 
        self.sliders_status_var.set(self.use_hw_sliders)
 
        self.sliders_checkbutton = tk.Checkbutton(self.buttonframe,
 
            text="Sliders", variable=self.sliders_status_var,
 
            command=lambda: self.toggle_slider_connectedness(),
 
            bg='black', fg='white')
light9/collector/device.py
Show inline comments
 
@@ -42,24 +42,25 @@ def _8bit(f):
 

	
 
def resolve(deviceType, deviceAttr, values):
 
    """
 
    return one value to use for this attr, given a set of them that
 
    have come in simultaneously. len(values) >= 1.
 
    """
 
    if len(values) == 1:
 
        return values[0]
 
    if deviceAttr == L9['color']:
 
        rgbs = [hex_to_rgb(v) for v in values]
 
        return rgb_to_hex([max(*component) for component in zip(*rgbs)])
 
    # angles should perhaps use average; gobo choice use the most-open one
 
    
 
    return max(values)
 
    
 
def toOutputAttrs(deviceType, deviceAttrSettings):
 
    """
 
    Given device attr settings like {L9['color']: Literal('#ff0000')},
 
    return a similar dict where the keys are output attrs (like
 
    L9['red']) and the values are suitable for Collector.setAttr
 

	
 
    :outputAttrRange happens before we get here.
 
    """
 
    def floatAttr(attr, default=0):
 
        out = deviceAttrSettings.get(attr)
light9/effect/effecteval.py
Show inline comments
 
@@ -22,25 +22,25 @@ def scale(value, strength):
 
    elif isinstance(value, (int, float)):
 
        return value * strength
 
    else:
 
        raise NotImplementedError(repr(value))
 
    
 
class EffectEval(object):
 
    """
 
    runs one effect's code to turn effect attr settings into output
 
    device settings. No state; suitable for reload().
 
    """
 
    def __init__(self, graph, effect):
 
        self.graph = graph
 
        self.effect = effect
 
        self.effect = effect 
 

	
 
        # effect : [(dev, attr, value, isScaled)]
 
        self.effectOutputs = {}
 
        
 
        #for ds in g.objects(g.value(uri, L9['effectClass']), L9['deviceSetting']):
 
        #    self.setting = (g.value(ds, L9['device']), g.value(ds, L9['attr']))
 

	
 
        self.graph.addHandler(self.updateEffectsFromGraph)
 

	
 
    def updateEffectsFromGraph(self):
 
        self.effectOutputs = {}
 
        for effect in self.graph.subjects(RDF.type, L9['Effect']):
light9/effect/sequencer.py
Show inline comments
 
@@ -65,61 +65,65 @@ class Note(object):
 

	
 
        p1, p2 = self.points[i], self.points[i + 1]
 
        frac = (t - p1[0]) / (p2[0] - p1[0])
 
        y = p1[1] + (p2[1] - p1[1]) * frac
 
        return y
 
        
 
    def outputSettings(self, t):
 
        """
 
        list of (device, attr, value)
 
        """
 
        effectSettings = [(L9['strength'], self.evalCurve(t))]
 
        return self.effectEval.outputFromEffect(effectSettings, t)
 
                
 

	
 

	
 
class CodeWatcher(object):
 
    def __init__(self, onChange):
 
        self.onChange = onChange
 

	
 
        self.notifier = INotify()
 
        self.notifier.startReading()
 
        self.notifier.watch(
 
            FilePath(effecteval.__file__.replace('.pyc', '.py')),
 
            callbacks=[self.codeChange])
 

	
 
    def codeChange(self, watch, path, mask):
 
        def go():
 
            log.info("reload effecteval")
 
            reload(effecteval)
 
            self.onChange()
 
        # in case we got an event at the start of the write
 
        reactor.callLater(.1, go) 
 
    
 
        
 

	
 
class Sequencer(object):
 
    def __init__(self, graph, sendToCollector):
 
        self.graph = graph
 
        self.sendToCollector = sendToCollector
 
        self.music = MusicTime(period=.2, pollCurvecalc=False)
 

	
 
        self.recentUpdateTimes = []
 
        self.lastStatLog = 0
 
        self.notes = {} # song: [notes]
 
        self.graph.addHandler(self.compileGraph)
 
        self.update()
 

	
 
        self.watchCode()
 

	
 
    def watchCode(self):
 
        self.notifier = INotify()
 
        self.notifier.startReading()
 
        self.notifier.watch(
 
            FilePath(effecteval.__file__.replace('.pyc', '.py')),
 
            callbacks=[self.codeChange])
 

	
 
    def codeChange(self, watch, path, mask):
 
        def go():
 
            reload(effecteval)
 
            self.graph.addHandler(self.compileGraph)
 
        # in case we got an event at the start of the write
 
        reactor.callLater(.1, go) 
 
        self.codeWatcher = CodeWatcher(
 
            onChange=lambda: self.graph.addHandler(self.compileGraph))
 

	
 
    def compileGraph(self):
 
        """rebuild our data from the graph"""
 
        g = self.graph
 

	
 
        log.info("compileGraph")
 
        reload(effecteval)
 
        
 
        for song in g.subjects(RDF.type, L9['Song']):
 
            self.notes[song] = []
 
            for note in g.objects(song, L9['note']):
 
                self.notes[song].append(Note(g, note, effecteval))
 

	
 
    @stats.update.time()
 
    def update(self):
 
        now = time.time()
 
        self.recentUpdateTimes = self.recentUpdateTimes[-20:] + [now]
 
        stats.recentFps = len(self.recentUpdateTimes) / (self.recentUpdateTimes[-1] - self.recentUpdateTimes[0] + .0001)
 
        if now > self.lastStatLog + 10:
 
            log.info("%.2f fps", stats.recentFps)
0 comments (0 inline, 0 general)