changeset 516:73b181155555

curvecalc ui touchups, adjustable pane, curve draw speedup, restructured main layout code Ignore-this: b0468548ef9f276dea4a8689dc425b24
author drewp@bigasterisk.com
date Mon, 29 Jun 2009 04:06:17 +0000
parents d541914c8716
children f15ffbfc5cd6
files bin/curvecalc light9/curve.py light9/zoomcontrol.py
diffstat 3 files changed, 159 insertions(+), 141 deletions(-) [+]
line wrap: on
line diff
--- a/bin/curvecalc	Mon Jun 29 02:18:45 2009 +0000
+++ b/bin/curvecalc	Mon Jun 29 04:06:17 2009 +0000
@@ -203,6 +203,9 @@
             dispatcher.send("expr_error", sender=self.subexpr, exc=str(e))
             return Submaster.Submaster('Error: %s' % str(e), temporary=True)
 
+    def __repr__(self):
+        return "<Subterm %s %s>" % (self.submaster, self.subexpr)
+
 class Subtermview(tk.Frame):
     def __init__(self, master, graph, st, **kw):
         self.subterm = st
@@ -271,7 +274,8 @@
             self.lastsendtime = now
             self.lastsendlevs = levs
 
-def create_status_lines(master):
+def makeStatusLines(master):
+    """various labels that listen for dispatcher signals"""
     for signame,textfilter in [
         ('input time',lambda t: "%.2fs"%t),
         ('output levels',
@@ -282,13 +286,13 @@
         ('update period',lambda t: "%.1fms"%(t*1000)),
         ('update status',lambda t: str(t)),
         ]:
-        l = tk.Label(master,anchor='w',justify='left')
+        l = tk.Label(master, anchor='w', justify='left', text='%s:' % signame)
         l.pack(side='top',fill='x')
         dispatcher.connect(lambda val,l=l,sn=signame,tf=textfilter:
                            l.config(text=sn+": "+tf(val)),
-                           signame,weak=0)
+                           signame, weak=False)
 
-def add_one_subterm(graph, subUri, curveset, subterms, root, master, expr=None):
+def add_one_subterm(graph, subUri, curveset, subterms, master, expr=None):
     subname = graph.label(subUri)
     if expr is None:
         expr = '%s(t)' % subname
@@ -302,7 +306,7 @@
 
     return term
 
-def sub_commands_tk(master, curveset, subterms, root, ssv, graph):
+def makeSubtermCommandRow(master, curveset, subterms, root, ssv, graph):
     """
     the row that starts with 'reload subs' button
     """
@@ -314,7 +318,7 @@
         graph.add((uri, RDF.type, L9.Subterm))
         graph.add((uri, RDFS.label, Literal(newname.get())))
         add_one_subterm(graph, uri,
-                        curveset, subterms, root, ssv, None)
+                        curveset, subterms, ssv, None)
         if evt.state & 4: # control key modifier
             curveset.new_curve(newname.get())
         newname.set('')
@@ -356,19 +360,17 @@
         graph.add((uri, L9['expression'], Literal(subterm.subexpr.expr)))
     return graph
 
-def add_subterms_for_song(graph, song, curveset, subterms, root, master):
+def add_subterms_for_song(graph, song, curveset, subterms, master):
     for st in graph.objects(song, L9['subterm']):
         try:
-            add_one_subterm(graph, graph.value(st, L9['sub']), curveset, subterms,
-                            root, master, graph.value(st, L9['expression']))
+            add_one_subterm(graph, graph.value(st, L9['sub']), curveset,
+                            subterms, master, graph.value(st, L9['expression']))
         except rdflib.exceptions.UniquenessError:
             print "working around curvecalc save corruption"
             # curvecalc put all the expressions on one subterm, which is wrong
             for expr in graph.objects(st, L9['expression']):
                 add_one_subterm(graph, graph.value(st, L9['sub']),
-                                curveset, subterms,
-                                root, master,
-                                expr)
+                                curveset, subterms, master, expr)
                 
 
 def graphPathForSubterms(song):
@@ -419,11 +421,6 @@
 
 
 def main():
-    root=tk.Tk()
-    root.tk_setPalette("gray50")
-    toplevelat("curvecalc",root)
-    root.tk_focusFollowsMouse()
-
     parser = optparse.OptionParser()
     parser.set_usage("%prog [opts] songURI")
     parser.add_option("--sliders", action='store_true',
@@ -439,53 +436,67 @@
 
     log.debug("music")
     music=Music()
-
-    zc = Zoomcontrol(root)
-    zc.pack(side='top',fill='x')
-
+    graph = makeGraph()
     curveset = Curveset(sliders=opts.sliders)
-    csv = Curvesetview(root, curveset)
-    csv.pack(side='top',fill='both',exp=1)
+    subterms = []
 
-    subtermArea = tk.Frame(root)
-    subtermArea.pack(side='top', fill='x')
-
-    sw = tk.ScrolledWindow(subtermArea)
-    sw.pack(fill='both')
-    subtermScroll = tk.Frame(sw.subwidget('window'))
-    subtermScroll.pack(fill='both')
-
-    graph = makeGraph()
-
+    graph.parse(graphPathForSubterms(song), format='n3')
+    
+    log.debug("output")
+    out = Output(subterms, music)
 
     musicfilename = showconfig.songOnDisk(song)
     maxtime = wavelength(musicfilename)
-    dispatcher.send("max time",maxtime=maxtime)
-    dispatcher.connect(lambda: maxtime, "get max time",weak=0)
-    dispatcher.send("show all")
+    dispatcher.connect(lambda: maxtime, "get max time", weak=False)
+
+
+    root=tk.Tk()
+    root.tk_setPalette("gray50")
+    toplevelat("curvecalc",root)
+    root.tk_focusFollowsMouse()
+    root.title("Curvemaster 3000MX - %s" % graph.label(song))
+
+    if 'fixed top rows':
+        zc = Zoomcontrol(root)
+        zc.pack(side='top', fill='x')
+
+    if 'panes':
+        panes = tk.PanedWindow(root, height=1)
+        panes.add('curvesetView')
+        panes.add('subterms')
+        panes.pack(side='top', fill='both', expand=True)
+
+        csv = Curvesetview(panes.subwidget('curvesetView'), curveset,
+                           height=400)
+        csv.pack(fill='both', expand=True)
+
+        subtermArea = tk.Frame(panes.subwidget('subterms'), height=100)
+        subtermArea.pack(fill='both', expand=True)
+
+        subtermScroll = tk.ScrolledWindow(subtermArea)
+        subtermScroll.pack(fill='both')
+
+    if 'fixed bottom rows':
+        makeSubtermCommandRow(root, curveset, subterms, root, subtermArea,
+                              graph).pack(side='top', fill='x')
+        makeStatusLines(root)
+
+        helpBox = tk.Frame(root)
+        createHelpLines(helpBox)
+        helpBox.pack(side='top', fill='x')
+
+    add_subterms_for_song(graph, song, curveset, subterms,
+                          subtermScroll.subwidget('window'))
+    setupKeyBindings(root, song, subterms, curveset)
+
+    # curvesetview must already exist, since this makes 'add_curve'
+    # signals for all the initial curves
     curveset.load(basename=os.path.join(showconfig.curvesDir(),
                                         showconfig.songFilenameFromURI(song)),
                   skipMusic=opts.skip_music)
-
-    subterms = []
-    sub_commands_tk(root, curveset, subterms, root, subtermArea,
-                    graph).pack(side='top',fill='both')
-
-    try:
-        graph.parse(graphPathForSubterms(song), format='n3')
-        add_subterms_for_song(graph, song, curveset, subterms, root,
-                              subtermScroll)
-    except OSError, e:
-        print e
-
-    log.debug("output")
-    out = Output(subterms, music)
-
-    setupKeyBindings(root, song, subterms, curveset)
-
-    create_status_lines(root)
-    createHelpLines(root)
-    root.title("Curvemaster 3000MX - %s" % graph.label(song))
+    
+    dispatcher.send("max time",maxtime=maxtime)
+    dispatcher.send("show all")
 
     tksupport.install(root,ms=20)
     log.debug("run")
--- a/light9/curve.py	Mon Jun 29 02:18:45 2009 +0000
+++ b/light9/curve.py	Mon Jun 29 04:06:17 2009 +0000
@@ -20,6 +20,9 @@
         self.points = []
         self._muted = False
 
+    def __repr__(self):
+        return "<Curve (%s points)>" % len(self.points)
+
     def muted():
         doc = "Whether to currently send levels (boolean, obviously)"
         def fget(self):
@@ -317,13 +320,13 @@
 
     def screen_from_world(self,p):
         start,end = self.zoom
-        ht = self.winfo_height()
-        return (p[0]-start)/(end-start)*self.winfo_width(), (ht-5)-p[1]*(ht-10)
+        ht = self.height
+        return (p[0]-start)/(end-start)*self.width, (ht-5)-p[1]*(ht-10)
 
     def world_from_screen(self,x,y):
         start,end = self.zoom
-        ht = self.winfo_height()
-        return x/self.winfo_width()*(end-start)+start, ((ht-5)-y)/(ht-10)
+        ht = self.height
+        return x/self.width*(end-start)+start, ((ht-5)-y)/(ht-10)
     
     def input_time(self, val, forceUpdate=False):
         # i tried various things to make this not update like crazy,
@@ -350,12 +353,13 @@
                 dispatcher.send("knob out", value=prevKey[1], curve=self.curve)
         
     def update_curve(self,*args):
-
+        self.width, self.height = self.winfo_width(), self.winfo_height()
+        
         self.zoom = dispatcher.send("zoom area")[0][1]
         cp = self.curve.points
 
         visible_x = (self.world_from_screen(0,0)[0],
-                     self.world_from_screen(self.winfo_width(),0)[0])
+                     self.world_from_screen(self.width, 0)[0])
 
         visible_idxs = self.curve.indices_between(visible_x[0], visible_x[1],
                                                   beyond=1)
@@ -368,7 +372,7 @@
         else:
             self['bg'] = 'black'
 
-        if self.winfo_height() < 40:
+        if self.height < 40:
             self._draw_gradient()
         else:
             self._draw_markers(visible_x)
@@ -381,7 +385,7 @@
 
     def _draw_gradient(self):
         gradient_res = 3
-        for x in range(0,self.winfo_width(),gradient_res):
+        for x in range(0, self.width, gradient_res):
             wx = self.world_from_screen(x,0)[0]
             mag = self.curve.eval(wx, allow_muting=False)
             if self.curve.muted:
@@ -412,11 +416,11 @@
         
     def _draw_one_marker(self,t,label):
         x = self.screen_from_world((t,0))[0]
-        ht = self.winfo_height()
+        ht = self.height
         self.create_line(x,ht,x,ht-20, fill='white',
                          tags=('curve',))
         self.create_text(x,ht-20,text=label,anchor='s', fill='white',
-                         tags=('curve',))
+                         font="arial 7", tags=('curve',))
 
 
     def _draw_line(self,visible_points):
@@ -602,6 +606,7 @@
 
         
 class Curveset:
+    
     curves = None # curvename : curve
     def __init__(self, sliders=False):
         """sliders=True means support the hardware sliders"""
@@ -622,7 +627,10 @@
         
     def load(self,basename, skipMusic=False):
         """find all files that look like basename-curvename and add
-        curves with their contents"""
+        curves with their contents
+
+        This fires 'add_curve' dispatcher events to announce the new curves.
+        """
         def sorter(name):
             return not name.endswith('music'), name
         for filename in sorted(glob.glob("%s-*"%basename), key=sorter):
@@ -632,7 +640,8 @@
             c=Curve()
             c.load(filename)
             curvename = curvename.replace('-','_')
-            self.add_curve(curvename,c)            
+            self.add_curve(curvename,c)
+            
     def save(self,basename):
         """writes a file for each curve with a name
         like basename-curvename"""
@@ -729,125 +738,123 @@
 
 class CurveRow(tk.Frame):
     """
-    one of the repeating curve rows
+    one of the repeating curve rows (including widgets on the left)
     """
     def __init__(self, master, name, curve, slider, knobEnabled):
         tk.Frame.__init__(self, master, relief='raised', bd=1)
 
+        self.collapsed = tk.IntVar()
+        self.muted = tk.IntVar()
+
         labelFont = "arial 8"
 
         leftside = tk.Frame(self)
         leftside.pack(side='left')
 
-        collapsed = tk.IntVar()
+        self.curveView = Curveview(self, curve, knobEnabled=knobEnabled)
+        self.curveView.pack(side='left', fill='both', expand=True)
+        self.curveView.config(height=100)
+
         txt = "curve '%s'" % name
         if len(name) > 7:
             txt = name
-        curve_name_label = tk.Label(leftside,text=txt,font=labelFont,
-                 width=15)
+        curve_name_label = tk.Label(leftside, text=txt, font=labelFont,width=15)
         curve_name_label.pack(side='left')
 
-        sliderLabel = None
-
         collapsed_cb = tk.Checkbutton(leftside, text="C",
-                       font=labelFont, variable=collapsed)
+                                      font=labelFont, variable=self.collapsed)
         collapsed_cb.pack(side='left')
-
-        def toggleCollapsed():
-            collapsed.set(not collapsed.get())
-
+        self.collapsed.trace('w', self.update_ui_to_collapsed_state)
+        dispatcher.connect(self.toggleCollapsed, "toggle collapse",
+                           sender=curve)
 
-        def update_ui_to_collapsed_state(*args):
-            if collapsed.get():
-                if sliderLabel:
-                    sliderLabel.pack_forget()
-                self.pack(exp=0)
-            else:
-                if sliderLabel:
-                    sliderLabel.pack(side='left')
-                self.pack(exp=1)
-        collapsed.trace('w', update_ui_to_collapsed_state)
+        self.default_bg = leftside['bg']
+        muted_cb = tk.Checkbutton(leftside, text="M", font=labelFont,
+                                  variable=self.muted)
+        muted_cb.pack(side='left')
+        self.muted.trace('w', self.sync_mute_to_curve)
+        dispatcher.connect(self.mute_changed, 'mute changed', sender=curve)
 
-
-        muted = tk.IntVar()
-        default_bg = leftside['bg']
-        muted_cb = tk.Checkbutton(leftside, text="M", font=labelFont,
-                       variable=muted)
-        muted_cb.pack(side='left')
-
+        self.sliderLabel = None
         if slider is not None:
             # slider should have a checkbutton, defaults to off for
             # music tracks
-            sliderLabel = tk.Label(leftside, text="Slider %s" % slider,
-                                   fg='#800000', font=labelFont)
-            sliderLabel.pack(side='left')
+            self.sliderLabel = tk.Label(leftside, text="Slider %s" % slider,
+                                        fg='#800000', font=labelFont)
+            self.sliderLabel.pack(side='left')
 
-        cv = Curveview(self, curve,
-                       knobEnabled=knobEnabled)
-        cv.pack(side='left',fill='both',exp=1)
+        # widgets that need recoloring when we tint the row:
+        self.widgets = [leftside, collapsed_cb, muted_cb,
+                        curve_name_label, self]
+        if self.sliderLabel:
+            self.widgets.append(self.sliderLabel)
 
-        def sync_mute_to_curve(*args):
-            """send value from Tk var to the master attribute inside Curve"""
-            new_mute = muted.get()
-            old_mute = cv.curve.muted
-            if new_mute == old_mute:
-                return
+    def toggleCollapsed(self):
+        self.collapsed.set(not self.collapsed.get())
 
-            cv.curve.muted = new_mute
-
-        muted.trace('w', sync_mute_to_curve)
+    def update_ui_to_collapsed_state(self, *args):
+        if self.collapsed.get():
+            if self.sliderLabel:
+                self.sliderLabel.pack_forget()
+            self.curveView.config(height=25)
+        else:
+            if self.sliderLabel:
+                self.sliderLabel.pack(side='left')
+            self.curveView.config(height=100)
 
-        def update_mute_look():
-            if muted.get():
-                new_bg = 'grey20'
-            else:
-                new_bg = default_bg
+    def sync_mute_to_curve(self, *args):
+        """send value from Tk var to the master attribute inside Curve"""
+        new_mute = self.muted.get()
+        old_mute = self.curveView.curve.muted
+        if new_mute == old_mute:
+            return
+
+        self.curveView.curve.muted = new_mute
 
-            widgets = [leftside, collapsed_cb, muted_cb, curve_name_label, self]
-            if sliderLabel:
-                widgets.append(sliderLabel)
-            for widget in widgets:
-                widget['bg'] = new_bg
+    def update_mute_look(self):
+        """set colors on the widgets in the row according to self.muted.get()"""
+        if self.muted.get():
+            new_bg = 'grey20'
+        else:
+            new_bg = self.default_bg
 
-        def mute_changed():
-            muted.set(cv.curve.muted)
-            update_mute_look()
+        for widget in self.widgets:
+            widget['bg'] = new_bg
 
-        dispatcher.connect(mute_changed, 'mute changed', sender=cv.curve,
-                           weak=False)
-
-        dispatcher.connect(toggleCollapsed, "toggle collapse", sender=cv.curve,
-                           weak=False)
+    def mute_changed(self):
+        """call this if curve.muted changed"""
+        self.muted.set(self.curveView.curve.muted)
+        self.update_mute_look()
 
 
-class Curvesetview(tk.Frame):
+class Curvesetview(tk.ScrolledWindow):
     def __init__(self, master, curveset, **kw):
         self.curves = {} # curvename : Curveview
         self.curveset = curveset
-        tk.Frame.__init__(self,master,**kw)
+        tk.ScrolledWindow.__init__(self,master,**kw)
         
-        f = tk.Frame(self,relief='raised',bd=1)
+        f = tk.Frame(self.window,relief='raised',bd=1)
         f.pack(side='top',fill='x')
         tk.Label(f, text="new curve named: (C-N)").pack(side='left')
         
         self.newcurvename = tk.StringVar()
-
-        def new_curve(event):
-            self.curveset.new_curve(self.newcurvename.get())
-            self.newcurvename.set('')
         
         entry = tk.Entry(f, textvariable=self.newcurvename)
         entry.pack(side='left', fill='x',exp=1)        
-        entry.bind("<Key-Return>", new_curve)
+        entry.bind("<Key-Return>", self.new_curve)
 
         def focus_entry():
             entry.focus()
         
         dispatcher.connect(self.add_curve, "add_curve", sender=self.curveset)
         dispatcher.connect(focus_entry, "focus new curve", weak=False)
+
+    def new_curve(self, event):
+        self.curveset.new_curve(self.newcurvename.get())
+        self.newcurvename.set('')
         
     def add_curve(self, name, slider=None, knobEnabled=False):
         curve = self.curveset.curves[name]
-        f = CurveRow(self, name, curve, slider, knobEnabled)
-        f.pack(side='top',fill='both',exp=1)
+        f = CurveRow(self.window, name, curve, slider, knobEnabled)
+        f.pack(side='top', fill='both')
 
--- a/light9/zoomcontrol.py	Mon Jun 29 02:18:45 2009 +0000
+++ b/light9/zoomcontrol.py	Mon Jun 29 04:06:17 2009 +0000
@@ -173,8 +173,8 @@
                     txt=txt+"sec"
                 self.create_line(x,0,x,15,
                                  tags=('tics',))
-                self.create_text(x,self.winfo_height()-1,anchor='s',
-                                 text=txt,tags=('tics',),font='6x13')
+                self.create_text(x, self.winfo_height()-1, anchor='s',
+                                 text=txt, tags=('tics',), font='arial 7')
                 lastx = x