Files @ ee07ea5cd46b
Branch filter:

Location: light9/bin/subcomposer - annotation

Drew Perttula
start makefile for tkdnd build
Ignore-this: 4fbfd8185354989db9a1db4449710c2e
b5efddd80dad
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
1a84c5e83d3e
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
1a84c5e83d3e
321fc6150ee3
2c782ca93e73
0f7e99d02dc6
1a84c5e83d3e
5442f5d8979a
ce4fffe8e413
6885c2fa9369
1a84c5e83d3e
bdfdfea84510
1a84c5e83d3e
321fc6150ee3
9dd2baa41cca
ce4fffe8e413
f29788d1c8c9
f987eb9c3672
f987eb9c3672
f987eb9c3672
f987eb9c3672
bf728997bfde
1a84c5e83d3e
1a84c5e83d3e
6885c2fa9369
6885c2fa9369
6885c2fa9369
6885c2fa9369
6885c2fa9369
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
bf728997bfde
bf728997bfde
321fc6150ee3
6885c2fa9369
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
bf728997bfde
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
6885c2fa9369
6885c2fa9369
f29788d1c8c9
1a84c5e83d3e
ce4fffe8e413
f29788d1c8c9
1a84c5e83d3e
321fc6150ee3
321fc6150ee3
1a84c5e83d3e
f987eb9c3672
bf728997bfde
bf728997bfde
f987eb9c3672
f987eb9c3672
bf728997bfde
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
bf728997bfde
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
1a84c5e83d3e
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
1a84c5e83d3e
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
1a84c5e83d3e
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
6885c2fa9369
321fc6150ee3
321fc6150ee3
2c782ca93e73
a6662d61ebcd
1a84c5e83d3e
1a84c5e83d3e
1a84c5e83d3e
1a84c5e83d3e
2c782ca93e73
a6662d61ebcd
1a84c5e83d3e
5442f5d8979a
1a84c5e83d3e
321fc6150ee3
321fc6150ee3
5442f5d8979a
1a84c5e83d3e
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
bf728997bfde
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
321fc6150ee3
f987eb9c3672
f987eb9c3672
2c782ca93e73
321fc6150ee3
2c782ca93e73
1a84c5e83d3e
1a84c5e83d3e
1a84c5e83d3e
1a84c5e83d3e
321fc6150ee3
8b307310cc1b
8b307310cc1b
f29788d1c8c9
2c782ca93e73
2c782ca93e73
321fc6150ee3
321fc6150ee3
1a84c5e83d3e
1a84c5e83d3e
1a84c5e83d3e
6885c2fa9369
6885c2fa9369
1a84c5e83d3e
ce4fffe8e413
f29788d1c8c9
ce4fffe8e413
321fc6150ee3
4e558643c952
f29788d1c8c9
ce4fffe8e413
ce4fffe8e413
#!bin/python
"""
subcomposer
  session
  observable(currentSub), a Submaster which tracks the graph

    EditChoice widget
      can change currentSub to another sub

    Levelbox widget
      watch observable(currentSub) for a new sub, and also watch currentSub for edits to push to the OneLevel widgets

        OneLevel widget
          UI edits are caught here and go all the way back to currentSub


"""
from __future__ import division, nested_scopes
import time, logging
from optparse import OptionParser
import logging
import Tkinter as tk
import louie as dispatcher
from twisted.internet import reactor, tksupport, task
from rdflib import URIRef

from run_local import log
from light9.dmxchanedit import Levelbox
from light9 import dmxclient, Patch, Submaster, prof
from light9.uihelpers import toplevelat
from light9.rdfdb.syncedgraph import SyncedGraph
from light9.rdfdb import clientsession
from light9.tkdnd import initTkdnd
from light9.namespaces import L9
from light9.observable import Observable
from light9.editchoice import EditChoice, Local


class Subcomposer(tk.Frame):
    """
    <session> l9:currentSub ?sub is the URI of the sub we're tied to for displaying and
    editing. If we don't have a currentSub, then we're actually
    editing a session-local sub called <session> l9:currentSub <sessionLocalSub>

    I'm not sure that Locals should even be PersistentSubmaster with
    uri and graph storage, but I think that way is making fewer
    special cases.

    Contains an EditChoice widget

    Dependencies:

      graph (?session :currentSub ?s) -> self.currentSub
      self.currentSub -> graph
      self.currentSub -> self._currentChoice (which might be Local)
      self._currentChoice (which might be Local) -> self.currentSub

      inside the current sub:
      graph -> Submaster levels (handled in Submaster)
      Submaster levels -> OneLevel widget
      OneLevel widget -> Submaster.editLevel
      Submaster.editLevel -> graph (handled in Submaster)

    """
    def __init__(self, master, graph, session):
        tk.Frame.__init__(self, master, bg='black')
        self.graph = graph
        self.session = session

        # this is a PersistentSubmaster (even for local) or None if we're not initialized
        self.currentSub = Observable(None)

        self._currentChoice = Observable(Local)

        def pc(val):
            log.info("change viewed sub to %s", val)
        self._currentChoice.subscribe(pc)

        EditChoice(self, self.graph, self._currentChoice).frame.pack(side='top')

    def setupSubChoiceLinks(self):

        def ann():
            print "currently: session=%s currentSub=%r _currentChoice=%r" % (
                self.session, self.currentSub(), self._currentChoice())

        @graph.addHandler
        def graphChanged():
            s = graph.value(self.session, L9['currentSub'])
            if s is None:
                s = self.makeLocal()
            self.currentSub(Submaster.PersistentSubmaster(graph, s))

        @self.currentSub.subscribe
        def subChanged(newSub):
            if newSub is None:
                graph.patchObject(self.session,
                                  self.session, L9['currentSub'], None)
                return
            self.sendupdate()
            graph.patchObject(self.session,
                              self.session, L9['currentSub'], newSub.uri)

            if newSub and 'local' in newSub.uri: # wrong- use rdf:type or something?
                self._currentChoice(Local)
            else:
                # i think right here is the point that the last local
                # becomes garbage, and we could clean it up. 
                self._currentChoice(newSub.uri)

        dispatcher.connect(self.levelsChanged, "sub levels changed")
            
        @self._currentChoice.subscribe
        def choiceChanged(newChoice):
            if newChoice is Local:
                newChoice = self.makeLocal()
            if newChoice is not None:
                self.currentSub(Submaster.PersistentSubmaster(
                    graph, newChoice))

    def levelsChanged(self, sub):
        if sub == self.currentSub():
            self.sendupdate()
        
    def makeLocal(self):
        # todo: put a type on this, so subChanged can identify it right
        # todo: where will these get stored, or are they local to this
        # subcomposer process and don't use PersistentSubmaster at all?
        return URIRef("http://local/%s" % time.time())
        
    def setupLevelboxUi(self):
        self.levelbox = Levelbox(self, graph, self.currentSub)
        self.levelbox.pack(side='top')

        tk.Button(self, text="All to zero",
             command=lambda *args: self.currentSub().clear()).pack(side='top')

    def savenewsub(self, subname):
        leveldict={}
        for i,lev in zip(range(len(self.levels)),self.levels):
            if lev!=0:
                leveldict[Patch.get_channel_name(i+1)]=lev

        s=Submaster.Submaster(subname,leveldict=leveldict)
        s.save()

    def sendupdate(self):
        d = self.currentSub().get_dmx_list()
        dmxclient.outputlevels(d, twisted=True)


def launch(opts, args, root, graph, session):
    if not opts.no_geometry:
        toplevelat("subcomposer - %s" % opts.session, root, graph, session)

    sc = Subcomposer(root, graph, session)
    sc.pack()

    tk.Label(root,text="Bindings: B1 adjust level; B2 set full; B3 instant bump",
             font="Helvetica -12 italic",anchor='w').pack(side='top',fill='x')

    if len(args) == 1:
        # it might be a little too weird that cmdline arg to this
        # process changes anything else in the same session. But also
        # I'm not sure who would ever make 2 subcomposers of the same
        # session (except when quitting and restarting, to get the
        # same window pos), so maybe it doesn't matter. But still,
        # this tool should probably default to making new sessions
        # usually instead of loading the same one
        graph.patchObject(session,
                          session, L9['currentSub'], URIRef(args[0]))

    task.LoopingCall(sc.sendupdate).start(10)


#############################

if __name__ == "__main__":
    parser = OptionParser(usage="%prog [suburi]")
    parser.add_option('--no-geometry', action='store_true',
                      help="don't save/restore window geometry")
    clientsession.add_option(parser)
    opts, args = parser.parse_args()

    logging.basicConfig(level=logging.DEBUG)

    root=tk.Tk()
    root.config(bg='black')
    root.tk_setPalette("#004633")

    initTkdnd(root.tk, 'tkdnd/trunk/')

    graph = SyncedGraph("subcomposer")
    session = clientsession.getUri('subcomposer', opts)

    graph.initiallySynced.addCallback(lambda _: launch(opts, args, root, graph, session))

    root.protocol('WM_DELETE_WINDOW', reactor.stop)
    tksupport.install(root,ms=10)
    prof.run(reactor.run, profile=False)