Changeset - ce4fffe8e413
[Not reviewed]
default
0 4 0
drewp@bigasterisk.com - 13 years ago 2012-07-18 09:59:10
drewp@bigasterisk.com
update SC to read rdf graph
Ignore-this: 7f6788bae887723c9ac12644c1a382da
4 files changed with 93 insertions and 126 deletions:
0 comments (0 inline, 0 general)
bin/rdfdb
Show inline comments
 
@@ -74,6 +74,11 @@ them here. We should be able to take pat
 
and keep updating the same data (e.g. a stream of changes as the user
 
drags a slider) and collapse them into a single edit for clarity.
 

	
 
proposed rule feature:
 
rdfdb should be able to watch a pair of (sourceFile, rulesFile) and
 
rerun the rules when either one changes. Should the sourceFile be able
 
to specify its own rules file? That would be easier configuration.
 

	
 
"""
 
from twisted.internet import reactor
 
import twisted.internet.error
bin/subcomposer
Show inline comments
 
@@ -5,26 +5,21 @@ import sys,os,time,atexit
 
from optparse import OptionParser
 
import Tkinter as tk
 
import louie as dispatcher
 
from twisted.internet import reactor, tksupport, task
 

	
 
import run_local
 
from light9.dmxchanedit import Levelbox
 
from light9 import dmxclient, Patch, Submaster, showconfig
 
from light9 import dmxclient, Patch, Submaster, showconfig, prof
 
from light9.uihelpers import toplevelat
 
from light9.rdfdb.syncedgraph import SyncedGraph
 

	
 
class Subcomposer(tk.Frame):
 
    def __init__(self, master, levelboxopts=None, dmxdummy=0, numchannels=72,
 
        use_persistentlevels=0):
 
    def __init__(self, master, graph):
 
        tk.Frame.__init__(self, master, bg='black')
 
        self.dmxdummy = dmxdummy
 
        self.numchannels = numchannels
 
        self.graph = graph
 

	
 
        self.levels = [0]*numchannels # levels should never get overwritten, just edited
 

	
 
        self.levelbox = Levelbox(self, num_channels=numchannels)
 
        self.levelbox = Levelbox(self, graph)
 
        self.levelbox.pack(side='top')
 
        # the dmx levels we edit and output, range is 0..1 (dmx chan 1 is
 
        # the 0 element)
 
        self.levelbox.setlevels(self.levels)
 

	
 
        self.savebox = EntryCommand(self, cmd=self.savenewsub)
 
        self.savebox.pack(side='top')
 
@@ -33,31 +28,17 @@ class Subcomposer(tk.Frame):
 
        self.loadbox.pack(side='top')
 

	
 
        def alltozero():
 
            self.set_levels([0] * self.numchannels)
 
            dispatcher.send("levelchanged")
 
            for lev in self.levelbox.levels:
 
                lev.setlevel(0)
 

	
 
        tk.Button(self, text="all to zero", command=alltozero).pack(side='top')
 

	
 
        dispatcher.connect(self.levelchanged,"levelchanged")
 
        dispatcher.connect(self.sendupdate,"levelchanged")
 

	
 
        if use_persistentlevels:
 
            self.persistentlevels()
 

	
 
        self.lastupdate=0 # time we last sent to dmx
 

	
 
        self.lastsent=[] # copy of levels
 
        dispatcher.connect(self.sendupdate, "levelchanged")
 

	
 
    def fill_both_boxes(self, subname):
 
        for box in [self.savebox, self.loadbox]:
 
            box.set(subname)
 

	
 
    def persistentlevels(self):
 
        """adjusts levels from subcomposer.savedlevels, if possible; and
 
        arranges to save the levels in that file upon exit"""
 
        self.load_levels()
 
        atexit.register(self.save_levels)
 

	
 
    def save_levels(self, *args):
 
        levelfile = file("subcomposer.savedlevels","w")
 
        levelfile.write(" ".join(map(str, self.levels)))
 
@@ -70,13 +51,6 @@ class Subcomposer(tk.Frame):
 
        except IOError:
 
            pass
 

	
 
    def levelchanged(self, channel=None, newlevel=None):
 
        if channel is not None and newlevel is not None:
 
            if channel>len(self.levels):
 
                return
 
            self.levels[channel-1]=max(0,min(1,float(newlevel)))
 
        self.levelbox.setlevels(self.levels)
 

	
 
    def savenewsub(self, subname):
 
        leveldict={}
 
        for i,lev in zip(range(len(self.levels)),self.levels):
 
@@ -92,23 +66,20 @@ class Subcomposer(tk.Frame):
 
        self.set_levels(s.get_dmx_list())
 
        dispatcher.send("levelchanged")
 

	
 
    def toDmxLevels(self):
 
        # the dmx levels we edit and output, range is 0..1 (dmx chan 1 is
 
        # the 0 element)
 
        out = {}
 
        for lev in self.levelbox.levels:
 
            out[lev.channelnum] = lev.currentlevel
 
        if not out:
 
            return []
 
        
 
        return [out.get(i, 0) for i in range(max(out.keys()) + 1)]
 
     
 
    def sendupdate(self):
 
        if not self.dmxdummy:
 
            dmxclient.outputlevels(self.levels)
 
            self.lastupdate = time.time()
 
            self.lastsent = self.levels[:]
 
        dmxclient.outputlevels(self.toDmxLevels(), twisted=True)
 

	
 
    def considersendupdate(self, use_after_loop=0):
 
        """If use_after_loop is true, it is the period of the after loop."""
 
        if self.lastsent != self.levels or time.time() > self.lastupdate + 1:
 
            self.sendupdate()
 
        if use_after_loop:
 
            self.after(use_after_loop, self.considersendupdate, use_after_loop)
 

	
 
    def set_levels(self, levels):
 
        oldLen = len(self.levels)
 
        self.levels[:] = levels + [0] * (oldLen - len(levels))
 
        dispatcher.send("levelchanged")
 

	
 
class EntryCommand(tk.Frame):
 
    def __init__(self, master, verb="Save", cmd=None):
 
@@ -131,23 +102,6 @@ class EntryCommand(tk.Frame):
 
        self.entry.insert(0, text)
 

	
 

	
 
def open_sub_editing_window(subname, use_mainloop=1, dmxdummy=0):
 
    if use_mainloop:
 
        toplevel = tk.Tk()
 
    else:
 
        toplevel = tk.Toplevel()
 
    if dmxdummy:
 
        dummy_str = ' (dummy)'
 
    else:
 
        dummy_str = ''
 
    toplevel.title("Subcomposer: %s%s" % (subname, dummy_str))
 
    sc = Subcomposer(toplevel, use_persistentlevels=0, dmxdummy=dmxdummy)
 
    sc.pack(fill='both', expand=1)
 
    sc.loadsub(subname)
 
    sc.considersendupdate(use_after_loop=10)
 
    if use_mainloop:
 
        tk.mainloop()
 

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

	
 
if __name__ == "__main__":
 
@@ -162,10 +116,9 @@ if __name__ == "__main__":
 
    if not opts.no_geometry:
 
        toplevelat("subcomposer", root)
 

	
 
    sc = Subcomposer(root, dmxdummy=0,
 
                     #numchannels=276 # use this to see all the skyline dims
 
                     #numchannels=118
 
                     )
 
    graph = SyncedGraph("subcomposer")
 

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

	
 
    tk.Label(root,text="Bindings: B1 adjust level; B2 set full; B3 instant bump",
 
@@ -176,11 +129,7 @@ if __name__ == "__main__":
 
        sc.loadsub(args[0])
 
        sc.fill_both_boxes(args[0])
 

	
 
    while 1:
 
        try:
 
            root.update()
 
        except tk.TclError:
 
            break
 
    task.LoopingCall(sc.sendupdate).start(1)
 

	
 
        sc.considersendupdate()
 
        time.sleep(.01)
 
    tksupport.install(root,ms=10)
 
    prof.run(reactor.run, profile=False)
light9/dmxchanedit.py
Show inline comments
 
@@ -3,11 +3,23 @@
 
widget to show all dmx channel levels and allow editing. levels might
 
not actually match what dmxserver is outputting.
 

	
 
proposal for new focus and edit system:
 
- rows can be selected
 
- the chan number or label can be used to select rows. dragging over rows brings all of them into or out of the current selection
 
- numbers drag up and down (like today)
 
- if you drag a number in a selected row, all the selected numbers change
 
- if you start dragging a number in an unselected row, your row becomes the new selection and then the edit works
 

	
 

	
 
proposal for new attribute system:
 
- we always want to plan some attributes for each light: where to center; what stage to cover; what color gel to apply; whether the light is burned out
 
- we have to stop packing these into the names. Names should be like 'b33' or 'blue3' or just '44'. maybe 'blacklight'.
 

	
 
"""
 
from __future__ import nested_scopes,division
 
import Tkinter as tk
 
from light9 import Patch
 
from light9.uihelpers import make_frame, colorlabel, eventtoparent
 
from rdflib import RDF
 
from light9.namespaces import L9
 
import louie as dispatcher
 

	
 
stdfont = ('Arial', 9)
 
@@ -18,18 +30,27 @@ def gradient(lev, low=(80,80,180), high=
 
     return col
 

	
 
class Onelevel(tk.Frame):
 
    """a name/level pair"""
 
    def __init__(self, parent, channelnum):
 
        """channelnum is 1..68, like the real dmx"""
 
    """a name/level pair
 

	
 
    source data is like this:
 
        ch:b11-c     a :Channel;
 
         :output dmx:c54;
 
         rdfs:label "b11-c" .
 
    """
 
    def __init__(self, parent, graph, channelUri):
 
        tk.Frame.__init__(self,parent, height=20)
 
        self.graph = graph
 
        self.uri = channelUri
 
        self.currentlevel = 0 # the level we're displaying, 0..1
 

	
 
        self.channelnum=channelnum
 
        self.currentlevel=0 # the level we're displaying, 0..1
 
        
 
        # no statement yet
 
        self.channelnum = int(
 
             self.graph.value(self.uri, L9['output']).rsplit('/c')[-1])
 

	
 
        # 3 widgets, left-to-right:
 

	
 
        # channel number -- will turn yellow when being altered
 
        self.num_lab = tk.Label(self, text=str(channelnum),
 
        self.num_lab = tk.Label(self, text=str(self.channelnum),
 
                                width=3, bg='grey40', 
 
                                fg='white',
 
                                font=stdfont,
 
@@ -37,7 +58,7 @@ class Onelevel(tk.Frame):
 
        self.num_lab.pack(side='left')
 

	
 
        # text description of channel
 
        self.desc_lab=tk.Label(self, text=Patch.get_channel_name(channelnum),
 
        self.desc_lab=tk.Label(self, text=self.graph.label(self.uri),
 
                               width=14,
 
                               font=stdfont,
 
                               anchor='w',
 
@@ -47,16 +68,9 @@ class Onelevel(tk.Frame):
 

	
 
        # current level of channel, shows intensity with color
 
        self.level_lab = tk.Label(self, width=3, bg='lightBlue',
 
                                  anchor='e', 
 
                                  anchor='e', font=stdfont,
 
                                  padx=1, pady=0, bd=0, height=1)
 
        self.level_lab.pack(side='left')
 
        # setting the font in the label somehow makes tk run a low
 
        # slower. Magically, startup is much faster if tk can layout
 
        # the window with some standard font in the rows (so the row
 
        # heights are all fixed and taller?), and then I replace the
 
        # last font. Tk resizes the window faster than you can see,
 
        # but startup is still fast. Very weird.
 
        self.after(1, lambda: self.level_lab.config(font=stdfont))
 

	
 
        self.setlevel(0)
 
        self.setupmousebindings()
 
@@ -68,15 +82,15 @@ class Onelevel(tk.Frame):
 
            self._start_lev=self.currentlevel
 
        def b1motion(ev):
 
            delta=self._start_y-ev.y
 
            self.changelevel(self._start_lev+delta*.005)
 
            self.setlevel(self._start_lev+delta*.005)
 
        def b1up(ev):
 
            self.desc_lab.config(bg='black')
 
        def b3up(ev):
 
            self.changelevel(0.0)
 
            self.setlevel(0.0)
 
        def b3down(ev):
 
            self.changelevel(1.0)
 
            self.setlevel(1.0)
 
        def b2down(ev): # same thing for now
 
            self.changelevel(1.0)
 
            self.setlevel(1.0)
 

	
 
        # make the buttons work in the child windows
 
        for w in self.winfo_children():
 
@@ -88,7 +102,6 @@ class Onelevel(tk.Frame):
 
                           ('<ButtonPress-3>', b3down)):
 

	
 
                w.bind(e,func)
 
#                w.bind(e,lambda ev,e=e: eventtoparent(ev,e))
 
        
 
    def colorlabel(self):
 
        """color the level label based on its own text (which is 0..100)"""
 
@@ -96,39 +109,44 @@ class Onelevel(tk.Frame):
 
        lev=float(txt)/100
 
        self.level_lab.config(bg=gradient(lev))
 

	
 
    def setlevel(self,newlev):
 
    def setlevel(self, newlev):
 
        """the main program is telling us to change our
 
        display. newlev is 0..1"""
 
        self.currentlevel=newlev
 
        newlev="%d"%(newlev*100)
 
        self.currentlevel = min(1, max(0, newlev))
 
        newlev = "%d" % (self.currentlevel * 100)
 
        olddisplay=self.level_lab.cget('text')
 
        if newlev!=olddisplay:
 
            self.level_lab.config(text=newlev)
 
            self.colorlabel()
 
        dispatcher.send("levelchanged", channel=self.uri, newlevel=newlev)
 

	
 
    def getlevel(self):
 
        """returns currently displayed level, 0..1"""
 
        return self.currentlevel
 

	
 
    def changelevel(self,newlev):
 
class Levelbox(tk.Frame):
 
    def __init__(self, parent, graph):
 
        tk.Frame.__init__(self,parent)
 

	
 
        """the user is adjusting the level on this widget.  the main
 
        program needs to hear about it. then the main program will
 
        call setlevel()"""
 
        self.graph = graph
 
        graph.addHandler(self.updateChannels)
 

	
 
        dispatcher.send("levelchanged",channel=self.channelnum,newlevel=newlev)
 
    
 
class Levelbox(tk.Frame):
 
    def __init__(self, parent, num_channels=68):
 
        tk.Frame.__init__(self,parent)
 
    def updateChannels(self):
 
        """(re)make Onelevel boxes for the defined channels"""
 

	
 
        [ch.destroy() for ch in self.winfo_children()]
 
        self.levels = [] # Onelevel objects
 

	
 
        rows = 37
 
        frames = [make_frame(self) for x in range((num_channels // rows) + 1)]
 
        chans = list(self.graph.subjects(RDF.type, L9.Channel))
 
        cols = 2
 
        rows = len(chans) // cols
 

	
 
        for channel in range(1, num_channels+1):
 
        def make_frame(parent):
 
             f = tk.Frame(parent, bd=0, bg='black')
 
             f.pack(side='left')
 
             return f
 
        
 
        columnFrames = [make_frame(self) for x in range(cols)]
 

	
 
        for i, channel in enumerate(chans): # sort?
 
            # frame for this channel
 
            f = Onelevel(frames[channel // rows],channel)
 
            f = Onelevel(columnFrames[i // rows], self.graph, channel)
 

	
 
            self.levels.append(f)
 
            f.pack(side='top')
light9/uihelpers.py
Show inline comments
 
@@ -15,11 +15,6 @@ windowlocations = {
 
    'scenes' : '504x198+462+12',
 
}
 

	
 
def make_frame(parent):
 
    f = Frame(parent, bd=0, bg='black')
 
    f.pack(side='left')
 
    return f
 

	
 
def bindkeys(root,key, func):
 
    root.bind(key, func)
 
    for w in root.winfo_children():
0 comments (0 inline, 0 general)