diff --git a/bin/curvecalc b/bin/curvecalc --- a/bin/curvecalc +++ b/bin/curvecalc @@ -1,5 +1,4 @@ #!bin/python - """ now launches like this: % bin/curvecalc http://light9.bigasterisk.com/show/dance2007/song1 @@ -12,7 +11,7 @@ todo: curveview should preserve more obj from __future__ import division import sys -sys.path.append('/usr/lib/python2.7/dist-packages') # For gtk +sys.path.append('/usr/lib/python2.7/dist-packages') # For gtk from twisted.internet import gtk3reactor gtk3reactor.install() from twisted.internet import reactor @@ -24,13 +23,13 @@ from gi.repository import GObject from gi.repository import Gdk from urlparse import parse_qsl -import louie as dispatcher +import louie as dispatcher from rdflib import URIRef, Literal, RDF, RDFS import logging from run_local import log from light9 import showconfig, networking -from light9.curvecalc import curveview +from light9.curvecalc import curveview from light9.curvecalc.curve import Curveset from light9.curvecalc.curveedit import serveCurveEdit from light9.curvecalc.musicaccess import Music @@ -46,34 +45,40 @@ from rdfdb.patch import Patch from rdfdb.syncedgraph import SyncedGraph from light9.wavelength import wavelength + class SubtermExists(ValueError): pass + class Main(object): + def __init__(self, graph, opts, session, curveset, music): self.graph, self.opts, self.session = graph, opts, session self.curveset, self.music = curveset, music self.lastSeenInputTime = 0 - self.currentSubterms = [] # Subterm objects that are synced to the graph + self.currentSubterms = [ + ] # Subterm objects that are synced to the graph self.setTheme() wtree = self.wtree = Gtk.Builder() wtree.add_from_file("light9/curvecalc/curvecalc.glade") mainwin = wtree.get_object("MainWindow") - + mainwin.connect("destroy", self.onQuit) wtree.connect_signals(self) mainwin.show_all() mainwin.connect("delete-event", lambda *args: reactor.crash()) + def updateTitle(): - mainwin.set_title("curvecalc - %s" % - graph.label( - graph.value(session, L9['currentSong']))) + mainwin.set_title( + "curvecalc - %s" % + graph.label(graph.value(session, L9['currentSong']))) + graph.addHandler(updateTitle) - songChoice = Observable(None) # to be connected with the session song + songChoice = Observable(None) # to be connected with the session song self.registerGraphToSongChoice(wtree, session, graph, songChoice) self.registerSongChoiceToGraph(session, graph, songChoice) @@ -82,28 +87,31 @@ class Main(object): ec = EditChoice(graph, songChoice, label="Editing song:") wtree.get_object("currentSongEditChoice").add(ec) ec.show() - + wtree.get_object("subterms").connect("add", self.onSubtermChildAdded) - - self.refreshCurveView() - + + self.refreshCurveView() + self.makeStatusLines(wtree.get_object("status")) self.setupNewSubZone() self.acceptDragsOnCurveViews() - + # may not work wtree.get_object("paned1").set_position(600) def registerGraphToSongChoice(self, wtree, session, graph, songChoice): + def setSong(): current = graph.value(session, L9['currentSong']) if not wtree.get_object("followPlayerSongChoice").get_active(): songChoice(current) dispatcher.send("song_has_changed") + graph.addHandler(setSong) def registerSongChoiceToGraph(self, session, graph, songChoice): self.muteSongChoiceUntil = 0 + def songChoiceToGraph(newSong): if newSong is Local: raise NotImplementedError('what do i patch') @@ -116,12 +124,12 @@ class Main(object): log.debug('muted') return self.muteSongChoiceUntil = now + 1 - - graph.patchObject(context=session, subject=session, - predicate=L9['currentSong'], newObject=newSong) - - - + + graph.patchObject(context=session, + subject=session, + predicate=L9['currentSong'], + newObject=newSong) + songChoice.subscribe(songChoiceToGraph) def registerCurrentPlayerSongToUi(self, wtree, graph, songChoice): @@ -129,47 +137,51 @@ class Main(object): and current_player_song 'song' param -> songChoice, if you're in autofollow """ + def current_player_song(song): # (this is run on every frame) ps = wtree.get_object("playerSong") if URIRef(ps.get_uri()) != song: log.debug("update playerSong to %s", ps.get_uri()) + def setLabel(): ps.set_label(graph.label(song)) + graph.addHandler(setLabel) ps.set_uri(song) if song != songChoice(): if wtree.get_object("followPlayerSongChoice").get_active(): log.debug('followPlayerSongChoice is on') songChoice(song) - + dispatcher.connect(current_player_song, "current_player_song") self.current_player_song = current_player_song - + def setupNewSubZone(self): self.wtree.get_object("newSubZone").drag_dest_set( flags=Gtk.DestDefaults.ALL, targets=[Gtk.TargetEntry('text/uri-list', 0, 0)], actions=Gdk.DragAction.COPY) - + def acceptDragsOnCurveViews(self): w = self.wtree.get_object("curves") w.drag_dest_set(flags=Gtk.DestDefaults.ALL, targets=[Gtk.TargetEntry('text/uri-list', 0, 0)], actions=Gdk.DragAction.COPY) - def recv(widget, context, x, y, selection, - targetType, time): + + def recv(widget, context, x, y, selection, targetType, time): subUri = URIRef(selection.data.strip()) print "into curves", subUri - with self.graph.currentState( - tripleFilter=(subUri, RDFS.label, None)) as current: + with self.graph.currentState(tripleFilter=(subUri, RDFS.label, + None)) as current: subName = current.label(subUri) if '?' in subUri: subName = self.handleSubtermDrop(subUri) else: try: - self.makeSubterm(subName, withCurve=True, + self.makeSubterm(subName, + withCurve=True, sub=subUri, expr="%s(t)" % subName) except SubtermExists: @@ -177,23 +189,26 @@ class Main(object): # correct-- user mihgt need to fix things pass curveView = self.curvesetView.row(subName).curveView - t = self.lastSeenInputTime # curveView.current_time() # new curve hasn't heard the time yet. this has gotten too messy- everyone just needs to be able to reach the time source + t = self.lastSeenInputTime # curveView.current_time() # new curve hasn't heard the time yet. this has gotten too messy- everyone just needs to be able to reach the time source print "time", t - curveView.add_points([(t - .5, 0), - (t, 1)]) + curveView.add_points([(t - .5, 0), (t, 1)]) + w.connect("drag-data-received", recv) - + def onDragDataInNewSubZone(self, widget, context, x, y, selection, - targetType, time): + targetType, time): data = URIRef(selection.data.strip()) if '?' in data: self.handleSubtermDrop(data) return - with self.graph.currentState(tripleFilter=(data, None, None)) as current: + with self.graph.currentState(tripleFilter=(data, None, + None)) as current: subName = current.label(data) - self.makeSubterm(newname=subName, withCurve=True, sub=data, + self.makeSubterm(newname=subName, + withCurve=True, + sub=data, expr="%s(t)" % subName) - + def handleSubtermDrop(self, data): params = parse_qsl(data.split('?')[1]) flattened = dict(params) @@ -216,21 +231,21 @@ class Main(object): def onRedrawCurves(self, *args): dispatcher.send("all curves rebuild") - + def onSubtermsMap(self, *args): # if this was called too soon, like in __init__, the gtktable # would get its children but it wouldn't lay anything out that # I can see, and I'm not sure why. Waiting for map event is # just a wild guess. self.graph.addHandler(self.set_subterms_from_graph) - + def onNewSubterm(self, *args): self.makeSubterm(Literal(""), withCurve=False) return # pretty sure i don't want this back, but not completely sure # what the UX should be to get the new curve. - + dialog = self.wtree.get_object("newSubterm") # the plan is to autocomplete this on existing subterm names # (but let you make one up, too) @@ -245,9 +260,9 @@ class Main(object): def currentSong(self): - with self.graph.currentState( - tripleFilter=(self.session, L9['currentSong'], None) - ) as current: + with self.graph.currentState(tripleFilter=(self.session, + L9['currentSong'], + None)) as current: return current.value(self.session, L9['currentSong']) def songSubtermsContext(self): @@ -277,13 +292,13 @@ class Main(object): (uri, RDF.type, L9.Subterm, ctx), (uri, RDFS.label, Literal(newname), ctx), (self.currentSong(), L9['subterm'], uri, ctx), - ] + ] if sub is not None: quads.append((uri, L9['sub'], sub, ctx)) if expr is not None: quads.append((uri, L9['expression'], Literal(expr), ctx)) self.graph.patch(Patch(addQuads=quads)) - + return uri def all_subterm_labels(self): @@ -301,7 +316,7 @@ class Main(object): if sub is not None: labels.append(current.label(sub)) return labels - + def set_subterms_from_graph(self): """rebuild all the gtktable 'subterms' widgets and the self.currentSubterms list""" @@ -311,7 +326,7 @@ class Main(object): for st in set(self.graph.objects(song, L9['subterm'])): log.debug("song %s has subterm %s", song, st) term = Subterm(self.graph, st, self.songSubtermsContext(), - self.curveset) + self.curveset) newList.append(term) self.currentSubterms[:] = newList @@ -335,13 +350,12 @@ class Main(object): * { font-size: 92%; } .button:link { font-size: 7px } ''') - + screen = Gdk.Display.get_default_screen(Gdk.Display.get_default()) for p in providers: Gtk.StyleContext.add_provider_for_screen( - screen, p, - Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) - + screen, p, Gtk.STYLE_PROVIDER_PRIORITY_APPLICATION) + def onSubtermChildAdded(self, subtermsTable, *args): # this would probably work, but isn't getting called log.info("onSubtermChildAdded") @@ -369,7 +383,7 @@ class Main(object): ns.update(globals()) ns.update(self.__dict__) togglePyConsole(self, item, ns) - + def onSeeCurrentTime(self, item): dispatcher.send("see time") @@ -395,15 +409,14 @@ class Main(object): def makeStatusLines(self, master): """various labels that listen for dispatcher signals""" for row, (signame, textfilter) in enumerate([ - ('input time', lambda t: "%.2fs"%t), - ('output levels', - lambda levels: textwrap.fill("; ".join(["%s:%.2f"%(n,v) - for n,v in - levels.items()[:2] - if v>0]),70)), - ('update period', lambda t: "%.1fms"%(t*1000)), + ('input time', lambda t: "%.2fs" % t), + ('output levels', lambda levels: textwrap.fill( + "; ".join([ + "%s:%.2f" % (n, v) for n, v in levels.items()[:2] if v > 0 + ]), 70)), + ('update period', lambda t: "%.1fms" % (t * 1000)), ('update status', lambda x: str(x)), - ]): + ]): key = Gtk.Label("%s:" % signame) value = Gtk.Label("") master.resize(row + 1, 2) @@ -412,28 +425,31 @@ class Main(object): key.set_alignment(1, 0) value.set_alignment(0, 0) - dispatcher.connect(lambda val, value=value, tf=textfilter: - value.set_text(tf(val)), - signame, weak=False) + dispatcher.connect(lambda val, value=value, tf=textfilter: value. + set_text(tf(val)), + signame, + weak=False) dispatcher.connect(lambda val: setattr(self, 'lastSeenInputTime', val), - 'input time', weak=False) + 'input time', + weak=False) master.show_all() def refreshCurveView(self): wtree = self.wtree - mtimes = [os.path.getmtime(f) for f in [ - 'light9/curvecalc/curveview.py', - 'light9/curvecalc/zoomcontrol.py', - ]] + mtimes = [ + os.path.getmtime(f) for f in [ + 'light9/curvecalc/curveview.py', + 'light9/curvecalc/zoomcontrol.py', + ] + ] if (not hasattr(self, 'curvesetView') or - self.curvesetView._mtimes != mtimes): + self.curvesetView._mtimes != mtimes): print "reload curveview.py" curvesVBox = wtree.get_object("curves") zoomControlBox = wtree.get_object("zoomControlBox") [curvesVBox.remove(c) for c in curvesVBox.get_children()] - [zoomControlBox.remove(c) for c in - zoomControlBox.get_children()] + [zoomControlBox.remove(c) for c in zoomControlBox.get_children()] try: linecache.clearcache() reload(curveview) @@ -443,8 +459,8 @@ class Main(object): self.curvesetView.live = False # mem problem somewhere; need to hold a ref to this - self.curvesetView = curveview.Curvesetview(self.graph, - curvesVBox, zoomControlBox, self.curveset) + self.curvesetView = curveview.Curvesetview( + self.graph, curvesVBox, zoomControlBox, self.curveset) self.curvesetView._mtimes = mtimes # this is scheduled after some tk shuffling, to @@ -463,6 +479,7 @@ class MaxTime(object): """ looks up the time in seconds for the session's current song """ + def __init__(self, graph, session): self.graph, self.session = graph, session graph.addHandler(self.update) @@ -480,6 +497,7 @@ class MaxTime(object): def get(self): return self.maxtime + def launch(args, graph, session, opts, startTime, music): try: @@ -492,7 +510,7 @@ def launch(args, graph, session, opts, s pass curveset = Curveset(graph=graph, session=session) - + log.debug("startup: output %s", time.time() - startTime) mt = MaxTime(graph, session) @@ -501,9 +519,8 @@ def launch(args, graph, session, opts, s start = Main(graph, opts, session, curveset, music) out = Output(graph, session, music, curveset, start.currentSubterms) + dispatcher.send("show all") - dispatcher.send("show all") - if opts.startup_only: log.debug("quitting now because of --startup-only") return @@ -515,22 +532,24 @@ def launch(args, graph, session, opts, s requestHandler.set_status(404) requestHandler.write("not hovering over any time") return - with graph.currentState( - tripleFilter=(session, L9['currentSong'], None)) as g: + with graph.currentState(tripleFilter=(session, L9['currentSong'], + None)) as g: song = g.value(session, L9['currentSong']) - json.dump({"song": song, "hoverTime" : times[0]}, requestHandler) - + json.dump({"song": song, "hoverTime": times[0]}, requestHandler) + serveCurveEdit(networking.curveCalc.port, hoverTimeResponse, start.curveset) + def main(): startTime = time.time() parser = optparse.OptionParser() parser.set_usage("%prog [opts] [songURI]") - parser.add_option("--debug", action="store_true", - help="log at DEBUG") - parser.add_option("--reload", action="store_true", + parser.add_option("--debug", action="store_true", help="log at DEBUG") + parser.add_option("--reload", + action="store_true", help="live reload of themes and code") - parser.add_option("--startup-only", action='store_true', + parser.add_option("--startup-only", + action='store_true', help="quit after loading everything (for timing tests)") parser.add_option("--profile", help='"hotshot" or "stat"') clientsession.add_option(parser) @@ -540,16 +559,15 @@ def main(): log.debug("startup: music %s", time.time() - startTime) - session = clientsession.getUri('curvecalc', opts) music = Music() graph = SyncedGraph(networking.rdfdb.url, "curvecalc") - graph.initiallySynced.addCallback( - lambda _: launch(args, graph, session, opts, startTime, music)) + graph.initiallySynced.addCallback(lambda _: launch(args, graph, session, + opts, startTime, music)) from light9 import prof prof.run(reactor.run, profile=opts.profile) + main() -