Changeset - 1aa91a31c0e2
[Not reviewed]
default
0 13 0
Drew Perttula - 6 years ago 2019-05-25 12:06:01
drewp@bigasterisk.com
reformat some missed files
Ignore-this: f13152975437adeb48ed619ab676365e
12 files changed with 147 insertions and 53 deletions:
0 comments (0 inline, 0 general)
light9/Effects.py
Show inline comments
 

	
 
import random as random_mod
 
import math
 
import logging, colorsys
 
import light9.Submaster as Submaster
 
from .chase import chase as chase_logic
 
from . import showconfig
light9/FlyingFader.py
Show inline comments
 
from tkinter.tix import *
 
from time import time, sleep
 

	
 

	
 

	
 
class Mass:
 

	
 
    def __init__(self):
 
        self.x = 0  # position
 
        self.xgoal = 0  # position goal
 

	
light9/Submaster.py
Show inline comments
 

	
 
import os, logging, time
 
from rdflib import Graph, RDF
 
from rdflib import RDFS, Literal, BNode
 
from light9.namespaces import L9, XSD
 
from light9.TLUtility import dict_scale, dict_max
 
from light9 import showconfig
 
from light9.Patch import resolve_name, get_dmx_channel, get_channel_uri, reload_data
 
from louie import dispatcher
 
from .rdfdb.patch import Patch
 
log = logging.getLogger('submaster')
 

	
 

	
 
class Submaster(object):
 
    """mapping of channels to levels"""
 

	
 
    def __init__(self, name, levels):
 
        """this sub has a name just for debugging. It doesn't get persisted.
 
        See PersistentSubmaster.
 

	
 
        levels is a dict
 
        """
 
@@ -48,13 +49,15 @@ class Submaster(object):
 
    def no_nonzero(self):
 
        return all(v == 0 for v in self.levels.values())
 

	
 
    def __mul__(self, scalar):
 
        return Submaster("%s*%s" % (self.name, scalar),
 
                         levels=dict_scale(self.levels, scalar))
 

	
 
    __rmul__ = __mul__
 

	
 
    def max(self, *othersubs):
 
        return sub_maxes(self, *othersubs)
 

	
 
    def __add__(self, other):
 
        return self.max(other)
 

	
 
@@ -82,14 +85,15 @@ class Submaster(object):
 
        for k, v in list(leveldict.items()):
 
            if v == 0:
 
                continue
 
            try:
 
                dmxchan = get_dmx_channel(k) - 1
 
            except ValueError:
 
                log.error("error trying to compute dmx levels for submaster %s"
 
                          % self.name)
 
                log.error(
 
                    "error trying to compute dmx levels for submaster %s" %
 
                    self.name)
 
                raise
 
            if dmxchan >= len(levels):
 
                levels.extend([0] * (dmxchan - len(levels) + 1))
 
            levels[dmxchan] = max(v, levels[dmxchan])
 

	
 
        return levels
 
@@ -116,20 +120,22 @@ class Submaster(object):
 
        for k in list(self.levels.keys()) + list(otherlevels.keys()):
 
            keys_set[k] = 1
 
        all_keys = list(keys_set.keys())
 

	
 
        xfaded_sub = Submaster("xfade", {})
 
        for k in all_keys:
 
            xfaded_sub.set_level(k,
 
                                 linear_fade(self.levels.get(k, 0),
 
                                             otherlevels.get(k, 0),
 
            xfaded_sub.set_level(
 
                k,
 
                linear_fade(self.levels.get(k, 0), otherlevels.get(k, 0),
 
                                             amount))
 

	
 
        return xfaded_sub
 

	
 

	
 
class PersistentSubmaster(Submaster):
 

	
 
    def __init__(self, graph, uri):
 
        if uri is None:
 
            raise TypeError("uri must be URIRef")
 
        self.graph, self.uri = graph, uri
 
        self.graph.addHandler(self.setName)
 
        self.graph.addHandler(self.setLevels)
 
@@ -190,29 +196,33 @@ class PersistentSubmaster(Submaster):
 
    def _saveContext(self):
 
        """the context in which we should save all the lightLevel triples for
 
        this sub"""
 
        typeStmt = (self.uri, RDF.type, L9['Submaster'])
 
        with self.graph.currentState(tripleFilter=typeStmt) as current:
 
            try:
 
                log.debug("submaster's type statement is in %r so we save there" %
 
                log.debug(
 
                    "submaster's type statement is in %r so we save there" %
 
                          list(current.contextsForStatement(typeStmt)))
 
                ctx = current.contextsForStatement(typeStmt)[0]
 
            except IndexError:
 
                log.info("declaring %s to be a submaster" % self.uri)
 
                ctx = self.uri
 
                self.graph.patch(Patch(addQuads=[
 
                self.graph.patch(
 
                    Patch(addQuads=[
 
                    (self.uri, RDF.type, L9['Submaster'], ctx),
 
                    ]))
 

	
 
        return ctx
 

	
 
    def editLevel(self, chan, newLevel):
 
        self.graph.patchMapping(self._saveContext(),
 
                                subject=self.uri, predicate=L9['lightLevel'],
 
                                subject=self.uri,
 
                                predicate=L9['lightLevel'],
 
                                nodeClass=L9['ChannelSetting'],
 
                                keyPred=L9['channel'], newKey=chan,
 
                                keyPred=L9['channel'],
 
                                newKey=chan,
 
                                valuePred=L9['level'],
 
                                newValue=Literal(newLevel))
 

	
 
    def clear(self):
 
        """set all levels to 0"""
 
        with self.graph.currentState() as g:
 
@@ -227,13 +237,12 @@ class PersistentSubmaster(Submaster):
 
            quads.extend(current.quads((self.uri, None, None)))
 
            for s,p,o,c in quads:
 
                if p == L9['lightLevel']:
 
                    quads.extend(current.quads((o, None, None)))
 
        return quads
 

	
 

	
 
    def save(self):
 
        raise NotImplementedError("obsolete?")
 
        if self.temporary:
 
            log.info("not saving temporary sub named %s",self.name)
 
            return
 

	
 
@@ -262,18 +271,20 @@ def linear_fade(start, end, amount):
 
    """Fades between two floats by an amount.  amount is a float between
 
    0 and 1.  If amount is 0, it will return the start value.  If it is 1,
 
    the end value will be returned."""
 
    level = start + (amount * (end - start))
 
    return level
 

	
 

	
 
def sub_maxes(*subs):
 
    nonzero_subs = [s for s in subs if not s.no_nonzero()]
 
    name = "max(%s)" % ", ".join([repr(s) for s in nonzero_subs])
 
    return Submaster(name,
 
                     levels=dict_max(*[sub.levels for sub in nonzero_subs]))
 

	
 

	
 
def combine_subdict(subdict, name=None, permanent=False):
 
    """A subdict is { Submaster objects : levels }.  We combine all
 
    submasters first by multiplying the submasters by their corresponding
 
    levels and then max()ing them together.  Returns a new Submaster
 
    object.  You can give it a better name than the computed one that it
 
    will get or make it permanent if you'd like it to be saved to disk.
 
@@ -284,14 +295,16 @@ def combine_subdict(subdict, name=None, 
 
        maxes.name = name
 
    if permanent:
 
        maxes.temporary = False
 

	
 
    return maxes
 

	
 

	
 
class Submasters(object):
 
    "Collection o' Submaster objects"
 

	
 
    def __init__(self, graph):
 
        self.submasters = {} # uri : Submaster
 
        self.graph = graph
 

	
 
        graph.addHandler(self.findSubs)
 

	
 
@@ -329,26 +342,29 @@ class Submasters(object):
 
    def get_sub_by_uri(self, uri):
 
        return self.submasters[uri]
 

	
 
    def get_sub_by_name(self, name):
 
        return get_sub_by_name(name, self)
 

	
 

	
 
# a global instance of Submasters, created on demand
 
_submasters = None
 

	
 

	
 
def get_global_submasters(graph):
 
    """
 
    Get (and make on demand) the global instance of
 
    Submasters. Cached, but the cache is not correctly using the graph
 
    argument. The first graph you pass will stick in the cache.
 
    """
 
    global _submasters
 
    if _submasters is None:
 
        _submasters = Submasters(graph)
 
    return _submasters
 

	
 

	
 
def get_sub_by_name(name, submasters=None):
 
    """name is a channel or sub nama, submasters is a Submasters object.
 
    If you leave submasters empty, it will use the global instance of
 
    Submasters."""
 
    if not submasters:
 
        submasters = get_global_submasters()
light9/TLUtility.py
Show inline comments
 
"""Collected utility functions, many are taken from Drew's utils.py in
 
Cuisine CVS and Hiss's Utility.py."""
 

	
 

	
 
import sys
 

	
 
__author__ = "David McClosky <dmcc@bigasterisk.com>, " + \
 
             "Drew Perttula <drewp@bigasterisk.com>"
 
__cvsid__ = "$Id: TLUtility.py,v 1.1 2003/05/25 08:25:35 dmcc Exp $"
 
__version__ = "$Revision: 1.1 $"[11:-2]
 

	
 

	
 
def make_attributes_from_args(*argnames):
 
    """
 
    This function simulates the effect of running
 
      self.foo=foo
 
    for each of the given argument names ('foo' in the example just
 
    now). Now you can write:
 
@@ -31,56 +31,63 @@ def make_attributes_from_args(*argnames)
 
    for a in argnames:
 
        try:
 
            setattr(callerself,a,callerlocals[a])
 
        except KeyError:
 
            raise KeyError("Function has no argument '%s'" % a)
 

	
 

	
 
def enumerate(*collections):
 
    """Generates an indexed series:  (0,coll[0]), (1,coll[1]) ...
 
    
 
    this is a multi-list version of the code from the PEP:
 
    enumerate(a,b) gives (0,a[0],b[0]), (1,a[1],b[1]) ...
 
    """
 
    i = 0
 
    iters = [iter(collection) for collection in collections]
 
    while True:
 
        yield [i,] + [next(iterator) for iterator in iters]
 
        yield [
 
            i,
 
        ] + [next(iterator) for iterator in iters]
 
        i += 1
 

	
 

	
 
def dumpobj(o):
 
    """Prints all the object's non-callable attributes"""
 
    print(repr(o))
 
    for a in [x for x in dir(o) if not callable(getattr(o, x))]:
 
        try:
 
            print("  %20s: %s " % (a, getattr(o, a)))
 
        except:
 
            pass
 
    print("")
 

	
 

	
 
def dict_filter_update(d, **newitems):
 
    """Adds a set of new keys and values to dictionary 'd' if the values are
 
    true:
 

	
 
    >>> some_dict = {}
 
    >>> dict_filter_update(some_dict, a=None, b=0, c=1, e={}, s='hello')
 
    >>> some_dict
 
    {'c': 1, 's': 'hello'}
 
    """
 
    for k, v in list(newitems.items()):
 
        if v: d[k] = v
 

	
 

	
 
def try_get_logger(channel):
 
    """Tries to get a logger with the channel 'channel'.  Will return a
 
    silent DummyClass if logging is not available."""
 
    try:
 
        import logging
 
        log = logging.getLogger(channel)
 
    except ImportError:
 
        log = DummyClass()
 
    return log
 

	
 

	
 
class DummyClass:
 
    """A class that can be instantiated but never used.  It is intended to
 
    be replaced when information is available.
 
    
 
    Usage:
 
    >>> d = DummyClass(1, 2, x="xyzzy")
 
@@ -93,42 +100,50 @@ class DummyClass:
 
    >>> d.somefunction()
 
    Traceback (most recent call last):
 
      File "<stdin>", line 1, in ?
 
      File "Utility.py", line 33, in __getattr__
 
        raise AttributeError, "Attempted usage of a DummyClass: %s" % key
 
    AttributeError: Attempted usage of a DummyClass: somefunction"""
 

	
 
    def __init__(self, use_warnings=1, raise_exceptions=0, **kw):
 
        """Constructs a DummyClass"""
 
        make_attributes_from_args('use_warnings', 'raise_exceptions')
 

	
 
    def __getattr__(self, key):
 
        """Raises an exception to warn the user that a Dummy is not being
 
        replaced in time."""
 
        if key == "__del__":
 
            return
 
        msg = "Attempted usage of '%s' on a DummyClass" % key
 
        if self.use_warnings:
 
            import warnings
 
            warnings.warn(msg)
 
        if self.raise_exceptions:
 
            raise AttributeError(msg)
 
        return lambda *args, **kw: self.bogus_function()
 

	
 
    def bogus_function(self):
 
        pass
 

	
 

	
 
class ClassyDict(dict):
 
    """A dict that accepts attribute-style access as well (for keys
 
    that are legal names, obviously). I used to call this Struct, but
 
    chose the more colorful name to avoid confusion with the struct
 
    module."""
 

	
 
    def __getattr__(self, a):
 
        return self[a]
 

	
 
    def __setattr__(self, a, v):
 
        self[a] = v
 

	
 
    def __delattr__(self, a):
 
        del self[a]
 

	
 

	
 
def trace(func):
 
    """Good old fashioned Lisp-style tracing.  Example usage:
 
    
 
    >>> def f(a, b, c=3):
 
    >>>     print a, b, c
 
    >>>     return a + b
 
@@ -142,68 +157,73 @@ def trace(func):
 
    3
 

	
 
    TODO: print out default keywords (maybe)
 
          indent for recursive call like the lisp version (possible use of 
 
              generators?)"""
 
    name = func.__name__
 

	
 
    def tracer(*args, **kw):
 
        s = '|>> %s called' % name
 
        if args:
 
            s += ' args: %r' % list(args)
 
        if kw:
 
            s += ' kw: %r' % kw
 
        print(s)
 
        ret = func(*args, **kw)
 
        print('<<| %s returned %s' % (name, ret))
 
        return ret
 

	
 
    return tracer
 

	
 

	
 
# these functions taken from old light8 code
 
def dict_max(*dicts):
 
    """
 
    ({'a' : 5, 'b' : 9}, {'a' : 10, 'b' : 4})
 
      returns ==> {'a' : 10, 'b' : 9}
 
    """
 
    newdict = {}
 
    for d in dicts:
 
        for k,v in list(d.items()):
 
            newdict[k] = max(v, newdict.get(k, 0))
 
    return newdict
 

	
 

	
 
def dict_scale(d,scl):
 
    """scales all values in dict and returns a new dict"""
 
    return dict([(k,v*scl) for k,v in list(d.items())])
 
    
 

	
 
def dict_subset(d, dkeys, default=0):
 
    """Subset of dictionary d: only the keys in dkeys.  If you plan on omitting
 
    keys, make sure you like the default."""
 
    newd = {} # dirty variables!
 
    for k in dkeys:
 
        newd[k] = d.get(k, default)
 
    return newd
 

	
 

	
 
# functions specific to Timeline
 
# TBD
 
def last_less_than(array, x):
 
    """array must be sorted"""
 
    best = None
 
    for elt in array:
 
        if elt <= x:
 
            best = elt
 
        elif best is not None:
 
            return best
 
    return best
 

	
 

	
 
# TBD
 
def first_greater_than(array, x):
 
    """array must be sorted"""
 
    array_rev = array[:]
 
    array_rev.reverse()
 
    best = None
 
    for elt in array_rev:
 
        if elt >= x:
 
            best = elt
 
        elif best is not None:
 
            return best
 
    return best
 

	
 

	
light9/chase.py
Show inline comments
 

	
 

	
 

	
 
def chase(t,
 
          ontime=0.5,
 
          offset=0.2,
 
          onval=1.0,
 
          offval=0.0,
 
          names=None,
light9/showconfig.py
Show inline comments
 
@@ -4,41 +4,51 @@ from os import path, getenv
 
from rdflib import Graph
 
from rdflib import URIRef
 
from .namespaces import MUS, L9
 
log = logging.getLogger('showconfig')
 

	
 
_config = None # graph
 

	
 

	
 
def getGraph():
 
    warnings.warn("code that's using showconfig.getGraph should be "
 
                  "converted to use the sync graph", stacklevel=2)
 
    warnings.warn(
 
        "code that's using showconfig.getGraph should be "
 
        "converted to use the sync graph",
 
        stacklevel=2)
 
    global _config
 
    if _config is None:
 
        graph = Graph()
 
        # note that logging is probably not configured the first time
 
        # we're in here
 
        warnings.warn("reading n3 files around %r" % root())
 
        for f in FilePath(root()).globChildren("*.n3") + FilePath(root()).globChildren("build/*.n3"):
 
        for f in FilePath(root()).globChildren("*.n3") + FilePath(
 
                root()).globChildren("build/*.n3"):
 
            graph.parse(location=f.path, format='n3')
 
        _config = graph
 
    return _config
 

	
 

	
 
def root():
 
    r = getenv("LIGHT9_SHOW")
 
    if r is None:
 
        raise OSError(
 
            "LIGHT9_SHOW env variable has not been set to the show root")
 
    return r
 

	
 

	
 
_showUri = None
 

	
 

	
 
def showUri():
 
    """Return the show URI associated with $LIGHT9_SHOW."""
 
    global _showUri
 
    if _showUri is None:
 
        _showUri = URIRef(open(path.join(root(), 'URI')).read().strip())
 
    return _showUri
 

	
 

	
 
def songOnDisk(song):
 
    """given a song URI, where's the on-disk file that mpd would read?"""
 
    graph = getGraph()
 
    root = graph.value(showUri(), L9['musicRoot'])
 
    if not root:
 
        raise ValueError("%s has no :musicRoot" % showUri())
 
@@ -46,33 +56,38 @@ def songOnDisk(song):
 
    name = graph.value(song, L9['songFilename'])
 
    if not name:
 
        raise ValueError("Song %r has no :songFilename" % song)
 

	
 
    return path.abspath(path.join(root, name))
 

	
 

	
 
def songFilenameFromURI(uri):
 
    """
 
    'http://light9.bigasterisk.com/show/dance2007/song8' -> 'song8'
 

	
 
    everything that uses this should be deprecated for real URIs
 
    everywhere"""
 
    assert isinstance(uri, URIRef)
 
    return uri.split('/')[-1]
 

	
 

	
 
def getSongsFromShow(graph, show):
 
    playList = graph.value(show, L9['playList'])
 
    if not playList:
 
        raise ValueError("%r has no l9:playList" % show)
 
    # The patch in https://github.com/RDFLib/rdflib/issues/305 fixed a
 
    # serious bug here.
 
    songs = list(graph.items(playList))
 

	
 
    return songs
 

	
 

	
 
def curvesDir():
 
    return path.join(root(),"curves")
 

	
 

	
 
def subFile(subname):
 
    return path.join(root(),"subs",subname)
 

	
 

	
 
def subsDir():
 
    return path.join(root(),'subs')
light9/subclient.py
Show inline comments
 
@@ -2,13 +2,15 @@ from light9.collector.collector_client i
 
from twisted.internet import reactor, task
 
import traceback
 
import time
 
import logging
 
log = logging.getLogger()
 

	
 

	
 
class SubClient:
 

	
 
    def __init__(self):
 
        """assumed that your init saves self.graph"""
 
        pass # we may later need init code for network setup
 

	
 
    def get_levels_as_sub(self):
 
        """Subclasses must implement this method and return a Submaster
 
@@ -16,23 +18,25 @@ class SubClient:
 

	
 
    def send_levels(self):
 
        self._send_sub()
 

	
 
    def send_levels_loop(self, delay=1000):
 
        now = time.time()
 

	
 
        def done(sec):
 
            reactor.callLater(max(0, time.time() - (now + delay)),
 
            reactor.callLater(max(0,
 
                                  time.time() - (now + delay)),
 
                              self.send_levels_loop)
 

	
 
        def err(e):
 
            log.warn('subclient loop: %r', e)
 
            reactor.callLater(2, self.send_levels_loop)
 
            
 
        d = self._send_sub()
 
        d.addCallbacks(done, err)
 

	
 

	
 
    def _send_sub(self):
 
        try:
 
            with self.graph.currentState() as g:
 
                outputSettings = self.get_output_settings(_graph=g)
 
        except:
 
            traceback.print_exc()
light9/tkdnd.py
Show inline comments
 
from glob import glob
 
from os.path import join, basename
 

	
 

	
 
class TkdndEvent(object):
 
    """
 
    see http://www.ellogon.org/petasis/tcltk-projects/tkdnd/tkdnd-man-page
 
    for details on the fields
 

	
 
    The longer attribute names (action instead of %A) were made up for
 
@@ -36,13 +37,15 @@ class TkdndEvent(object):
 

	
 
    tclSubstitutions = ' '.join(sorted(substitutions.keys()))
 

	
 
    def __repr__(self):
 
        return "<TkdndEvent %r>" % self.__dict__
 

	
 

	
 
class Hover(object):
 

	
 
    def __init__(self, widget, style):
 
        self.widget, self.style = widget, style
 
        self.oldStyle = {}
 

	
 
    def set(self, ev):
 
        for k, v in list(self.style.items()):
 
@@ -50,27 +53,28 @@ class Hover(object):
 
        self.widget.configure(**self.style)
 
        return ev.action
 

	
 
    def restore(self, ev):
 
        self.widget.configure(**self.oldStyle)
 

	
 

	
 
def initTkdnd(tk, tkdndBuildDir):
 
    """
 
    pass the 'tk' attribute of any Tkinter object, and the top dir of
 
    your built tkdnd package
 
    """
 
    tk.call('source', join(tkdndBuildDir, 'library/tkdnd.tcl'))
 
    for dll in glob(join(tkdndBuildDir,
 
    for dll in glob(
 
            join(tkdndBuildDir,
 
                         '*tkdnd*' + tk.call('info', 'sharedlibextension'))):
 
        tk.call('tkdnd::initialise',
 
                join(tkdndBuildDir, 'library'),
 
                join('..', basename(dll)),
 
                'tkdnd')
 
        tk.call('tkdnd::initialise', join(tkdndBuildDir, 'library'),
 
                join('..', basename(dll)), 'tkdnd')
 

	
 
def dragSourceRegister(widget,
 
                       action='copy', datatype='text/uri-list', data=''):
 

	
 
def dragSourceRegister(widget, action='copy', datatype='text/uri-list',
 
                       data=''):
 
    """
 
    if the 'data' param is callable, it will be called every time to
 
    look up the current data.
 

	
 
    If the callable returns None (or data is None to begin with), the drag
 
    """
 
@@ -84,19 +88,23 @@ def dragSourceRegister(widget,
 
    def init():
 
        dataValue = data() if callable(data) else data
 
        if dataValue is None:
 
            return
 
        return (action, datatype, dataValue)
 

	
 
    funcId = widget._register(init,
 
    funcId = widget._register(
 
        init,
 
                              widget._substitute,
 
                              1 # needscleanup
 
                              )
 
    widget.bind("<<DragInitCmd>>", funcId)
 

	
 
def dropTargetRegister(widget, typeList=None,
 

	
 
def dropTargetRegister(
 
        widget,
 
        typeList=None,
 
                       onDropEnter=None,
 
                       onDropPosition=None,
 
                       onDropLeave=None,
 
                       onDrop=None,
 
                       hoverStyle=None,
 
                       ):
 
@@ -113,17 +121,20 @@ def dropTargetRegister(widget, typeList=
 
    widget.configure(**hoverStyle) and onDropLeave to restore the
 
    widget's style. onDrop is also wrapped to do a restore.
 
    """
 

	
 
    if hoverStyle is not None:
 
        hover = Hover(widget, hoverStyle)
 

	
 
        def wrappedDrop(ev):
 
            hover.restore(ev)
 
            if onDrop:
 
                return onDrop(ev)
 
        return dropTargetRegister(widget, typeList=typeList,
 

	
 
        return dropTargetRegister(widget,
 
                                  typeList=typeList,
 
                                  onDropEnter=hover.set,
 
                                  onDropLeave=hover.restore,
 
                                  onDropPosition=onDropPosition,
 
                                  onDrop=wrappedDrop)
 

	
 
    if typeList is None:
 
@@ -135,10 +146,10 @@ def dropTargetRegister(widget, typeList=
 
        ('<<DropPosition>>', onDropPosition),
 
        ('<<DropLeave>>', onDropLeave),
 
        ('<<Drop>>', onDrop),
 
        ]:
 
        if not handler:
 
            continue
 
        func = widget._register(handler, subst=TkdndEvent.makeEvent, needcleanup=1)
 
        func = widget._register(handler,
 
                                subst=TkdndEvent.makeEvent,
 
                                needcleanup=1)
 
        widget.bind(sequence, func + " " + TkdndEvent.tclSubstitutions)
 

	
 

	
light9/uihelpers.py
Show inline comments
 
"""all the tiny tk helper functions"""
 

	
 

	
 
#from Tkinter import Button
 
import logging, time
 
from rdflib import Literal
 
from tkinter.tix import Button, Toplevel, Tk, IntVar, Entry, DoubleVar
 
import tkinter
 
from light9.namespaces import L9
 
@@ -17,12 +16,13 @@ windowlocations = {
 
    'cuefader' : '314x212+546+741',
 
    'effect' : '24x24+0963+338',
 
    'stage' : '823x683+37+030',
 
    'scenes' : '504x198+462+12',
 
}
 

	
 

	
 
def bindkeys(root,key, func):
 
    root.bind(key, func)
 
    for w in root.winfo_children():
 
        w.bind(key, func)
 

	
 

	
 
@@ -34,19 +34,21 @@ def toplevel_savegeometry(tl,name):
 
            f.write(tl.geometry())
 
        # else the window never got mapped
 
    except Exception as e:
 
        # it's ok if there's no saved geometry
 
        pass
 

	
 

	
 
def toplevelat(name, existingtoplevel=None, graph=None, session=None):
 
    tl = existingtoplevel or Toplevel()
 
    tl.title(name)
 

	
 
    lastSaved = [None]
 
    setOnce = [False]
 
    graphSetTime = [0]
 

	
 
    def setPosFromGraphOnce():
 
        """
 
        the graph is probably initially empty, but as soon as it gives
 
        us one window position, we stop reading them
 
        """
 
        if setOnce[0]:
 
@@ -80,17 +82,18 @@ def toplevelat(name, existingtoplevel=No
 
        graph.addHandler(setPosFromGraphOnce)
 

	
 
    if name in windowlocations:
 
        tl.geometry(positionOnCurrentDesktop(windowlocations[name]))
 

	
 
    if graph is not None:
 
        tl._toplevelat_funcid = tl.bind("<Configure>",
 
                                        lambda ev,tl=tl,name=name: savePos(ev))
 
        tl._toplevelat_funcid = tl.bind(
 
            "<Configure>", lambda ev, tl=tl, name=name: savePos(ev))
 

	
 
    return tl
 

	
 

	
 
def positionOnCurrentDesktop(xform, screenWidth=1920, screenHeight=1440):
 
    size, x, y = xform.split('+')
 
    x = int(x) % screenWidth
 
    y = int(y) % screenHeight
 
    return "%s+%s+%s" % (size, x, y)
 

	
 
@@ -98,65 +101,80 @@ def positionOnCurrentDesktop(xform, scre
 
def toggle_slider(s):
 
    if s.get() == 0:
 
        s.set(100)
 
    else:
 
        s.set(0)
 

	
 

	
 
# for lambda callbacks
 
def printout(t):
 
    print('printout', t)
 

	
 

	
 
def printevent(ev):
 
    for k in dir(ev):
 
        if not k.startswith('__'):
 
            print('ev', k, getattr(ev,k))
 

	
 

	
 
def eventtoparent(ev,sequence):
 
    "passes an event to the parent, screws up TixComboBoxes"
 

	
 
    wid_class = str(ev.widget.__class__)
 
    if wid_class == 'Tix.ComboBox' or wid_class == 'Tix.TixSubWidget':
 
        return
 

	
 
    evdict={}
 
    for x in ['state', 'time', 'y', 'x', 'serial']:
 
        evdict[x]=getattr(ev,x)
 

	
 

	
 
#    evdict['button']=ev.num
 
    par=ev.widget.winfo_parent()
 
    if par!=".":
 
        ev.widget.nametowidget(par).event_generate(sequence,**evdict)
 
    #else the event made it all the way to the top, unhandled
 

	
 

	
 
def colorlabel(label):
 
    """color a label based on its own text"""
 
    txt=label['text'] or "0"
 
    lev=float(txt)/100
 
    low=(80,80,180)
 
    high=(255,55,0o50)
 
    out = [int(l+lev*(h-l)) for h,l in zip(high,low)]
 
    col="#%02X%02X%02X" % tuple(out)
 
    label.config(bg=col)
 

	
 

	
 
# TODO: get everyone to use this
 
def colorfade(low, high, percent):
 
    '''not foolproof.  make sure 0 < percent < 1'''
 
    out = [int(l+percent*(h-l)) for h,l in zip(high,low)]
 
    col="#%02X%02X%02X" % tuple(out)
 
    return col
 

	
 

	
 
def colortotuple(anytkobj, colorname):
 
    'pass any tk object and a color name, like "yellow"'
 
    rgb = anytkobj.winfo_rgb(colorname)
 
    return [v / 256 for v in rgb]
 

	
 

	
 
class Togglebutton(Button):
 
    """works like a single radiobutton, but it's a button so the
 
    label's on the button face, not to the side. the optional command
 
    callback is called on button set, not on unset. takes a variable
 
    just like a checkbutton"""
 
    def __init__(self,parent,variable=None,command=None,downcolor='red',**kw):
 

	
 
    def __init__(self,
 
                 parent,
 
                 variable=None,
 
                 command=None,
 
                 downcolor='red',
 
                 **kw):
 

	
 
        self.oldcommand = command
 
        Button.__init__(self,parent,command=self.invoke,**kw)
 

	
 
        self._origbkg = self.cget('bg')
 
        self.downcolor = downcolor
 
@@ -192,16 +210,18 @@ class Togglebutton(Button):
 
        else: # unset
 
            self.config(bg=self._origbkg,relief='raised')
 
        return "break"
 

	
 

	
 
class FancyDoubleVar(DoubleVar):
 

	
 
    def __init__(self,master=None):
 
        DoubleVar.__init__(self,master)
 
        self.callbacklist = {} # cbname : mode
 
        self.namedtraces = {} # name : cbname
 

	
 
    def trace_variable(self,mode,callback):
 
        """Define a trace callback for the variable.
 

	
 
        MODE is one of "r", "w", "u" for read, write, undefine.
 
        CALLBACK must be a function which is called when
 
        the variable is read, written or undefined.
 
@@ -213,13 +233,15 @@ class FancyDoubleVar(DoubleVar):
 

	
 
        # we build a list of the trace callbacks (the py functrions and the tcl functionnames)
 
        self.callbacklist[cbname] = mode
 
#        print "added trace:",callback,cbname
 

	
 
        return cbname
 

	
 
    trace=trace_variable
 

	
 
    def disable_traces(self):
 
        for cb,mode in list(self.callbacklist.items()):
 
#            DoubleVar.trace_vdelete(self,v[0],k)
 
            self._tk.call("trace", "vdelete", self._name, mode,cb)
 
            # but no master delete!
 

	
 
@@ -227,16 +249,19 @@ class FancyDoubleVar(DoubleVar):
 
        for cb,mode in list(self.callbacklist.items()):
 
#            self.trace_variable(v[0],v[1])
 
            self._tk.call("trace", "variable", self._name, mode,cb)
 

	
 
    def trace_named(self, name, callback):
 
        if name in self.namedtraces:
 
            print("FancyDoubleVar: already had a trace named %s - replacing it" % name)
 
            print(
 
                "FancyDoubleVar: already had a trace named %s - replacing it" %
 
                name)
 
            self.delete_named(name)
 

	
 
        cbname = self.trace_variable('w',callback) # this will register in self.callbacklist too
 
        cbname = self.trace_variable(
 
            'w', callback)  # this will register in self.callbacklist too
 

	
 
        self.namedtraces[name] = cbname
 
        return cbname
 

	
 
    def delete_named(self, name):
 
        if name in self.namedtraces:
 
@@ -244,23 +269,29 @@ class FancyDoubleVar(DoubleVar):
 
            cbname = self.namedtraces[name]
 

	
 
            self.trace_vdelete('w',cbname)
 
	    #self._tk.call("trace","vdelete",self._name,'w',cbname)
 
            print("FancyDoubleVar: successfully deleted trace named %s" % name)
 
        else:
 
            print("FancyDoubleVar: attempted to delete named %s which wasn't set to any function" % name)
 
            print(
 
                "FancyDoubleVar: attempted to delete named %s which wasn't set to any function"
 
                % name)
 

	
 

	
 
def get_selection(listbox):
 
    'Given a listbox, returns first selection as integer'
 
    selection = int(listbox.curselection()[0]) # blech
 
    return selection
 

	
 

	
 
if __name__=='__main__':
 
    root=Tk()
 
    root.tk_focusFollowsMouse()
 
    iv=IntVar()
 

	
 
    def cb():
 
        print("cb!")
 

	
 
    t = Togglebutton(root,text="testbutton",command=cb,variable=iv)
 
    t.pack()
 
    Entry(root,textvariable=iv).pack()
 
    root.mainloop()
light9/updatefreq.py
Show inline comments
 
"""calculates your updates-per-second"""
 

	
 
import time
 

	
 
import time
 

	
 
class Updatefreq:
 
    """make one of these, call update() on it as much as you want,
 
    and then float() or str() the object to learn the updates per second.
 

	
 
    the samples param to __init__ specifies how many past updates will
 
@@ -12,23 +12,22 @@ class Updatefreq:
 
    
 
    def __init__(self,samples=20):
 
        self.times=[0]
 
        self.samples=samples
 

	
 
    def update(self):
 

	
 
        """call this every time you do an update"""
 
        self.times=self.times[-self.samples:]
 
        self.times.append(time.time())
 

	
 
    def __float__(self):
 
        
 
        """a cheap algorithm, for now, which looks at the first and
 
        last times only"""
 

	
 
        try:
 
            hz=len(self.times)/(self.times[-1]-self.times[0])
 
        except ZeroDivisionError:
 
            return 0.0
 
        return hz
 

	
 
    def __str__(self):
 
        return "%.2fHz"%float(self)
light9/wavelength.py
Show inline comments
 
#!/usr/bin/python
 

	
 
import sys, wave
 

	
 
import sys, wave
 

	
 
def wavelength(filename):
 
    filename = filename.replace('.ogg', '.wav')
 
    wavefile = wave.open(filename, 'rb')
 

	
 
    framerate = wavefile.getframerate() # frames / second
 
    nframes = wavefile.getnframes() # number of frames
 
    song_length = nframes / framerate
 

	
 
    return song_length
 

	
 

	
 
if __name__ == "__main__":
 
    for songfile in sys.argv[1:]:
 
        print(songfile, wavelength(songfile))
light9/wavepoints.py
Show inline comments
 
import wave, audioop
 

	
 
import wave, audioop
 

	
 
def simp(filename, seconds_per_average=0.001):
 
    """smaller seconds_per_average means fewer data points"""
 
    wavefile = wave.open(filename, 'rb')
 
    print("# gnuplot data for %s, seconds_per_average=%s" % \
 
        (filename, seconds_per_average))
 
    print("# %d channels, samplewidth: %d, framerate: %s, frames: %d\n# Compression type: %s (%s)" % wavefile.getparams())
 
    print(
 
        "# %d channels, samplewidth: %d, framerate: %s, frames: %d\n# Compression type: %s (%s)"
 
        % wavefile.getparams())
 

	
 
    framerate = wavefile.getframerate() # frames / second
 
    frames_to_read = int(framerate * seconds_per_average)
 
    print("# frames_to_read=%s" % frames_to_read)
 

	
 
    time_and_max = []
 
@@ -32,9 +34,9 @@ def simp(filename, seconds_per_average=0
 
            # break
 

	
 
    # find the min and max
 
    min_value, max_value = min(values), max(values)
 
    points = [] # (secs,height)
 
    for count, value in time_and_max:
 
        points.append((count/framerate,
 
                       (value - min_value) / (max_value - min_value)))
 
        points.append(
 
            (count / framerate, (value - min_value) / (max_value - min_value)))
 
    return points
0 comments (0 inline, 0 general)