# HG changeset patch # User drewp@bigasterisk.com # Date 2009-06-29 04:06:17 # Node ID 73b1811555553cc56560b4ba336a13b6c687e993 # Parent d541914c87164a1bbe585c9b82ef5258d1c2ef26 curvecalc ui touchups, adjustable pane, curve draw speedup, restructured main layout code Ignore-this: b0468548ef9f276dea4a8689dc425b24 diff --git a/bin/curvecalc b/bin/curvecalc --- a/bin/curvecalc +++ b/bin/curvecalc @@ -203,6 +203,9 @@ class Subterm: dispatcher.send("expr_error", sender=self.subexpr, exc=str(e)) return Submaster.Submaster('Error: %s' % str(e), temporary=True) + def __repr__(self): + return "" % (self.submaster, self.subexpr) + class Subtermview(tk.Frame): def __init__(self, master, graph, st, **kw): self.subterm = st @@ -271,7 +274,8 @@ class Output: 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 @@ def create_status_lines(master): ('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 @@ def add_one_subterm(graph, subUri, curve 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 @@ def sub_commands_tk(master, curveset, su 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 @@ def createSubtermGraph(song, subterms): 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 createHelpLines(root): 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 @@ def main(): 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") diff --git a/light9/curve.py b/light9/curve.py --- a/light9/curve.py +++ b/light9/curve.py @@ -20,6 +20,9 @@ class Curve(object): self.points = [] self._muted = False + def __repr__(self): + return "" % len(self.points) + def muted(): doc = "Whether to currently send levels (boolean, obviously)" def fget(self): @@ -317,13 +320,13 @@ class Curveview(tk.Canvas): 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 @@ class Curveview(tk.Canvas): 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 @@ class Curveview(tk.Canvas): 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 @@ class Curveview(tk.Canvas): 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 @@ class Curveview(tk.Canvas): 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 Sliders(BCF2000): class Curveset: + curves = None # curvename : curve def __init__(self, sliders=False): """sliders=True means support the hardware sliders""" @@ -622,7 +627,10 @@ class Curveset: 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 @@ class Curveset: 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 Curveset: 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("", new_curve) + entry.bind("", 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') diff --git a/light9/zoomcontrol.py b/light9/zoomcontrol.py --- a/light9/zoomcontrol.py +++ b/light9/zoomcontrol.py @@ -173,8 +173,8 @@ class Zoomcontrol(object,tk.Canvas): 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