diff --git a/bin/run_local.py b/bin/run_local.py --- a/bin/run_local.py +++ b/bin/run_local.py @@ -17,12 +17,12 @@ def fixSysPath(): root + 'env/lib/python3.7/lib-dynload', '/usr/lib/python3/dist-packages/', '/usr/lib/python3.7', -# '/usr/lib/python3.7/plat-x86_64-linux-gnu', -# '/usr/lib/python3.7/lib-tk', -# root + 'env/local/lib/python3.7/site-packages', -# root + 'env/local/lib/python3.7/site-packages/gtk-2.0', + # '/usr/lib/python3.7/plat-x86_64-linux-gnu', + # '/usr/lib/python3.7/lib-tk', + # root + 'env/local/lib/python3.7/site-packages', + # root + 'env/local/lib/python3.7/site-packages/gtk-2.0', root + 'env/lib/python3.7/site-packages', -# root + 'env/lib/python3.7/site-packages/gtk-2.0', + # root + 'env/lib/python3.7/site-packages/gtk-2.0', ] diff --git a/light9/Effects.py b/light9/Effects.py --- a/light9/Effects.py +++ b/light9/Effects.py @@ -1,4 +1,3 @@ - import random as random_mod import math import logging, colorsys diff --git a/light9/FlyingFader.py b/light9/FlyingFader.py --- a/light9/FlyingFader.py +++ b/light9/FlyingFader.py @@ -2,7 +2,6 @@ from tkinter.tix import * from time import time, sleep - class Mass: def __init__(self): diff --git a/light9/Submaster.py b/light9/Submaster.py --- a/light9/Submaster.py +++ b/light9/Submaster.py @@ -1,4 +1,3 @@ - import os, logging, time from rdflib import Graph, RDF from rdflib import RDFS, Literal, BNode @@ -10,8 +9,10 @@ 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. @@ -51,7 +52,9 @@ class Submaster(object): 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) @@ -76,7 +79,7 @@ class Submaster(object): return hash(self.ident()) def get_dmx_list(self): - leveldict = self.get_levels() # gets levels of sub contents + leveldict = self.get_levels() # gets levels of sub contents levels = [] for k, v in list(leveldict.items()): @@ -85,8 +88,9 @@ class Submaster(object): 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)) @@ -119,14 +123,16 @@ class Submaster(object): 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), - amount)) + 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") @@ -144,7 +150,7 @@ class PersistentSubmaster(Submaster): def changeName(self, newName): self.graph.patchObject(self.uri, self.uri, RDFS.label, Literal(newName)) - + def setName(self): log.info("sub update name %s %s", self.uri, self.graph.label(self.uri)) self.name = self.graph.label(self.uri) @@ -193,23 +199,27 @@ class PersistentSubmaster(Submaster): 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" % - list(current.contextsForStatement(typeStmt))) + 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.uri, RDF.type, L9['Submaster'], ctx), + 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)) @@ -225,16 +235,15 @@ class PersistentSubmaster(Submaster): quads = [] with self.graph.currentState() as current: quads.extend(current.quads((self.uri, None, None))) - for s,p,o,c in quads: + 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) + log.info("not saving temporary sub named %s", self.name) return graph = Graph() @@ -265,12 +274,14 @@ def linear_fade(start, end, amount): 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 @@ -287,10 +298,12 @@ def combine_subdict(subdict, name=None, return maxes + class Submasters(object): "Collection o' Submaster objects" + def __init__(self, graph): - self.submasters = {} # uri : Submaster + self.submasters = {} # uri : Submaster self.graph = graph graph.addHandler(self.findSubs) @@ -332,9 +345,11 @@ class Submasters(object): 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 @@ -346,6 +361,7 @@ def get_global_submasters(graph): _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 @@ -359,14 +375,14 @@ def get_sub_by_name(name, submasters=Non try: val = int(name) - s = Submaster("#%d" % val, levels={val : 1.0}) + s = Submaster("#%d" % val, levels={val: 1.0}) return s except ValueError: pass try: subnum = get_dmx_channel(name) - s = Submaster("'%s'" % name, levels={subnum : 1.0}) + s = Submaster("'%s'" % name, levels={subnum: 1.0}) return s except ValueError: pass diff --git a/light9/TLUtility.py b/light9/TLUtility.py --- a/light9/TLUtility.py +++ b/light9/TLUtility.py @@ -1,13 +1,13 @@ """Collected utility functions, many are taken from Drew's utils.py in Cuisine CVS and Hiss's Utility.py.""" - import sys __author__ = "David McClosky , " + \ "Drew Perttula " __cvsid__ = "$Id: TLUtility.py,v 1.1 2003/05/25 08:25:35 dmcc Exp $" -__version__ = "$Revision: 1.1 $"[11:-2] +__version__ = "$Revision: 1.1 $" [11:-2] + def make_attributes_from_args(*argnames): """ @@ -25,15 +25,16 @@ def make_attributes_from_args(*argnames) self.baz=baz ... """ - - callerlocals=sys._getframe(1).f_locals - callerself=callerlocals['self'] + + callerlocals = sys._getframe(1).f_locals + callerself = callerlocals['self'] for a in argnames: try: - setattr(callerself,a,callerlocals[a]) + 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]) ... @@ -43,9 +44,12 @@ def enumerate(*collections): 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)) @@ -56,6 +60,7 @@ def dumpobj(o): pass print("") + def dict_filter_update(d, **newitems): """Adds a set of new keys and values to dictionary 'd' if the values are true: @@ -68,6 +73,7 @@ def dict_filter_update(d, **newitems): 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.""" @@ -78,6 +84,7 @@ def try_get_logger(channel): 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. @@ -96,9 +103,11 @@ class DummyClass: 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.""" @@ -111,21 +120,27 @@ class DummyClass: 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: @@ -145,6 +160,7 @@ def trace(func): 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: @@ -155,8 +171,10 @@ def trace(func): 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): """ @@ -165,22 +183,25 @@ def dict_max(*dicts): """ newdict = {} for d in dicts: - for k,v in list(d.items()): + for k, v in list(d.items()): newdict[k] = max(v, newdict.get(k, 0)) return newdict -def dict_scale(d,scl): + +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())]) - + 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! + 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): @@ -193,6 +214,7 @@ def last_less_than(array, x): return best return best + # TBD def first_greater_than(array, x): """array must be sorted""" @@ -205,5 +227,3 @@ def first_greater_than(array, x): elif best is not None: return best return best - - diff --git a/light9/chase.py b/light9/chase.py --- a/light9/chase.py +++ b/light9/chase.py @@ -1,6 +1,3 @@ - - - def chase(t, ontime=0.5, offset=0.2, diff --git a/light9/showconfig.py b/light9/showconfig.py --- a/light9/showconfig.py +++ b/light9/showconfig.py @@ -6,21 +6,27 @@ from rdflib import URIRef from .namespaces import MUS, L9 log = logging.getLogger('showconfig') -_config = None # graph +_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: @@ -28,7 +34,10 @@ def root(): "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 @@ -36,6 +45,7 @@ def showUri(): _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() @@ -49,6 +59,7 @@ def songOnDisk(song): return path.abspath(path.join(root, name)) + def songFilenameFromURI(uri): """ 'http://light9.bigasterisk.com/show/dance2007/song8' -> 'song8' @@ -58,6 +69,7 @@ def songFilenameFromURI(uri): assert isinstance(uri, URIRef) return uri.split('/')[-1] + def getSongsFromShow(graph, show): playList = graph.value(show, L9['playList']) if not playList: @@ -68,11 +80,14 @@ def getSongsFromShow(graph, show): return songs + def curvesDir(): - return path.join(root(),"curves") + return path.join(root(), "curves") + def subFile(subname): - return path.join(root(),"subs",subname) + return path.join(root(), "subs", subname) + def subsDir(): - return path.join(root(),'subs') + return path.join(root(), 'subs') diff --git a/light9/subclient.py b/light9/subclient.py --- a/light9/subclient.py +++ b/light9/subclient.py @@ -5,10 +5,12 @@ 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 + 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 @@ -19,17 +21,19 @@ class SubClient: 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: diff --git a/light9/tkdnd.py b/light9/tkdnd.py --- a/light9/tkdnd.py +++ b/light9/tkdnd.py @@ -1,6 +1,7 @@ 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 @@ -14,15 +15,15 @@ class TkdndEvent(object): unnecessarily change their types later. """ substitutions = { - "%A" : "action", - "%b" : "button", - "%D" : "data", - "%m" : "modifiers", - "%T" : "type", - "%W" : "targetWindow", - "%X" : "mouseX", - "%Y" : "mouseY", - } + "%A": "action", + "%b": "button", + "%D": "data", + "%m": "modifiers", + "%T": "type", + "%W": "targetWindow", + "%X": "mouseX", + "%Y": "mouseY", + } @classmethod def makeEvent(cls, *args): @@ -39,7 +40,9 @@ class TkdndEvent(object): def __repr__(self): return "" % self.__dict__ + class Hover(object): + def __init__(self, widget, style): self.widget, self.style = widget, style self.oldStyle = {} @@ -53,21 +56,22 @@ class Hover(object): 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, - '*tkdnd*' + tk.call('info', 'sharedlibextension'))): - tk.call('tkdnd::initialise', - join(tkdndBuildDir, 'library'), - join('..', basename(dll)), - 'tkdnd') + for dll in glob( + join(tkdndBuildDir, + '*tkdnd*' + tk.call('info', 'sharedlibextension'))): + 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. @@ -87,19 +91,23 @@ def dragSourceRegister(widget, return return (action, datatype, dataValue) - funcId = widget._register(init, - widget._substitute, - 1 # needscleanup - ) + funcId = widget._register( + init, + widget._substitute, + 1 # needscleanup + ) widget.bind("<>", funcId) -def dropTargetRegister(widget, typeList=None, - onDropEnter=None, - onDropPosition=None, - onDropLeave=None, - onDrop=None, - hoverStyle=None, - ): + +def dropTargetRegister( + widget, + typeList=None, + onDropEnter=None, + onDropPosition=None, + onDropLeave=None, + onDrop=None, + hoverStyle=None, +): """ the optional callbacks will be called with a TkdndEvent argument. @@ -116,11 +124,14 @@ def dropTargetRegister(widget, typeList= 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, @@ -128,17 +139,17 @@ def dropTargetRegister(widget, typeList= if typeList is None: typeList = ['*'] - widget.tk.call(*(['tkdnd::drop_target', 'register', widget._w]+typeList)) + widget.tk.call(*(['tkdnd::drop_target', 'register', widget._w] + typeList)) for sequence, handler in [ ('<>', onDropEnter), ('<>', onDropPosition), ('<>', onDropLeave), ('<>', 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) - - diff --git a/light9/uihelpers.py b/light9/uihelpers.py --- a/light9/uihelpers.py +++ b/light9/uihelpers.py @@ -1,6 +1,5 @@ """all the tiny tk helper functions""" - #from Tkinter import Button import logging, time from rdflib import Literal @@ -11,32 +10,34 @@ from light9.namespaces import L9 log = logging.getLogger("toplevel") windowlocations = { - 'sub' : '425x738+00+00', - 'console' : '168x24+848+000', - 'leveldisplay' : '144x340+870+400', - 'cuefader' : '314x212+546+741', - 'effect' : '24x24+0963+338', - 'stage' : '823x683+37+030', - 'scenes' : '504x198+462+12', + 'sub': '425x738+00+00', + 'console': '168x24+848+000', + 'leveldisplay': '144x340+870+400', + 'cuefader': '314x212+546+741', + 'effect': '24x24+0963+338', + 'stage': '823x683+37+030', + 'scenes': '504x198+462+12', } -def bindkeys(root,key, func): + +def bindkeys(root, key, func): root.bind(key, func) for w in root.winfo_children(): w.bind(key, func) -def toplevel_savegeometry(tl,name): +def toplevel_savegeometry(tl, name): try: geo = tl.geometry() if not geo.startswith("1x1"): - f=open(".light9-window-geometry-%s" % name.replace(' ','_'),'w') + f = open(".light9-window-geometry-%s" % name.replace(' ', '_'), 'w') 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) @@ -44,6 +45,7 @@ def toplevelat(name, existingtoplevel=No lastSaved = [None] setOnce = [False] graphSetTime = [0] + def setPosFromGraphOnce(): """ the graph is probably initially empty, but as soon as it gives @@ -83,11 +85,12 @@ def toplevelat(name, existingtoplevel=No tl.geometry(positionOnCurrentDesktop(windowlocations[name])) if graph is not None: - tl._toplevelat_funcid = tl.bind("", - lambda ev,tl=tl,name=name: savePos(ev)) + tl._toplevelat_funcid = tl.bind( + "", 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 @@ -101,105 +104,122 @@ def toggle_slider(s): 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)) + print('ev', k, getattr(ev, k)) -def eventtoparent(ev,sequence): + +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={} + evdict = {} for x in ['state', 'time', 'y', 'x', 'serial']: - evdict[x]=getattr(ev,x) + evdict[x] = getattr(ev, x) + + # evdict['button']=ev.num - par=ev.widget.winfo_parent() - if par!=".": - ev.widget.nametowidget(par).event_generate(sequence,**evdict) + 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) + 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) + 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) + Button.__init__(self, parent, command=self.invoke, **kw) self._origbkg = self.cget('bg') self.downcolor = downcolor self._variable = variable if self._variable: - self._variable.trace('w',self._varchanged) + self._variable.trace('w', self._varchanged) self._setstate(self._variable.get()) else: self._setstate(0) - self.bind("",self.invoke) - self.bind("<1>",self.invoke) - self.bind("",self.invoke) + self.bind("", self.invoke) + self.bind("<1>", self.invoke) + self.bind("", self.invoke) - def _varchanged(self,*args): + def _varchanged(self, *args): self._setstate(self._variable.get()) - def invoke(self,*ev): + def invoke(self, *ev): if self._variable: self._variable.set(not self.state) else: self._setstate(not self.state) - if self.oldcommand and self.state: # call command only when state goes to 1 + if self.oldcommand and self.state: # call command only when state goes to 1 self.oldcommand() return "break" - def _setstate(self,newstate): + def _setstate(self, newstate): self.state = newstate - if newstate: # set - self.config(bg=self.downcolor,relief='sunken') - else: # unset - self.config(bg=self._origbkg,relief='raised') + if newstate: # set + self.config(bg=self.downcolor, relief='sunken') + 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): + + 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. @@ -213,27 +233,32 @@ 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 + # print "added trace:",callback,cbname return cbname - trace=trace_variable + + 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) + 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! def recreate_traces(self): - for cb,mode in list(self.callbacklist.items()): -# self.trace_variable(v[0],v[1]) - self._tk.call("trace", "variable", self._name, mode,cb) + 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 @@ -243,24 +268,30 @@ class FancyDoubleVar(DoubleVar): cbname = self.namedtraces[name] - self.trace_vdelete('w',cbname) - #self._tk.call("trace","vdelete",self._name,'w',cbname) + 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 + selection = int(listbox.curselection()[0]) # blech return selection -if __name__=='__main__': - root=Tk() + +if __name__ == '__main__': + root = Tk() root.tk_focusFollowsMouse() - iv=IntVar() + iv = IntVar() + def cb(): print("cb!") - t = Togglebutton(root,text="testbutton",command=cb,variable=iv) + + t = Togglebutton(root, text="testbutton", command=cb, variable=iv) t.pack() - Entry(root,textvariable=iv).pack() + Entry(root, textvariable=iv).pack() root.mainloop() diff --git a/light9/updatefreq.py b/light9/updatefreq.py --- a/light9/updatefreq.py +++ b/light9/updatefreq.py @@ -1,7 +1,7 @@ """calculates your updates-per-second""" +import time -import time class Updatefreq: """make one of these, call update() on it as much as you want, @@ -9,26 +9,25 @@ class Updatefreq: the samples param to __init__ specifies how many past updates will be stored. """ - - def __init__(self,samples=20): - self.times=[0] - self.samples=samples + + 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 = 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]) + hz = len(self.times) / (self.times[-1] - self.times[0]) except ZeroDivisionError: return 0.0 return hz + def __str__(self): - return "%.2fHz"%float(self) + return "%.2fHz" % float(self) diff --git a/light9/wavelength.py b/light9/wavelength.py --- a/light9/wavelength.py +++ b/light9/wavelength.py @@ -1,18 +1,19 @@ #!/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 + 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)) diff --git a/light9/wavepoints.py b/light9/wavepoints.py --- a/light9/wavepoints.py +++ b/light9/wavepoints.py @@ -1,14 +1,16 @@ +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 + framerate = wavefile.getframerate() # frames / second frames_to_read = int(framerate * seconds_per_average) print("# frames_to_read=%s" % frames_to_read) @@ -29,12 +31,12 @@ def simp(filename, seconds_per_average=0 values.append(m) count += frames_to_read # if count>1000000: - # break + # break # find the min and max min_value, max_value = min(values), max(values) - points = [] # (secs,height) + 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