Changeset - ffecebbdcc95
[Not reviewed]
default
0 2 0
drewp@bigasterisk.com - 13 years ago 2012-06-16 02:53:49
drewp@bigasterisk.com
curvecalc serves the time you're hovering over
Ignore-this: 1175a24abc1de8f6e995e925bb25caab
2 files changed with 21 insertions and 3 deletions:
0 comments (0 inline, 0 general)
bin/curvecalc
Show inline comments
 
#!bin/python
 

	
 
"""
 
now launches like this:
 
% bin/curvecalc http://light9.bigasterisk.com/show/dance2007/song1
 

	
 

	
 

	
 
todo: curveview should preserve more objects, for speed maybe
 

	
 
"""
 
from __future__ import division
 

	
 
from twisted.internet import gtk2reactor
 
gtk2reactor.install()
 
from twisted.internet import reactor
 

	
 
import time, textwrap, os, optparse, gtk, linecache, signal, traceback
 
import time, textwrap, os, optparse, gtk, linecache, signal, traceback, json
 
import louie as dispatcher 
 
from rdflib import URIRef, Graph, Literal, RDF, RDFS
 
import logging
 
log = logging.getLogger()
 

	
 
import run_local
 
from light9 import showconfig, prof
 
from light9 import showconfig, prof, networking
 
from light9.curvecalc.curve import Curveset
 
from light9.curvecalc import curveview 
 
from light9.curvecalc.musicaccess import Music, currentlyPlayingSong
 
from light9.wavelength import wavelength
 
from light9.namespaces import L9
 
from light9.curvecalc.subterm import read_all_subs, savekey, graphPathForSubterms
 
from light9.curvecalc.subtermview import add_one_subterm
 
from light9.curvecalc.output import Output
 
from light9.gtkpyconsole import togglePyConsole
 

	
 
def makeGraph():
 
    graphOrig = showconfig.getGraph()
 
    graph = Graph() # a copy, since we're going to add subs into it
 
    for s in graphOrig:
 
        graph.add(s)
 
    read_all_subs(graph)
 
    return graph
 

	
 
class SubtermExists(ValueError):
 
    pass
 

	
 
class Main(object):
 
    def __init__(self, graph, opts, song, curveset, subterms, music):
 
        self.graph, self.opts, self.song = graph, opts, song
 
        self.curveset, self.subterms, self.music = curveset, subterms, music
 
        self.lastSeenInputTime = 0
 

	
 
        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)
 
        gtk.rc_parse("theme/marble-ice/gtk-2.0/gtkrc")
 
        gtk.rc_parse_string("""style "default" {font_name = "sans 7"}""")
 
        if self.opts.reload:
 
            self.refreshTheme()
 
        mainwin.show_all()
 

	
 
        mainwin.connect("delete-event", lambda *args: reactor.crash())
 
        mainwin.set_title("curvecalc - %s" % graph.label(song))
 
        mainwin.parse_geometry("1x1-0+0")
 

	
 
        # this is the only one i found that would set the size right,
 
        # but it's a minimum size, which i don't really want
 
        mainwin.set_size_request(1678, 922)
 

	
 

	
 
        wtree.get_object("subterms").connect("add", self.onSubtermChildAdded)
 
        self.add_subterms_for_song(song, curveset, subterms)
 
        self.refreshCurveView()       
 
        
 
        self.makeStatusLines(wtree.get_object("status"))
 

	
 
        def connect(w):
 
            w.drag_dest_set(flags=gtk.DEST_DEFAULT_ALL,
 
                            targets=[('text/uri-list', 0, 0)],
 
                            actions=gtk.gdk.ACTION_COPY)
 
            w.connect("drag-data-received", self.onDataReceived)
 
        connect(mainwin)
 
        # that's not enough- deeper windows don't accept the
 
        # event. 
 
        mainwin.forall(connect) # not very effective
 
        connect(wtree.get_object("subterms")) # works for that area
 

	
 
        # may not work
 
        wtree.get_object("paned1").set_position(600)
 

	
 
    def onDataReceived(self, widget, context, x, y, selection,
 
                       targetType, time):
 
        uri = URIRef(selection.data.strip())
 
        subName = self.graph.label(uri)
 

	
 
        try:
 
            self.makeSubterm(subName, withCurve=True)
 
        except SubtermExists:
 
            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
 
        print "time", t
 
        curveView.add_points([(t - .5, 0),
 
                              (t, 1)])
 
            
 

	
 
    def onNewCurve(self, *args):
 
        dialog = self.wtree.get_object("newCurve")
 
        entry = self.wtree.get_object("newCurveName")
 
        # if you don't have songx, that should be the suggested name
 
        entry.set_text("")
 
        if dialog.run() == 1:
 
            self.curveset.new_curve(entry.get_text())
 
        dialog.hide()
 

	
 
    def onNewSubterm(self, *args):
 
        dialog = self.wtree.get_object("newSubterm")
 
        # the plan is to autocomplete this on existing subterm names
 
        # (but let you make one up, too)
 
        entry = self.wtree.get_object("newSubtermName").get_children()[0]
 
        entry.set_text("")
 
        entry.grab_focus()
 
        if dialog.run() == 1:
 
            newname = entry.get_text()
 
            wc = self.wtree.get_object("newSubtermMakeCurve").get_active()
 
            self.makeSubterm(newname, withCurve=wc)
 
        dialog.hide()
 

	
 
    def makeSubterm(self, newname, withCurve=False):
 
        uri = L9['sub/%s' % newname]
 
        if (uri, RDF.type, L9.Subterm) in self.graph:
 
            raise SubtermExists("already have a subterm named %r" % newname)
 
        self.graph.add((uri, RDF.type, L9.Subterm))
 
        self.graph.add((uri, RDFS.label, Literal(newname)))
 
        add_one_subterm(self.graph, uri,
 
                        self.curveset, self.subterms,
 
                        self.wtree.get_object("subterms"),
 
                        None, show=True)
 
        if withCurve:
 
            self.curveset.new_curve(newname)
 

	
 
    def refreshTheme(self):
 
        gtk.rc_reparse_all()
 
        reactor.callLater(1, self.refreshTheme)
 

	
 
    def onSubtermChildAdded(self, subtermsTable, *args):
 
        # this would probably work, but isn't getting called
 
        v = subtermsTable.get_parent().props.vadjustment
 
        v.props.value = v.props.upper
 

	
 
    def onQuit(self, *args):
 
        reactor.crash()
 
        # there's a hang after this, maybe in sem_wait in two
 
        # threads. I don't know whose they are.
 
        os.kill(os.getpid(), signal.SIGKILL)
 

	
 
    def onCollapseAll(self, *args):
 
        self.curvesetView.collapseAll()
 

	
 
    def onCollapseNone(self, *args):
 
        self.curvesetView.collapseNone()
 

	
 
    def onDelete(self, *args):
 
        self.curvesetView.onDelete()
 

	
 
    def onPythonConsole(self, item):
 
        togglePyConsole(self, item, self.__dict__)
 
        
 
    def onSeeCurrentTime(self, item):
 
        dispatcher.send("see time")
 

	
 
    def onSeeTimeUntilEnd(self, item):
 
        dispatcher.send("see time until end")
 

	
 
    def onZoomAll(self, item):
 
        dispatcher.send("show all")
 

	
 
    def onPlayPause(self, item):
 
        # since the X coord in a curveview affects the handling, one
 
        # of them may be able to pick this up
 
        results = dispatcher.send("onPlayPause")
 
        times = [t for listener, t in results if t is not None]
 
        self.music.playOrPause(t=times[0] if times else None)
 

	
 
    def onSave(self, *args):
 
        savekey(self.song, self.subterms, self.curveset)
 

	
 
    def add_subterms_for_song(self, song, curveset, subterms):
 
        master = self.wtree.get_object("subterms")
 
        for st in self.graph.objects(song, L9['subterm']):
 
            log.info("song %s has subterm %s", song, st)
 
            add_one_subterm(self.graph,
 
                            self.graph.value(st, L9['sub']),
 
                            curveset,
 
                            subterms,
 
                            master,
 
                            self.graph.value(st, L9['expression']))
 
        master.show_all()
 

	
 
    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()[:5]
 
                                                     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)
 
            master.attach(key, 0, 1, row, row + 1)
 
            master.attach(value, 1, 2, row, row + 1)
 
            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: setattr(self, 'lastSeenInputTime', val),
 
                           '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',
 
            ]]
 

	
 
        if (not hasattr(self, 'curvesetView') or
 
            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()]
 
            try:
 
                linecache.clearcache()
 
                reload(curveview)
 

	
 
                # old ones are not getting deleted right
 
                if hasattr(self, 'curvesetView'):
 
                    self.curvesetView.live = False
 

	
 
                # mem problem somewhere; need to hold a ref to this
 
                self.curvesetView = curveview.Curvesetview(
 
                    curvesVBox, zoomControlBox, self.curveset)
 
                self.curvesetView._mtimes = mtimes
 

	
 
                # this is scheduled after some tk shuffling, to
 
                # try to minimize the number of times we redraw
 
                # the curve at startup. If tk is very slow, it's
 
                # ok. You'll just get some wasted redraws.
 
                self.curvesetView.goLive()
 
            except Exception:
 
                print "reload failed:"
 
                traceback.print_exc()
 
        if self.opts.reload:
 
            reactor.callLater(1, self.refreshCurveView)
 

	
 
    def onReloadSubs(self, *args): # wants to be ctrl-r  too
 
        read_all_subs(self.graph) # this will discover new subs (additive only)
 
        dispatcher.send('reload all subs') # this rereads each sub from its graph file
 
        dispatcher.send("all curves rebuild")
 

	
 

	
 
def main():
 
    startTime = time.time()
 
    parser = optparse.OptionParser()
 
    parser.set_usage("%prog [opts] [songURI]")
 
    parser.add_option("--sliders", action='store_true',
 
                      help='use hardware sliders')
 
    parser.add_option("--skip-music", action='store_true',
 
                      help="ignore music and smooth_music curve files")
 
    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',
 
                      help="quit after loading everything (for timing tests)")
 
    opts, args = parser.parse_args()
 

	
 
    logging.basicConfig(format="%(asctime)s %(levelname)-5s %(name)s %(filename)s:%(lineno)d: %(message)s")
 
    log.setLevel(logging.DEBUG if opts.debug else logging.INFO)
 

	
 
    log.debug("startup: music %s", time.time() - startTime)
 
    try:
 
        song = URIRef(args[0])
 
    except IndexError:
 
        song = currentlyPlayingSong()
 

	
 
    music = Music()
 
    graph = makeGraph()
 

	
 
    curveset = Curveset(sliders=opts.sliders)
 
    subterms = []
 

	
 
    curveset.load(basename=os.path.join(
 
        showconfig.curvesDir(),
 
        showconfig.songFilenameFromURI(song)),
 
                  skipMusic=opts.skip_music)
 

	
 
    subtermPath = graphPathForSubterms(song)
 
    try:
 
        graph.parse(subtermPath, format='n3')
 
    except IOError, e:
 
        if e.errno != 2:
 
            raise
 
        log.info("%s not found, starting with empty graph" % subtermPath)
 
    
 
    log.debug("startup: output %s", time.time() - startTime)
 
    out = Output(subterms, music)
 

	
 
    musicfilename = showconfig.songOnDisk(song)
 
    maxtime = wavelength(musicfilename)
 
    dispatcher.connect(lambda: maxtime, "get max time", weak=False)
 

	
 
    start = Main(graph, opts, song, curveset, subterms, music)
 

	
 
    dispatcher.send("max time", maxtime=maxtime)
 
    dispatcher.send("show all")
 
        
 
    if opts.startup_only:
 
        log.debug("quitting now because of --startup-only")
 
        return
 

	
 
    from twisted.web import server, resource
 
    class Hover(resource.Resource):
 
        isLeaf = True
 
        def render_GET(self, request):
 
            if request.path == '/hoverTime':
 
                results = dispatcher.send("onPlayPause")
 
                times = [t for listener, t in results if t is not None]
 
                if not times:
 
                    request.setResponseCode(404)
 
                    return "not hovering over any time"
 
                
 
                return json.dumps({"song":song, "hoverTime" : times[0]})
 
            raise NotImplementedError()
 

	
 
    reactor.listenTCP(networking.curveCalc.port,
 
                      server.Site(Hover()))
 

	
 

	
 
    prof.run(reactor.run, profile=False)
 

	
 
main()
light9/networking.py
Show inline comments
 
from urlparse import urlparse
 
from urllib import splitport
 
from showconfig import getGraph, showUri
 
from namespaces import L9
 

	
 
class ServiceAddress(object):
 
    def __init__(self, service):
 
        self.service = service
 

	
 
    def _url(self):
 
        graph = getGraph()
 
        net = graph.value(showUri(), L9['networking'])
 
        return str(graph.value(net, self.service))
 

	
 
    @property
 
    def port(self):
 
        _, netloc, _, _, _, _ = urlparse(self._url())
 
        host, port = splitport(netloc)
 
        return int(port)
 

	
 
    @property
 
    def host(self):
 
        _, netloc, _, _, _, _ = urlparse(self._url())
 
        host, port = splitport(netloc)
 
        return host
 

	
 
    @property
 
    def url(self):
 
        return self._url()
 

	
 
    def path(self, more):
 
        return self.url + str(more)
 

	
 
dmxServer = ServiceAddress(L9['dmxServer'])
 
musicPlayer = ServiceAddress(L9['musicPlayer'])
 
keyboardComposer = ServiceAddress(L9['keyboardComposer'])
 

	
 
curveCalc = ServiceAddress(L9['curveCalc'])
0 comments (0 inline, 0 general)