diff --git a/bin/ascoltami2 b/bin/ascoltami2 --- a/bin/ascoltami2 +++ b/bin/ascoltami2 @@ -1,7 +1,7 @@ #!bin/python from run_local import log from twisted.internet import reactor -import web, _thread, sys, optparse, logging +import sys, optparse, logging from rdflib import URIRef import gi gi.require_version('Gst', '1.0') @@ -12,7 +12,7 @@ from light9.ascoltami.playlist import Pl from light9.ascoltami.webapp import makeWebApp, songUri, songLocation from light9 import networking, showconfig -from gi.repository import GObject, Gst, Gtk +from gi.repository import GObject, Gst class App(object): diff --git a/bin/captureDevice b/bin/captureDevice --- a/bin/captureDevice +++ b/bin/captureDevice @@ -23,6 +23,7 @@ from light9.greplin_cyclone import Stats from light9.effect.settings import DeviceSettings from light9.collector.collector_client import sendToCollector from rdfdb.patch import Patch +from light9.zmqtransport import parseJsonMessage stats = scales.collection('/webServer', scales.PmfStat('setAttr')) @@ -174,7 +175,8 @@ def launch(graph): }), (r'/stats', StatsForCyclone), ]), - interface='::') + interface='::', + cap=cap) log.info('serving http on %s', networking.captureDevice.port) diff --git a/bin/collector b/bin/collector --- a/bin/collector +++ b/bin/collector @@ -9,127 +9,25 @@ Input can be over http or zmq. from run_local import log -from rdflib import URIRef, Literal from twisted.internet import reactor, utils -from txzmq import ZmqEndpoint, ZmqFactory, ZmqPullConnection import json import logging import optparse -import time import traceback import cyclone.web, cyclone.websocket from greplin import scales from cycloneerr import PrettyErrorHandler -from light9.collector.output import EnttecDmx, Udmx, DummyOutput +from light9 import networking from light9.collector.collector import Collector -from light9.namespaces import L9 -from light9 import networking -from rdfdb.syncedgraph import SyncedGraph +from light9.collector.weblisteners import WebListeners from light9.greplin_cyclone import StatsForCyclone - - -def parseJsonMessage(msg): - body = json.loads(msg) - settings = [] - for device, attr, value in body['settings']: - if isinstance(value, str) and value.startswith('http'): - value = URIRef(value) - else: - value = Literal(value) - settings.append((URIRef(device), URIRef(attr), value)) - return body['client'], body['clientSession'], settings, body['sendTime'] - - -def startZmq(port, collector): - stats = scales.collection('/zmqServer', scales.PmfStat('setAttr')) - - zf = ZmqFactory() - addr = 'tcp://*:%s' % port - log.info('creating zmq endpoint at %r', addr) - e = ZmqEndpoint('bind', addr) - - class Pull(ZmqPullConnection): - #highWaterMark = 3 - def onPull(self, message): - with stats.setAttr.time(): - # todo: new compressed protocol where you send all URIs up - # front and then use small ints to refer to devices and - # attributes in subsequent requests. - client, clientSession, settings, sendTime = parseJsonMessage( - message[0]) - collector.setAttrs(client, clientSession, settings, sendTime) - - s = Pull(zf, e) - - -class WebListeners(object): - - def __init__(self): - self.clients = [] - self.pendingMessageForDev = {} # dev: (attrs, outputmap) - self.lastFlush = 0 - - def addClient(self, client): - self.clients.append([client, {}]) # seen = {dev: attrs} - log.info('added client %s %s', len(self.clients), client) +from light9.namespaces import L9 +from light9.zmqtransport import parseJsonMessage, startZmq +from rdfdb.syncedgraph import SyncedGraph - def delClient(self, client): - self.clients = [[c, t] for c, t in self.clients if c != client] - log.info('delClient %s, %s left', client, len(self.clients)) - - def outputAttrsSet(self, dev, attrs, outputMap): - """called often- don't be slow""" - - self.pendingMessageForDev[dev] = (attrs, outputMap) - try: - self._flush() - except Exception: - traceback.print_exc() - raise - - def _flush(self): - now = time.time() - if now < self.lastFlush + .05 or not self.clients: - return - self.lastFlush = now - - while self.pendingMessageForDev: - dev, (attrs, outputMap) = self.pendingMessageForDev.popitem() - - msg = None # lazy, since makeMsg is slow +from light9.collector.output import EnttecDmx, Udmx, DummyOutput # noqa - # this omits repeats, but can still send many - # messages/sec. Not sure if piling up messages for the browser - # could lead to slowdowns in the real dmx output. - for client, seen in self.clients: - if seen.get(dev) == attrs: - continue - if msg is None: - msg = self.makeMsg(dev, attrs, outputMap) - - seen[dev] = attrs - client.sendMessage(msg) - - def makeMsg(self, dev, attrs, outputMap): - attrRows = [] - for attr, val in list(attrs.items()): - output, index = outputMap[(dev, attr)] - attrRows.append({ - 'attr': attr.rsplit('/')[-1], - 'val': val, - 'chan': (output.shortId(), index + 1) - }) - attrRows.sort(key=lambda r: r['chan']) - for row in attrRows: - row['chan'] = '%s %s' % (row['chan'][0], row['chan'][1]) - - msg = json.dumps({'outputAttrsSet': { - 'dev': dev, - 'attrs': attrRows - }}, - sort_keys=True) - return msg class Updates(cyclone.websocket.WebSocketHandler): @@ -163,10 +61,8 @@ def launch(graph, doLoadTest=False): try: # todo: drive outputs with config files outputs = [ - # EnttecDmx(L9['output/dmxA/'], '/dev/dmx3', 80), - Udmx(L9['output/dmxA/'], bus=5, numChannels=80), - #DummyOutput(L9['output/dmxA/'], 80), - Udmx(L9['output/dmxB/'], bus=7, numChannels=500), + DummyOutput(L9['output/dmxA/'], 80), + DummyOutput(L9['output/dmxB/'], 510), ] except Exception: log.error("setting up outputs:") diff --git a/bin/collector_loadtest.py b/bin/collector_loadtest.py --- a/bin/collector_loadtest.py +++ b/bin/collector_loadtest.py @@ -1,7 +1,7 @@ import sys sys.path.append('bin') from run_local import log -from light9.collector.collector_client import sendToCollector, sendToCollectorZmq +from light9.collector.collector_client import sendToCollector from light9.namespaces import L9, DEV from twisted.internet import reactor import time diff --git a/bin/effecteval b/bin/effecteval --- a/bin/effecteval +++ b/bin/effecteval @@ -4,7 +4,7 @@ from run_local import log from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks, returnValue import cyclone.web, cyclone.websocket, cyclone.httpclient -import sys, optparse, logging, subprocess, json, itertools +import sys, optparse, logging, json, itertools from rdflib import URIRef, Literal sys.path.append('/usr/lib/pymodules/python2.7/') # for numpy, on rpi @@ -20,6 +20,7 @@ from rdfdb.syncedgraph import SyncedGrap from greplin import scales from cycloneerr import PrettyErrorHandler +from light9.coffee import StaticCoffee class EffectEdit(PrettyErrorHandler, cyclone.web.RequestHandler): @@ -201,9 +202,9 @@ class SongEffectsEval(PrettyErrorHandler def get(self): song = URIRef(self.get_argument('song')) - effects = effectsForSong(self.settings.graph, song) + effects = effectsForSong(self.settings.graph, song) # noqa raise NotImplementedError - self.write(maxDict(effectDmxDict(e) for e in effects)) + self.write(maxDict(effectDmxDict(e) for e in effects)) # noqa # return dmx dict for all effects in the song, already combined @@ -259,19 +260,6 @@ class App(object): log.info("listening on %s" % networking.effectEval.port) -class StaticCoffee(PrettyErrorHandler, cyclone.web.RequestHandler): - - def initialize(self, src): - super(StaticCoffee, self).initialize() - self.src = src - - def get(self): - self.set_header('Content-Type', 'application/javascript') - self.write( - subprocess.check_output( - ['/usr/bin/coffee', '--compile', '--print', self.src])) - - if __name__ == "__main__": parser = optparse.OptionParser() parser.add_option( diff --git a/bin/homepageConfig b/bin/homepageConfig --- a/bin/homepageConfig +++ b/bin/homepageConfig @@ -1,14 +1,11 @@ #!bin/python from run_local import log -from rdflib import RDF, URIRef -from light9 import networking, showconfig +from light9 import showconfig from light9.namespaces import L9 from urllib.parse import urlparse from urllib.parse import splitport -from rdfdb.syncedgraph import SyncedGraph -from twisted.internet import reactor - +log.info('generating config') graph = showconfig.getGraph() netHome = graph.value(showconfig.showUri(), L9['networking']) diff --git a/bin/inputdemo b/bin/inputdemo --- a/bin/inputdemo +++ b/bin/inputdemo @@ -5,13 +5,12 @@ from twisted.internet import gtk3reactor gtk3reactor.install() from twisted.internet import reactor from rdflib import URIRef -import optparse, logging, urllib.request, urllib.parse, urllib.error, time +import optparse, logging, time from gi.repository import Gtk from run_local import log -from light9 import showconfig, networking +from light9 import networking from light9 import clientsession from rdfdb.syncedgraph import SyncedGraph -import cyclone.httpclient from light9.curvecalc.client import sendLiveInputPoint diff --git a/bin/inputquneo b/bin/inputquneo --- a/bin/inputquneo +++ b/bin/inputquneo @@ -9,7 +9,7 @@ import cyclone.web, cyclone.httpclient from rdflib import URIRef from twisted.internet import reactor, task from light9.curvecalc.client import sendLiveInputPoint -from light9.namespaces import L9, RDF, RDFS +from light9.namespaces import L9, RDF from rdfdb.syncedgraph import SyncedGraph from light9 import networking @@ -109,6 +109,7 @@ def main(): graph = SyncedGraph(networking.rdfdb.url, "inputQuneo") wm = WatchMidi(graph) reactor.run() + del wm main() diff --git a/bin/keyboardcomposer b/bin/keyboardcomposer --- a/bin/keyboardcomposer +++ b/bin/keyboardcomposer @@ -342,16 +342,16 @@ class KeyboardComposer(tk.Frame, SubClie for key in keys: # lowercase makes full=0 keysym = "" % key - tkobject.bind(keysym, \ - lambda evt, num=keys.index(key), d=d: \ - self.got_nudger(num, d)) + tkobject.bind(keysym, + lambda evt, num=keys.index(key), d=d: self. + got_nudger(num, d)) # uppercase makes full=1 keysym = "" % 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)) + 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 diff --git a/bin/listsongs b/bin/listsongs --- a/bin/listsongs +++ b/bin/listsongs @@ -7,7 +7,7 @@ function _songs { local expl; _descript compdef _songs curvecalc """ -from run_local import log +from run_local import log # noqa from twisted.internet import reactor from rdflib import RDF from light9 import networking diff --git a/bin/musicPad b/bin/musicPad --- a/bin/musicPad +++ b/bin/musicPad @@ -5,7 +5,6 @@ rewrite all the songs with silence at th import sys, wave, logging, os sys.path.append(".") from light9 import showconfig -from light9.namespaces import L9 from light9.ascoltami.playlist import Playlist logging.basicConfig(level=logging.INFO) log = logging.getLogger() diff --git a/bin/musictime b/bin/musictime --- a/bin/musictime +++ b/bin/musictime @@ -1,5 +1,5 @@ #!/usr/bin/env python -import run_local +import run_local # noqa import light9.networking import tkinter as tk diff --git a/bin/paintserver b/bin/paintserver --- a/bin/paintserver +++ b/bin/paintserver @@ -13,7 +13,7 @@ from rdflib import URIRef from light9 import clientsession import light9.paint.solve from cycloneerr import PrettyErrorHandler -from light9.namespaces import RDF, L9, DEV +from light9.namespaces import L9, DEV import imp diff --git a/bin/picamserve b/bin/picamserve --- a/bin/picamserve +++ b/bin/picamserve @@ -162,7 +162,6 @@ class Pics(cyclone.web.RequestHandler): if not self.running: raise StopIteration - now = time.time() self.write("%s %s\n" % (len(frame), frameTime)) self.write(frame) self.flush() diff --git a/bin/rdfdb b/bin/rdfdb --- a/bin/rdfdb +++ b/bin/rdfdb @@ -1,5 +1,5 @@ #!bin/python -import run_local +import run_local # noqa import os from light9 import networking, showconfig import rdfdb.service diff --git a/bin/subserver b/bin/subserver --- a/bin/subserver +++ b/bin/subserver @@ -4,11 +4,11 @@ live web display of all existing subs wi dragging them into CC or Timeline """ from run_local import log -import sys, optparse, logging, json, subprocess, datetime +import optparse, logging, json, subprocess, datetime from dateutil.tz import tzlocal from twisted.internet import reactor, defer import cyclone.web, cyclone.httpclient, cyclone.websocket -from rdflib import RDF, URIRef, Literal +from rdflib import URIRef, Literal import pyjade.utils from rdfdb.syncedgraph import SyncedGraph from rdfdb.patch import Patch diff --git a/bin/vidref b/bin/vidref --- a/bin/vidref +++ b/bin/vidref @@ -7,10 +7,9 @@ gtk2reactor.install() from twisted.internet import reactor, defer import gobject gobject.threads_init() -import gtk import sys, logging, optparse, json import cyclone.web, cyclone.httpclient, cyclone.websocket -from light9 import networking, showconfig +from light9 import networking from light9.vidref.main import Gui from light9.vidref.replay import snapshotDir from rdfdb.syncedgraph import SyncedGraph @@ -42,7 +41,7 @@ class Snapshot(cyclone.web.RequestHandle self.write(json.dumps({'snapshot': out})) self.set_header("Location", out) self.set_status(303) - except Exception as e: + except Exception: import traceback traceback.print_exc() raise diff --git a/bin/vidrefsetup b/bin/vidrefsetup --- a/bin/vidrefsetup +++ b/bin/vidrefsetup @@ -3,15 +3,12 @@ camera captures with a continuous camera capture yet """ from run_local import log -import sys, optparse, logging, json, subprocess, datetime -from dateutil.tz import tzlocal -from twisted.internet import reactor, defer +import optparse, logging +from twisted.internet import reactor import cyclone.web, cyclone.httpclient, cyclone.websocket -from rdflib import RDF, URIRef, Literal -import pyjade.utils +from rdflib import URIRef from rdfdb.syncedgraph import SyncedGraph -from rdfdb.patch import Patch -from light9.namespaces import L9, DCTERMS +from light9.namespaces import L9 from light9 import networking, showconfig from cycloneerr import PrettyErrorHandler diff --git a/bin/wavecurve b/bin/wavecurve --- a/bin/wavecurve +++ b/bin/wavecurve @@ -1,6 +1,6 @@ #!bin/python import optparse -import run_local +from run_local import log from light9.wavepoints import simp @@ -8,9 +8,10 @@ def createCurve(inpath, outpath, t): print("reading %s, writing %s" % (inpath, outpath)) points = simp(inpath.replace('.ogg', '.wav'), seconds_per_average=t) - f = file(outpath, 'w') + f = open(outpath, 'w') for time_val in points: print("%s %s" % time_val, file=f) + log.info(r'Wrote {outpath}') parser = optparse.OptionParser(usage="""%prog inputSong.wav outputCurve @@ -30,8 +31,6 @@ options, args = parser.parse_args() if options.all: from light9 import showconfig - from light9.namespaces import L9 - from rdflib import RDF from light9.ascoltami.playlist import Playlist graph = showconfig.getGraph() diff --git a/lib/ipython_view.py b/lib/ipython_view.py --- a/lib/ipython_view.py +++ b/lib/ipython_view.py @@ -21,11 +21,12 @@ import re import sys import os from gi.repository import Pango -from StringIO import StringIO +from io import StringIO +from functools import reduce try: - import IPython -except Exception,e: + import IPython +except Exception as e: raise "Error importing IPython (%s)" % str(e) ansi_colors = {'0;30': 'Black', @@ -148,11 +149,11 @@ class IterableIPShell: def shell(self, cmd,verbose=0,debug=0,header=''): stat = 0 - if verbose or debug: print header+cmd + if verbose or debug: print(header+cmd) # flush stdout so we don't mangle python's buffering if not debug: input, output = os.popen4(cmd) - print output.read() + print(output.read()) output.close() input.close() diff --git a/light9/Fadable.py b/light9/Fadable.py --- a/light9/Fadable.py +++ b/light9/Fadable.py @@ -1,5 +1,4 @@ # taken from SnackMix -- now that's reusable code -from tkinter.tix import * import time diff --git a/light9/FlyingFader.py b/light9/FlyingFader.py --- a/light9/FlyingFader.py +++ b/light9/FlyingFader.py @@ -1,6 +1,5 @@ -from tkinter.tix import * -from time import time, sleep - +from tkinter import tix +from time import time class Mass: @@ -65,7 +64,7 @@ class Mass: return not self._stopped -class FlyingFader(Frame): +class FlyingFader(tix.Frame): def __init__(self, master, @@ -75,7 +74,7 @@ class FlyingFader(Frame): font=('Arial', 8), labelwidth=12, **kw): - Frame.__init__(self, master) + tix.Frame.__init__(self, master) self.name = label self.variable = variable @@ -94,25 +93,23 @@ class FlyingFader(Frame): } scaleopts.update(kw) if scaleopts['orient'] == 'vert': - side1 = TOP - side2 = BOTTOM + side2 = tix.BOTTOM else: - side1 = RIGHT - side2 = LEFT + side2 = tix.LEFT - self.scale = Scale(self, **scaleopts) - self.vlabel = Label(self, text="0.0", width=6, font=font) - self.label = Label(self, - text=label, - font=font, - anchor='w', - width=labelwidth) #wraplength=40, ) + self.scale = tix.Scale(self, **scaleopts) + self.vlabel = tix.Label(self, text="0.0", width=6, font=font) + self.label = tix.Label(self, + text=label, + font=font, + anchor='w', + width=labelwidth) #wraplength=40, ) self.oldtrough = self.scale['troughcolor'] - self.scale.pack(side=side2, expand=1, fill=BOTH, anchor='c') - self.vlabel.pack(side=side2, expand=0, fill=X) - self.label.pack(side=side2, expand=0, fill=X) + self.scale.pack(side=side2, expand=1, fill=tix.BOTH, anchor='c') + self.vlabel.pack(side=side2, expand=0, fill=tix.X) + self.label.pack(side=side2, expand=0, fill=tix.X) for k in range(1, 10): self.scale.bind( @@ -150,7 +147,7 @@ class FlyingFader(Frame): mult = 1 if evt.state & 8 and evt.state & 4: mult = 0.25 # both elif evt.state & 8: mult = 0.5 # alt - elif evt.state & 4: mult = 2 # control + elif evt.state & 4: mult = 2 # control # noqa self.mass.x = self.variable.get() self.mass.goto(newlevel) @@ -204,22 +201,19 @@ def colorfade(scale, lev): if __name__ == '__main__': - root = Tk() + root = tix.Tk() root.tk_focusFollowsMouse() - FlyingFader(root, variable=DoubleVar(), label="suck").pack(side=LEFT, - expand=1, - fill=BOTH) - FlyingFader(root, variable=DoubleVar(), label="moof").pack(side=LEFT, - expand=1, - fill=BOTH) - FlyingFader(root, variable=DoubleVar(), label="zarf").pack(side=LEFT, - expand=1, - fill=BOTH) + FlyingFader(root, variable=tix.DoubleVar(), + label="suck").pack(side=tix.LEFT, expand=1, fill=tix.BOTH) + FlyingFader(root, variable=tix.DoubleVar(), + label="moof").pack(side=tix.LEFT, expand=1, fill=tix.BOTH) + FlyingFader(root, variable=tix.DoubleVar(), + label="zarf").pack(side=tix.LEFT, expand=1, fill=tix.BOTH) FlyingFader(root, - variable=DoubleVar(), - label="long name goes here. got it?").pack(side=LEFT, + variable=tix.DoubleVar(), + label="long name goes here. got it?").pack(side=tix.LEFT, expand=1, - fill=BOTH) + fill=tix.BOTH) root.mainloop() diff --git a/light9/Patch.py b/light9/Patch.py --- a/light9/Patch.py +++ b/light9/Patch.py @@ -1,4 +1,3 @@ -import os from rdflib import RDF from light9.namespaces import L9 from light9 import showconfig diff --git a/light9/Submaster.py b/light9/Submaster.py --- a/light9/Submaster.py +++ b/light9/Submaster.py @@ -1,12 +1,12 @@ -import os, logging, time +import logging 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 light9.Patch import resolve_name, get_dmx_channel, get_channel_uri from louie import dispatcher -from .rdfdb.patch import Patch +from rdfdb.patch import Patch log = logging.getLogger('submaster') @@ -73,7 +73,7 @@ class Submaster(object): # not sure how useful this is if not isinstance(other, Submaster): return -1 - return cmp(self.ident(), other.ident()) + return cmp(self.ident(), other.ident()) # noqa def __hash__(self): return hash(self.ident()) @@ -189,7 +189,7 @@ class PersistentSubmaster(Submaster): continue try: self.levels[name] = float(val) - except: + except Exception: log.error("name %r val %r" % (name, val)) raise @@ -326,11 +326,11 @@ class Submasters(object): def get_all_subs(self): "All Submaster objects" - l = sorted(list(self.submasters.items())) - l = [x[1] for x in l] + v = sorted(list(self.submasters.items())) + v = [x[1] for x in v] songs = [] notsongs = [] - for s in l: + for s in v: if s.name and s.name.startswith('song'): songs.append(s) else: diff --git a/light9/TLUtility.py b/light9/TLUtility.py --- a/light9/TLUtility.py +++ b/light9/TLUtility.py @@ -56,7 +56,7 @@ def dumpobj(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: + except Exception: pass print("") @@ -190,7 +190,7 @@ def dict_max(*dicts): 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 d.items()]) def dict_subset(d, dkeys, default=0): diff --git a/light9/ascoltami/player.py b/light9/ascoltami/player.py --- a/light9/ascoltami/player.py +++ b/light9/ascoltami/player.py @@ -4,8 +4,8 @@ alternate to the mpd music player, for a """ import time, logging, traceback -from gi.repository import GObject, Gst -from twisted.internet import reactor, task +from gi.repository import Gst +from twisted.internet import task log = logging.getLogger() @@ -30,7 +30,7 @@ class Player(object): task.LoopingCall(self.watchTime).start(.050) - bus = self.pipeline.get_bus() + #bus = self.pipeline.get_bus() # not working- see notes in pollForMessages #self.watchForMessages(bus) @@ -46,7 +46,7 @@ class Player(object): self.pause() self.lastWatchTime = t - except: + except Exception: traceback.print_exc() def watchForMessages(self, bus): diff --git a/light9/ascoltami/playlist.py b/light9/ascoltami/playlist.py --- a/light9/ascoltami/playlist.py +++ b/light9/ascoltami/playlist.py @@ -19,13 +19,13 @@ class Playlist(object): try: currentIndex = self.songs.index(currentSong) except IndexError: - raise ValueError("%r is not in the current playlist (%r)." % \ - (currentSong, self.playlistUri)) + raise ValueError("%r is not in the current playlist (%r)." % + (currentSong, self.playlistUri)) try: nextSong = self.songs[currentIndex + 1] except IndexError: - raise NoSuchSong("%r is the last item in the playlist." % \ + raise NoSuchSong("%r is the last item in the playlist." % currentSong) return nextSong diff --git a/light9/ascoltami/webapp.py b/light9/ascoltami/webapp.py --- a/light9/ascoltami/webapp.py +++ b/light9/ascoltami/webapp.py @@ -1,6 +1,5 @@ -import json, socket, subprocess, cyclone.web, os +import json, socket, subprocess, cyclone.web from twisted.python.util import sibpath -from twisted.python.filepath import FilePath from light9.namespaces import L9 from light9.showconfig import getSongsFromShow, songOnDisk from rdflib import URIRef @@ -140,7 +139,7 @@ class goButton(PrettyErrorHandler, cyclo """ if music is playing, this silently does nothing. """ - graph, player = self.settings.app.graph, self.settings.app.player + player = self.settings.app.player if player.isAutostopped(): player.resume() diff --git a/light9/coffee.py b/light9/coffee.py new file mode 100644 --- /dev/null +++ b/light9/coffee.py @@ -0,0 +1,23 @@ +from cycloneerr import PrettyErrorHandler +import cyclone.web +import subprocess + + +class StaticCoffee(PrettyErrorHandler, cyclone.web.RequestHandler): + """ + e.g. + + (r'/effect\.js', StaticCoffee, { + 'src': 'light9/effecteval/effect.coffee' + }), + """ # noqa + + def initialize(self, src): + super(StaticCoffee, self).initialize() + self.src = src + + def get(self): + self.set_header('Content-Type', 'application/javascript') + self.write( + subprocess.check_output( + ['/usr/bin/coffee', '--compile', '--print', self.src])) diff --git a/light9/collector/collector.py b/light9/collector/collector.py --- a/light9/collector/collector.py +++ b/light9/collector/collector.py @@ -7,8 +7,9 @@ from light9.collector.device import toOu # types only from rdflib import Graph, URIRef -from typing import List, Dict, Tuple, Any, TypeVar, Generic +from typing import List, Dict, Tuple, TypeVar, Generic, Optional from light9.collector.output import Output +from light9.collector.weblisteners import WebListeners ClientType = TypeVar('ClientType') ClientSessionType = TypeVar('ClientSessionType') @@ -24,7 +25,7 @@ def outputMap(graph, outputs): """ ret = {} - outputByUri = {} # universeUri : output + outputByUri: Dict[URIRef, Output] = {} # universeUri : output for out in outputs: outputByUri[out.uri] = out @@ -50,8 +51,11 @@ def outputMap(graph, outputs): class Collector(Generic[ClientType, ClientSessionType]): - def __init__(self, graph, outputs, listeners=None, clientTimeoutSec=10): - # type: (Graph, List[Output], List[Listener], float) -> None + def __init__(self, + graph: Graph, + outputs: List[Output], + listeners: Optional[WebListeners] = None, + clientTimeoutSec: float = 10): self.graph = graph self.outputs = outputs self.listeners = listeners diff --git a/light9/collector/collector_test.py b/light9/collector/collector_test.py --- a/light9/collector/collector_test.py +++ b/light9/collector/collector_test.py @@ -1,7 +1,7 @@ import unittest import datetime, time from freezegun import freeze_time -from rdflib import Namespace, URIRef +from rdflib import Namespace from light9.namespaces import L9, DEV from light9.collector.collector import Collector, outputMap diff --git a/light9/collector/device.py b/light9/collector/device.py --- a/light9/collector/device.py +++ b/light9/collector/device.py @@ -1,6 +1,5 @@ import logging -import math -from light9.namespaces import L9, RDF, DEV +from light9.namespaces import L9 from rdflib import Literal from webcolors import hex_to_rgb, rgb_to_hex from colormath.color_objects import sRGBColor, CMYColor diff --git a/light9/collector/output.py b/light9/collector/output.py --- a/light9/collector/output.py +++ b/light9/collector/output.py @@ -3,7 +3,7 @@ import sys import time import usb.core import logging -from twisted.internet import task, threads, reactor +from twisted.internet import threads, reactor from greplin import scales log = logging.getLogger('output') diff --git a/light9/collector/weblisteners.py b/light9/collector/weblisteners.py new file mode 100644 --- /dev/null +++ b/light9/collector/weblisteners.py @@ -0,0 +1,70 @@ +import logging, traceback, time, json +log = logging.getLogger('weblisteners') + +class WebListeners(object): + + def __init__(self): + self.clients = [] + self.pendingMessageForDev = {} # dev: (attrs, outputmap) + self.lastFlush = 0 + + def addClient(self, client): + self.clients.append([client, {}]) # seen = {dev: attrs} + log.info('added client %s %s', len(self.clients), client) + + def delClient(self, client): + self.clients = [[c, t] for c, t in self.clients if c != client] + log.info('delClient %s, %s left', client, len(self.clients)) + + def outputAttrsSet(self, dev, attrs, outputMap): + """called often- don't be slow""" + + self.pendingMessageForDev[dev] = (attrs, outputMap) + try: + self._flush() + except Exception: + traceback.print_exc() + raise + + def _flush(self): + now = time.time() + if now < self.lastFlush + .05 or not self.clients: + return + self.lastFlush = now + + while self.pendingMessageForDev: + dev, (attrs, outputMap) = self.pendingMessageForDev.popitem() + + msg = None # lazy, since makeMsg is slow + + # this omits repeats, but can still send many + # messages/sec. Not sure if piling up messages for the browser + # could lead to slowdowns in the real dmx output. + for client, seen in self.clients: + if seen.get(dev) == attrs: + continue + if msg is None: + msg = self.makeMsg(dev, attrs, outputMap) + + seen[dev] = attrs + client.sendMessage(msg) + + def makeMsg(self, dev, attrs, outputMap): + attrRows = [] + for attr, val in attrs.items(): + output, index = outputMap[(dev, attr)] + attrRows.append({ + 'attr': attr.rsplit('/')[-1], + 'val': val, + 'chan': (output.shortId(), index + 1) + }) + attrRows.sort(key=lambda r: r['chan']) + for row in attrRows: + row['chan'] = '%s %s' % (row['chan'][0], row['chan'][1]) + + msg = json.dumps({'outputAttrsSet': { + 'dev': dev, + 'attrs': attrRows + }}, + sort_keys=True) + return msg diff --git a/light9/curvecalc/client.py b/light9/curvecalc/client.py --- a/light9/curvecalc/client.py +++ b/light9/curvecalc/client.py @@ -4,7 +4,6 @@ client code for talking to curvecalc import cyclone.httpclient from light9 import networking import urllib.request, urllib.parse, urllib.error -from run_local import log def sendLiveInputPoint(curve, value): diff --git a/light9/curvecalc/curve.py b/light9/curvecalc/curve.py --- a/light9/curvecalc/curve.py +++ b/light9/curvecalc/curve.py @@ -1,4 +1,4 @@ -import glob, time, logging, ast, os +import logging, ast, os from bisect import bisect_left, bisect import louie as dispatcher from twisted.internet import reactor @@ -45,7 +45,7 @@ class Curve(object): def load(self, filename): self.points[:] = [] - for line in file(filename): + for line in open(filename): x, y = line.split() self.points.append((float(x), ast.literal_eval(y))) self.points.sort() @@ -75,7 +75,7 @@ class Curve(object): if filename.endswith('-music') or filename.endswith('_music'): print("not saving music track") return - f = file(filename, 'w') + f = open(filename, 'w') for p in self.points: f.write("%s %r\n" % p) f.close() diff --git a/light9/curvecalc/curveedit.py b/light9/curvecalc/curveedit.py --- a/light9/curvecalc/curveedit.py +++ b/light9/curvecalc/curveedit.py @@ -1,12 +1,11 @@ """ this may be split out from curvecalc someday, since it doesn't need to be tied to a gui """ -import cgi, time +import cgi from twisted.internet import reactor import cyclone.web, cyclone.httpclient, cyclone.websocket from rdflib import URIRef from cycloneerr import PrettyErrorHandler -from run_local import log from louie import dispatcher diff --git a/light9/curvecalc/curveview.py b/light9/curvecalc/curveview.py --- a/light9/curvecalc/curveview.py +++ b/light9/curvecalc/curveview.py @@ -1,4 +1,4 @@ -import math, logging, traceback +import math, logging from gi.repository import Gtk from gi.repository import Gdk from gi.repository import GooCanvas @@ -6,7 +6,6 @@ import louie as dispatcher from rdflib import Literal from twisted.internet import reactor from light9.curvecalc.zoomcontrol import RegionZoom -from light9.curvecalc import cursors from light9.curvecalc.curve import introPad, postPad from lib.goocanvas_compat import Points, polyline_new_line import imp @@ -1276,8 +1275,8 @@ class Curvesetview(object): if not self.live: # workaround for old instances living past reload() return - r = self.row_under_mouse() - key = event.string + #r = self.row_under_mouse() + #key = event.string pass # no handlers right now def row_under_mouse(self): diff --git a/light9/curvecalc/output.py b/light9/curvecalc/output.py --- a/light9/curvecalc/output.py +++ b/light9/curvecalc/output.py @@ -1,8 +1,6 @@ import time, logging from twisted.internet import reactor from light9 import Submaster, dmxclient -from light9.namespaces import L9 -from light9.curvecalc.subterm import Subterm from louie import dispatcher log = logging.getLogger("output") diff --git a/light9/curvecalc/subterm.py b/light9/curvecalc/subterm.py --- a/light9/curvecalc/subterm.py +++ b/light9/curvecalc/subterm.py @@ -1,8 +1,8 @@ -import math, os, random, logging -from rdflib import Graph, URIRef, RDF, RDFS, Literal +import logging +from rdflib import Literal from louie import dispatcher import light9.Effects -from light9 import Submaster, showconfig, prof +from light9 import Submaster from light9.Patch import get_dmx_channel from rdfdb.patch import Patch from light9.namespaces import L9 diff --git a/light9/curvecalc/zoomcontrol.py b/light9/curvecalc/zoomcontrol.py --- a/light9/curvecalc/zoomcontrol.py +++ b/light9/curvecalc/zoomcontrol.py @@ -1,5 +1,3 @@ -from gi.repository import Gtk -from gi.repository import GObject from gi.repository import GooCanvas import louie as dispatcher from light9.curvecalc import cursors diff --git a/light9/dmxchanedit.py b/light9/dmxchanedit.py --- a/light9/dmxchanedit.py +++ b/light9/dmxchanedit.py @@ -18,7 +18,7 @@ proposal for new attribute system: """ import tkinter as tk -from rdflib import RDF, Literal +from rdflib import RDF import math, logging from decimal import Decimal from light9.namespaces import L9 diff --git a/light9/dmxclient.py b/light9/dmxclient.py --- a/light9/dmxclient.py +++ b/light9/dmxclient.py @@ -72,5 +72,5 @@ dummy = os.getenv('DMXDUMMY') if dummy: print("dmxclient: DMX is in dummy mode.") - def outputlevels(*args, **kw): + def outputlevels(*args, **kw): # noqa pass diff --git a/light9/effect/effecteval.py b/light9/effect/effecteval.py --- a/light9/effect/effecteval.py +++ b/light9/effect/effecteval.py @@ -1,13 +1,10 @@ -from rdflib import URIRef, Literal -from light9.namespaces import L9, RDF, DEV +from rdflib import Literal +from light9.namespaces import L9, DEV from webcolors import rgb_to_hex, hex_to_rgb from colorsys import hsv_to_rgb -from decimal import Decimal import math -import traceback from noise import pnoise1 import logging -import time from light9.effect.settings import DeviceSettings from light9.effect.scale import scale import random @@ -135,7 +132,6 @@ def effect_animRainbow(effectSettings, s def effect_auraSparkles(effectSettings, strength, songTime, noteTime): out = {} tint = effectSettings.get(L9['tint'], '#ffffff') - tintStrength = float(effectSettings.get(L9['tintStrength'], 0)) print(effectSettings) tr, tg, tb = hex_to_rgb(tint) for n in range(1, 5 + 1): diff --git a/light9/effect/sequencer.py b/light9/effect/sequencer.py --- a/light9/effect/sequencer.py +++ b/light9/effect/sequencer.py @@ -44,7 +44,9 @@ class Note(object): ea = settingValues[L9['effectAttr']] self.baseEffectSettings[ea] = settingValues[L9['value']] - floatVal = lambda s, p: float(g.value(s, p).toPython()) + def floatVal(s, p): + return float(g.value(s, p).toPython()) + originTime = floatVal(uri, L9['originTime']) self.points = [] for curve in g.objects(uri, L9['curve']): diff --git a/light9/effect/settings.py b/light9/effect/settings.py --- a/light9/effect/settings.py +++ b/light9/effect/settings.py @@ -6,8 +6,7 @@ or output attrs (dmx channel). import decimal import numpy from rdflib import URIRef, Literal -from light9.namespaces import RDF, L9, DEV -from rdfdb.patch import Patch +from light9.namespaces import RDF, L9 import logging log = logging.getLogger('settings') from light9.collector.device import resolve diff --git a/light9/effect/settings_test.py b/light9/effect/settings_test.py --- a/light9/effect/settings_test.py +++ b/light9/effect/settings_test.py @@ -2,7 +2,7 @@ import unittest from rdflib import Literal from rdfdb.patch import Patch from rdfdb.localsyncedgraph import LocalSyncedGraph -from light9.namespaces import RDF, L9, DEV +from light9.namespaces import L9, DEV from light9.effect.settings import DeviceSettings diff --git a/light9/effecteval/effectloop.py b/light9/effecteval/effectloop.py --- a/light9/effecteval/effectloop.py +++ b/light9/effecteval/effectloop.py @@ -1,18 +1,17 @@ import time, json, logging, traceback import numpy import serial -from twisted.internet import reactor, threads +from twisted.internet import reactor from twisted.internet.defer import inlineCallbacks, returnValue, succeed from twisted.internet.error import TimeoutError -from rdflib import URIRef, Literal +from rdflib import URIRef import cyclone.httpclient -from light9.namespaces import L9, RDF, RDFS +from light9.namespaces import L9, RDF from light9.effecteval.effect import EffectNode from light9 import Effects from light9 import networking from light9 import Submaster from light9 import dmxclient -from light9 import prof log = logging.getLogger('effectloop') diff --git a/light9/gtkpyconsole.py b/light9/gtkpyconsole.py --- a/light9/gtkpyconsole.py +++ b/light9/gtkpyconsole.py @@ -1,5 +1,5 @@ from lib.ipython_view import IPythonView -import gi +import gi # noqa from gi.repository import Gtk from gi.repository import Pango diff --git a/light9/namespaces.py b/light9/namespaces.py --- a/light9/namespaces.py +++ b/light9/namespaces.py @@ -1,4 +1,4 @@ -from rdflib import Namespace, RDF, RDFS +from rdflib import Namespace, RDF, RDFS # noqa # Namespace was showing up in profiles diff --git a/light9/paint/solve.py b/light9/paint/solve.py --- a/light9/paint/solve.py +++ b/light9/paint/solve.py @@ -1,11 +1,11 @@ -from light9.namespaces import RDF, L9, DEV +from light9.namespaces import L9, DEV from PIL import Image import numpy import scipy.misc, scipy.ndimage, scipy.optimize import cairo import logging -from light9.effect.settings import DeviceSettings, parseHex, toHex +from light9.effect.settings import DeviceSettings, parseHex log = logging.getLogger('solve') @@ -151,7 +151,7 @@ class Solver(object): results = [] dist = ImageDist(img) if device is None: - items = list(self.samples.items()) + items = self.samples.items() else: items = self.samplesForDevice[device] for uri, img2 in sorted(items): @@ -220,18 +220,18 @@ class Solver(object): #saveNumpy('/tmp/sample_%s.png' % sample.split('/')[-1], # f(picSample)) sampleDist[sample] = dist.distanceTo(picSample) - results = sorted([(d, uri) for uri, d in list(sampleDist.items())]) + results = sorted([(d, uri) for uri, d in sampleDist.items()]) sample = results[0][1] # this is wrong; some wrong-alignments ought to be dimmer than full brightest0 = brightest(pic0) - brightestSample = brightest(self.samples[sample]) + #brightestSample = brightest(self.samples[sample]) if max(brightest0) < 1 / 255: return DeviceSettings(self.graph, []) - scale = brightest0 / brightestSample + #scale = brightest0 / brightestSample s = DeviceSettings.fromResource(self.graph, sample) # missing color scale, but it was wrong to operate on all devs at once @@ -300,7 +300,7 @@ class Solver(object): for dev, devSettings in settings.byDevice(): requestedColor = devSettings.getValue(dev, L9['color']) candidatePics = [] # (distance, path, picColor) - for sample, s in list(self.sampleSettings.items()): + for sample, s in self.sampleSettings.items(): path = self.path[sample] otherDevSettings = s.ofDevice(dev) if not otherDevSettings: diff --git a/light9/paint/solve_test.py b/light9/paint/solve_test.py --- a/light9/paint/solve_test.py +++ b/light9/paint/solve_test.py @@ -2,7 +2,7 @@ import unittest import numpy.testing from . import solve from rdflib import Namespace -from light9.namespaces import RDF, L9, DEV +from light9.namespaces import L9, DEV from rdfdb.localsyncedgraph import LocalSyncedGraph from light9.effect.settings import DeviceSettings diff --git a/light9/showconfig.py b/light9/showconfig.py --- a/light9/showconfig.py +++ b/light9/showconfig.py @@ -3,7 +3,7 @@ from twisted.python.filepath import File from os import path, getenv from rdflib import Graph from rdflib import URIRef -from .namespaces import MUS, L9 +from .namespaces import L9 log = logging.getLogger('showconfig') _config = None # graph diff --git a/light9/subclient.py b/light9/subclient.py --- a/light9/subclient.py +++ b/light9/subclient.py @@ -1,5 +1,5 @@ from light9.collector.collector_client import sendToCollector -from twisted.internet import reactor, task +from twisted.internet import reactor import traceback import time import logging @@ -38,7 +38,7 @@ class SubClient: try: with self.graph.currentState() as g: outputSettings = self.get_output_settings(_graph=g) - except: + except Exception: traceback.print_exc() return return sendToCollector('subclient', self.session, outputSettings) diff --git a/light9/uihelpers.py b/light9/uihelpers.py --- a/light9/uihelpers.py +++ b/light9/uihelpers.py @@ -33,7 +33,7 @@ def toplevel_savegeometry(tl, name): f = open(".light9-window-geometry-%s" % name.replace(' ', '_'), 'w') f.write(tl.geometry()) # else the window never got mapped - except Exception as e: + except Exception: # it's ok if there's no saved geometry pass diff --git a/light9/vidref/main.py b/light9/vidref/main.py --- a/light9/vidref/main.py +++ b/light9/vidref/main.py @@ -79,7 +79,7 @@ class Gui(object): try: with gtk.gdk.lock: self.replayViews.update(position) - except: + except Exception: traceback.print_exc() return True diff --git a/light9/vidref/musictime.py b/light9/vidref/musictime.py --- a/light9/vidref/musictime.py +++ b/light9/vidref/musictime.py @@ -1,9 +1,7 @@ -import restkit, time, json, logging +import time, json, logging from light9 import networking from twisted.internet import reactor from cyclone.httpclient import fetch -from restkit.errors import ResourceNotFound -import http_parser.http log = logging.getLogger() @@ -29,7 +27,6 @@ class MusicTime(object): self.period = period self.hoverPeriod = .05 self.onChange = onChange - self.musicResource = restkit.Resource(networking.musicPlayer.url) self.position = {} # driven by our pollCurvecalcTime and also by Gui.incomingTime @@ -125,6 +122,9 @@ class MusicTime(object): def sendTime(self, t): """request that the player go to this time""" - self.musicResource.post("time", - payload=json.dumps({"t": t}), - headers={"content-type": "application/json"}) + fetch( + method=b'POST', + url=networking.musicPlayer.path('time'), + body=json.dumps({"t": t}), + headers={b"content-type": [b"application/json"]}, + ) diff --git a/light9/vidref/remotepivideo.py b/light9/vidref/remotepivideo.py --- a/light9/vidref/remotepivideo.py +++ b/light9/vidref/remotepivideo.py @@ -6,8 +6,8 @@ import gtk import numpy import treq from twisted.internet import defer -from light9.vidref.replay import framerate, songDir, takeDir, snapshotDir -from light9 import prof, showconfig +from light9.vidref.replay import songDir, takeDir, snapshotDir +from light9 import showconfig from light9.namespaces import L9 from PIL import Image from io import StringIO diff --git a/light9/vidref/videorecorder.py b/light9/vidref/videorecorder.py --- a/light9/vidref/videorecorder.py +++ b/light9/vidref/videorecorder.py @@ -158,7 +158,7 @@ class VideoRecordSink(gst.Element): img = Image.fromstring('RGB', (cap['width'], cap['height']), buffer.data) self.imagesToSave.put((position, img, buffer.timestamp)) - except: + except Exception: traceback.print_exc() return gst.FLOW_OK diff --git a/light9/wavepoints.py b/light9/wavepoints.py --- a/light9/wavepoints.py +++ b/light9/wavepoints.py @@ -4,13 +4,14 @@ 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("# 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()) framerate = wavefile.getframerate() # frames / second + frames_to_read = int(framerate * seconds_per_average) print("# frames_to_read=%s" % frames_to_read) @@ -31,7 +32,7 @@ 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) diff --git a/light9/zmqtransport.py b/light9/zmqtransport.py new file mode 100644 --- /dev/null +++ b/light9/zmqtransport.py @@ -0,0 +1,41 @@ +import json +from rdflib import URIRef, Literal +from greplin import scales +from txzmq import ZmqEndpoint, ZmqFactory, ZmqPullConnection +import logging + +log = logging.getLogger('zmq') + + +def parseJsonMessage(msg): + body = json.loads(msg) + settings = [] + for device, attr, value in body['settings']: + if isinstance(value, str) and value.startswith('http'): + value = URIRef(value) + else: + value = Literal(value) + settings.append((URIRef(device), URIRef(attr), value)) + return body['client'], body['clientSession'], settings, body['sendTime'] + + +def startZmq(port, collector): + stats = scales.collection('/zmqServer', scales.PmfStat('setAttr')) + + zf = ZmqFactory() + addr = 'tcp://*:%s' % port + log.info('creating zmq endpoint at %r', addr) + e = ZmqEndpoint('bind', addr) + + class Pull(ZmqPullConnection): + #highWaterMark = 3 + def onPull(self, message): + with stats.setAttr.time(): + # todo: new compressed protocol where you send all URIs up + # front and then use small ints to refer to devices and + # attributes in subsequent requests. + client, clientSession, settings, sendTime = parseJsonMessage( + message[0]) + collector.setAttrs(client, clientSession, settings, sendTime) + + Pull(zf, e)