# HG changeset patch # User drewp # Date 2005-04-10 15:03:24 # Node ID 3905d3c92aaab0af0162497b212ceb088e1434f4 # Parent 186a6609503619983b0c08acdef21f5aef28125d twisted mainloop, more row-change keys, xmlrpc fadesub command on port 8050 diff --git a/flax/KeyboardComposer.py b/flax/KeyboardComposer.py --- a/flax/KeyboardComposer.py +++ b/flax/KeyboardComposer.py @@ -1,8 +1,10 @@ -from __future__ import nested_scopes +from __future__ import division,nested_scopes import sys, time sys.path.append('..') from Widgets.Fadable import Fadable +from twisted.internet import reactor,tksupport +from twisted.web import xmlrpc, server from Tix import * import math, atexit, pickle from Submaster import Submasters, sub_maxes @@ -40,10 +42,10 @@ class SubmasterTk(Frame): self.slider_var = DoubleVar() self.slider_var.set(current_level) self.scale = SubScale(self, variable=self.slider_var, width=20) - namelabel = Label(self, text=name, font="Arial 8", bg='black', + namelabel = Label(self, text=name, font="Arial 11", bg='black', fg='white') namelabel.pack(side=TOP) - levellabel = Label(self, textvariable=self.slider_var, font="Arial 8", + levellabel = Label(self, textvariable=self.slider_var, font="Arial 11", bg='black', fg='white') levellabel.pack(side=TOP) self.scale.pack(side=BOTTOM, expand=1, fill=BOTH) @@ -70,6 +72,7 @@ class KeyboardComposer(Frame): self.rows = [] # this holds Tk Frames for each row self.slider_vars = {} # this holds subname:sub Tk vars self.slider_table = {} # this holds coords:sub Tk vars + self.name_to_subtk = {} # subname : SubmasterTk instance self.current_row = 0 self.make_key_hints() @@ -123,13 +126,16 @@ class KeyboardComposer(Frame): lambda evt, num=keys.index(key), d=d: \ self.got_nudger(num, d, full=1)) - # page up and page down change the row - for key in ' '.split(): + # Row changing: + # Page dn, C-n, and ] do down + # Page up, C-p, and ' do up + for key in ' ' \ + ' '.split(): tkobject.bind(key, self.change_row) def change_row(self, event): diff = 1 - if event.keysym in ('Prior', 'p'): + if event.keysym in ('Prior', 'p', 'bracketright'): diff = -1 old_row = self.current_row self.current_row += diff @@ -163,6 +169,7 @@ class KeyboardComposer(Frame): current_level = self.current_sub_levels.get(sub.name, 0) subtk = self.draw_sub_slider(row, col, sub.name, current_level) self.slider_table[(rowcount, col)] = subtk + self.name_to_subtk[sub.name] = subtk col += 1 col %= 10 @@ -203,6 +210,7 @@ class KeyboardComposer(Frame): print "saving current levels as", subname sub = self.get_levels_as_sub() sub.name = subname + sub.temporary = 0 sub.save() def save(self): @@ -236,6 +244,20 @@ class KeyboardComposer(Frame): self.buttonframe.destroy() self.draw_ui() +class LevelServer(xmlrpc.XMLRPC): + def __init__(self,name_to_subtk): + self.name_to_subtk = name_to_subtk + + def xmlrpc_fadesub(self,subname,level,secs): + """submaster will fade to level in secs""" + try: + self.name_to_subtk[subname].scale.fade(level,secs) + ret='ok' + except Exception,e: + ret=str(e) + return ret + + if __name__ == "__main__": s = Submasters() @@ -243,9 +265,12 @@ if __name__ == "__main__": tl = toplevelat("Keyboard Composer", existingtoplevel=root) kc = KeyboardComposer(tl, s, dmxdummy=0) kc.pack(fill=BOTH, expand=1) - atexit.register(kc.save) - try: - mainloop() - except KeyboardInterrupt: - tl.destroy() - sys.exit() + + ls = LevelServer(kc.name_to_subtk) + reactor.listenTCP(8050, server.Site(ls)) + + root.bind("",reactor.stop) + root.protocol('WM_DELETE_WINDOW', reactor.stop) + reactor.addSystemEventTrigger('after','shutdown',kc.save) + tksupport.install(root,ms=10) + reactor.run() diff --git a/flax/MusicTime.py b/flax/MusicTime.py --- a/flax/MusicTime.py +++ b/flax/MusicTime.py @@ -34,7 +34,7 @@ class MusicTimeTk(tk.Frame, MusicTime): self.after(100, self.update_time) if __name__ == "__main__": - from optik import OptionParser + from optparse import OptionParser parser = OptionParser() parser.add_option("-s", "--server", default='dash') parser.add_option("-p", "--port", default=8040, type='int') diff --git a/flax/Submaster.py b/flax/Submaster.py --- a/flax/Submaster.py +++ b/flax/Submaster.py @@ -37,6 +37,7 @@ class Submaster: print "Can't read file for sub: %s" % self.name def save(self): if self.temporary: + print "not saving temporary sub named",self.name return subfile = file("subs/%s" % self.name, 'w') diff --git a/flax/TimelineDMX.py b/flax/TimelineDMX.py --- a/flax/TimelineDMX.py +++ b/flax/TimelineDMX.py @@ -27,7 +27,7 @@ class ShowRunner(tk.Frame): print "TimelineDMX: set timeline to", tlname self.show.set_timeline(tlname) def find_player(self): - self.player = xmlrpclib.Server("http://localhost:8040") + self.player = xmlrpclib.Server("http://spot:8040") def send_levels(self): levels = self.show.calc_active_submaster().get_dmx_list() diff --git a/flax/add_sub b/flax/add_sub new file mode 100644 --- /dev/null +++ b/flax/add_sub @@ -0,0 +1,22 @@ +#!/usr/bin/env python +# usage: add_sub [-l sublevel] subname subterms_to_add_to + +from optparse import OptionParser + +parser = OptionParser() +parser.add_option('-l', '--level', default='0') +opts, args = parser.parse_args() +print 'debug', opts, args + +sub = args.pop(0) +print "adding '%s' at %s" % (sub, opts.level) + +for subterm in args: + print "subterm", subterm + print + filename = 'subterms/%s' % subterm + subs = [line.split(None, 1)[0] for line in file(filename).readlines()] + if sub not in subs: + f = file(filename, 'a') + print 'appended!' + print >>f, "%s %s" % (sub, opts.level) diff --git a/flax/curvecalc b/flax/curvecalc --- a/flax/curvecalc +++ b/flax/curvecalc @@ -5,15 +5,18 @@ todo: curveview should preserve more obj """ from __future__ import division -import xmlrpclib,time,socket,sys,textwrap,math,glob +import xmlrpclib,time,socket,sys,textwrap,math,glob,random from bisect import bisect_left,bisect,bisect_right import Tkinter as tk from dispatch import dispatcher from twisted.internet import reactor,tksupport +import twisted from twisted.web.xmlrpc import Proxy sys.path.append("../light8") import dmxclient +sys.path.append("../../semprini") +from lengther import wavelength import Submaster from TLUtility import make_attributes_from_args @@ -23,15 +26,19 @@ class Curve: """curve does not know its name. see Curveset""" points = None # x-sorted list of (x,y) def __init__(self): - self.points = [] + self.points = [(0,0),(10,0)] def load(self,filename): + self.points[:]=[] for line in file(filename): self.points.append(tuple([float(a) for a in line.split()])) self.points.sort() dispatcher.send("points changed",sender=self) def save(self,filename): + if filename.endswith('-music') or filename.endswith('_music'): + print "not saving music track" + return f = file(filename,'w') for p in self.points: f.write("%s %s\n" % p) @@ -50,21 +57,45 @@ class Curve: y = p1[1]+(p2[1]-p1[1])*frac return y + def insert_pt(self,new_pt): + i = bisect(self.points,(new_pt[0],None)) + self.points.insert(i,new_pt) __call__=eval class Curveview(tk.Canvas): def __init__(self,master,curve,**kw): self.curve=curve + self._time = 0 tk.Canvas.__init__(self,master,width=10,height=10, relief='sunken',bd=1, - closeenough=5,**kw) + closeenough=5,takefocus=1, **kw) self.selected_points=[] # idx of points being dragged self.update() - self.bind("",self.focus) + # self.bind("",self.focus) dispatcher.connect(self.input_time,"input time") dispatcher.connect(self.update,"zoom changed") dispatcher.connect(self.update,"points changed",sender=self.curve) self.bind("",self.update) + for x in range(1, 6): + def add_kb_marker_point(evt, x=x): + print "add_kb_marker_point", evt + self.add_point((self.current_time(), (x - 1) / 4.0)) + + self.bind("" % x, add_kb_marker_point) + + + for butnum,factor in (5, 1.5),(4, 1/1.5): + self.bind(""%butnum, + lambda ev,factor=factor: + dispatcher.send("zoom about mouse", + t=self.world_from_screen(ev.x,0)[0], + factor=factor)) + self.bind("",lambda ev: + dispatcher.send("see time", + t=self.current_time())) + def current_time(self): + return self._time + def screen_from_world(self,p): start,end = self.zoom ht = self.winfo_height() @@ -79,6 +110,7 @@ class Curveview(tk.Canvas): pts = self.screen_from_world((val,0))+self.screen_from_world((val,1)) self.delete('timecursor') self.create_line(*pts,**dict(width=2,fill='red',tags=('timecursor',))) + self._time = t def update(self,*args): self.zoom = dispatcher.send("zoom area")[0][1] @@ -91,14 +123,55 @@ class Curveview(tk.Canvas): visrightidx = min(len(cp)-1,bisect_left(cp,(visible_x[1],None))+1) visible_points = cp[visleftidx:visrightidx+1] + visible_idxs = range(visleftidx,visrightidx+1) self.delete('curve') + + self._draw_markers(visible_x) + + self._draw_line(visible_idxs,visible_points) + + self.dots = {} # idx : canvas rectangle + + if len(visible_points)<50: + self._draw_handle_points(visible_idxs,visible_points) + + def _draw_markers(self,visible_x): + mark = self._draw_one_marker + + mark(0,"0") + t1,t2=visible_x + if t2-t1<30: + for t in range(int(t1),int(t2)+1): + mark(t,str(t)) + mark(-4,"-4") + + endtimes = dispatcher.send("get max time") + if endtimes: + endtime = endtimes[0][1] + mark(endtime,"end %.1f"%endtime) + mark(endtime+10,"post %.1f"%(endtime+10)) + + def _draw_one_marker(self,t,label): + x = self.screen_from_world((t,0))[0] + self.create_line(x,self.winfo_height(),x,self.winfo_height()-20, + tags=('curve',)) + self.create_text(x,self.winfo_height()-20,text=label,anchor='s', + tags=('curve',)) + + + def _draw_line(self,visible_idxs,visible_points): linepts=[] - for p in visible_points: + step=1 + linewidth=2 + if len(visible_points)>800: + step = int(len(visible_points)/800) + linewidth=1 + for p in visible_points[::step]: linepts.extend(self.screen_from_world(p)) - if not linepts: + if len(linepts)<4: return - line = self.create_line(*linepts,**{'tags':'curve'}) + line = self.create_line(*linepts,**dict(width=linewidth,tags='curve')) # canvas doesnt have keyboard focus, so i can't easily change the # cursor when ctrl is pressed @@ -106,35 +179,45 @@ class Curveview(tk.Canvas): # print ev.state # self.bind("",curs) # self.bind("",lambda ev: curs(0)) - self.tag_bind(line,"",self.newpoint) + self.tag_bind(line,"",self.newpointatmouse) - self.dots = {} # idx : canvas rectangle - if len(visible_points)<50: - for i,p in enumerate(visible_points): - rad=3 - p = self.screen_from_world(p) - dot = self.create_rectangle(p[0]-rad,p[1]-rad,p[0]+rad,p[1]+rad, - outline='black',fill='blue', - tags=('curve','point')) - self.tag_bind(dot,"", - lambda ev,i=i: self.dotpress(ev,i)) - self.bind("", - lambda ev,i=i: self.dotmotion(ev,i)) - self.bind("", - lambda ev,i=i: self.dotrelease(ev,i)) - self.dots[i]=dot + def _draw_handle_points(self,visible_idxs,visible_points): + for i,p in zip(visible_idxs,visible_points): + rad=3 + worldp = p + p = self.screen_from_world(p) + dot = self.create_rectangle(p[0]-rad,p[1]-rad,p[0]+rad,p[1]+rad, + outline='black',fill='blue', + tags=('curve','point', 'handle%d' % i)) + if worldp[1] == 0: + rad += 3 + dot2 = self.create_oval(p[0]-rad,p[1]-rad, + p[0]+rad,p[1]+rad, + outline='darkgreen', + tags=('curve','point', 'handle%d' % i)) + self.tag_bind('handle%d' % i,"", + lambda ev,i=i: self.dotpress(ev,i)) + self.bind("", + lambda ev,i=i: self.dotmotion(ev,i)) + self.bind("", + lambda ev,i=i: self.dotrelease(ev,i)) + self.dots[i]=dot - self.highlight_selected_dots() - - def newpoint(self,ev): - cp = self.curve.points + self.highlight_selected_dots() - p = self.world_from_screen(ev.x,ev.y) - i = bisect(cp,(p[0],None)) + def newpointatmouse(self, ev): + p = self.world_from_screen(ev.x,ev.y) + x, y = p + y = max(0, y) + y = min(1, y) + p = x, y + self.add_point(p) + + def add_point(self, p): self.unselect() - cp.insert(i,p) + self.curve.insert_pt(p) self.update() def highlight_selected_dots(self): @@ -150,7 +233,6 @@ class Curveview(tk.Canvas): def dotmotion(self,ev,dotidx): cp = self.curve.points - moved=0 for idx in self.selected_points: x,y = self.world_from_screen(ev.x,ev.y) @@ -178,9 +260,10 @@ class Curveset: """find all files that look like basename-curvename and add curves with their contents""" for filename in glob.glob("%s-*"%basename): - curvename = filename[filename.rfind('-')+1:] + curvename = filename[filename.find('-')+1:] c=Curve() c.load(filename) + curvename = curvename.replace('-','_') self.add_curve(curvename,c) def save(self,basename): """writes a file for each curve with a name @@ -192,6 +275,15 @@ class Curveset: dispatcher.send("add_curve",sender=self,name=name) def globalsdict(self): return self.curves.copy() + def new_curve(self,name): + if name=="": + print "no name given" + return + while name in self.curves: + name=name+"-1" + + self.add_curve(name,Curve()) + class Curvesetview(tk.Frame): curves = None # curvename : Curveview @@ -199,11 +291,21 @@ class Curvesetview(tk.Frame): self.curves = {} self.curveset = curveset tk.Frame.__init__(self,master,**kw) + + f = tk.Frame(self,relief='raised',bd=1) + f.pack(side='top',fill='x') + tk.Button(f,text="new curve named:", + command=lambda: self.curveset.new_curve(self.newcurvename.get())).pack(side='left') + self.newcurvename = tk.StringVar() + tk.Entry(f,textvariable=self.newcurvename).pack(side='left', + fill='x',exp=1) + + dispatcher.connect(self.add_curve,"add_curve",sender=self.curveset) def add_curve(self,name): f = tk.Frame(self,relief='raised',bd=1) f.pack(side='top',fill='both',exp=1) - tk.Label(f,text="curve %r"%name).pack(side='left') + tk.Label(f,text="curve %r"%name,width=15).pack(side='left') cv = Curveview(f,self.curveset.curves[name]) cv.pack(side='right',fill='both',exp=1) self.curves[name] = cv @@ -216,11 +318,11 @@ class Music: def current_time(self): """return deferred which gets called with the current time""" if self.player is None: - self.player = Proxy("http://spot:8040") - d = self.player.callRemote("songlength") - d.addCallback(lambda l: dispatcher.send("max time",maxtime=l)) - d = self.player.callRemote("songname") - d.addCallback(lambda n: dispatcher.send("songname",name=n)) + self.player = Proxy("http://miles:8040") +# d = self.player.callRemote("songlength") +# d.addCallback(lambda l: dispatcher.send("max time",maxtime=l)) +# d = self.player.callRemote("songname") +# d.addCallback(lambda n: dispatcher.send("songname",name=n)) d = self.player.callRemote('gettime') def sendtime(t): dispatcher.send("input time",val=t) @@ -232,22 +334,50 @@ class Music: class Subexpr: curveset = None - def __init__(self,curveset): + def __init__(self,curveset,expr=""): self.curveset = curveset self.lasteval = None - self.expr="" + self.expr=expr + self._smooth_random_items = [random.random() for x in range(100)] def eval(self,t): if self.expr=="": dispatcher.send("expr_error",sender=self,exc="no expr, using 0") return 0 glo = self.curveset.globalsdict() glo['t'] = t + + glo['nsin'] = lambda x: (math.sin(x * (2 * math.pi)) + 1) / 2 + glo['ncos'] = lambda x: (math.cos(x * (2 * math.pi)) + 1) / 2 + glo['within'] = lambda a, b: a < t < b + glo['bef'] = lambda x: t < x + glo['aft'] = lambda x: x < t + glo['smoove'] = lambda x: -2 * (x ** 3) + 3 * (x ** 2) + + def smooth_random(speed=1): + """1 = new stuff each second, <1 is slower, fade-ier""" + x = (t * speed) % len(self._smooth_random_items) + x1 = int(x) + x2 = (int(x) + 1) % len(self._smooth_random_items) + y1 = self._smooth_random_items[x1] + y2 = self._smooth_random_items[x2] + return y1 + (y2 - y1) * ((x - x1)) + + def notch_random(speed=1): + """1 = new stuff each second, <1 is slower, notch-ier""" + x = (t * speed) % len(self._smooth_random_items) + x1 = int(x) + y1 = self._smooth_random_items[x1] + return y1 + + glo['noise'] = smooth_random + glo['notch'] = notch_random + try: self.lasteval = eval(self.expr,glo) except Exception,e: dispatcher.send("expr_error",sender=self,exc=e) else: - dispatcher.send("expr_error",sender=self,exc="no errors") + dispatcher.send("expr_error",sender=self,exc="ok") return self.lasteval def expr(): @@ -283,18 +413,23 @@ class Subexprview(tk.Frame): class Subterm: """one Submaster and its Subexpr""" + def __init__(self,submaster,subexpr): + make_attributes_from_args('submaster','subexpr') def scaled(self,t): - return self.sub * self.subexpr.eval(t) + return self.submaster * self.subexpr.eval(t) class Subtermview(tk.Frame): def __init__(self,master,st,**kw): self.subterm = st tk.Frame.__init__(self,master,bd=1,relief='raised',**kw) - tk.Label(self,text="sub %r" % self.subterm.sub.name).pack(side='left') + l = tk.Label(self,text="sub %r" % self.subterm.submaster.name) + l.pack(side='left') sev=Subexprview(self,self.subterm.subexpr) sev.pack(side='left',fill='both',exp=1) class Output: + lastsendtime=0 + lastsendlevs=None def __init__(self,subterms): make_attributes_from_args('subterms') def send_dmx(self,t): @@ -304,8 +439,14 @@ class Output: scl = st.scaled(t) scaledsubs.append(scl) out = Submaster.sub_maxes(*scaledsubs) - dispatcher.send("output levels",val=out.get_levels()) - dmxclient.outputlevels(out.get_dmx_list(),twisted=1) + levs = out.get_levels() + now=time.time() + if now-self.lastsendtime>5 or levs!=self.lastsendlevs: + dispatcher.send("output levels",val=levs) + dmxclient.outputlevels(out.get_dmx_list(), + twisted=1,clientid='curvecalc') + self.lastsendtime = now + self.lastsendlevs = levs def create_status_lines(master): for signame,textfilter in [ @@ -313,7 +454,8 @@ def create_status_lines(master): ('output levels', lambda levels: textwrap.fill("; ".join(["%s:%.2f"%(n,v) for n,v in - levels.items()]),70)), + levels.items()[:5] + if v>0]),70)), ('update period',lambda t: "%.1fms"%(t*1000)), ]: l = tk.Label(master,anchor='w',justify='left') @@ -323,19 +465,54 @@ def create_status_lines(master): signame,weak=0) def savesubterms(filename,subterms): - f = file(filename,'w') + s="" for st in subterms: - f.write("%s %s\n" % (st.sub.name,st.subexpr.expr)) - f.close() + s=s+"%s %s\n" % (st.submaster.name,st.subexpr.expr) + + file(filename,'w').write(s) + +class SubtermSetView(tk.Frame): + def __init__(self, master, *args, **kw): + tk.Frame.__init__(self, master, *args, **kw) + self.cur_row = 0 + self.cur_col = 0 + self.ncols = 2 + def add_subtermview(self, stv): + stv.grid(row=self.cur_row, column=self.cur_col, sticky='news') + self.columnconfigure(self.cur_col, weight=1) + + self.cur_col += 1 + self.cur_col %= self.ncols + if self.cur_col == 0: + self.cur_row += 1 -def save(song,subterms,curveset): - savesubterms("subterms/"+song,subterms) - curveset.save(basename="curves/"+song) +def add_one_subterm(subname, curveset, subterms, root, expr=''): + term = Subterm(Submaster.Submaster(subname), Subexpr(curveset,expr)) + subterms.append(term) + + stv=Subtermview(ssv,term) + # stv.pack(side='top',fill='x') + global ssv + ssv.add_subtermview(stv) + + return term +def subterm_adder(master, curveset, subterms, root): + f=tk.Frame(master,relief='raised',bd=1) + newname = tk.StringVar() + + def add_cmd(): + add_one_subterm(newname.get(), curveset, subterms, root, '') + + tk.Button(f,text="new subterm named:", command=add_cmd).pack(side='left') + tk.Entry(f,textvariable=newname).pack(side='left',fill='x',exp=1) + return f + ####################################################################### root=tk.Tk() -root.wm_geometry("790x930") -#root.tk_focusFollowsMouse() +root.tk_setPalette("gray50") +root.wm_geometry("1120x850") +root.tk_focusFollowsMouse() music=Music() @@ -346,29 +523,38 @@ curveset = Curveset() csv = Curvesetview(root,curveset) csv.pack(side='top',fill='both',exp=1) -song = "16mix.wav" +ssv = SubtermSetView(root) +ssv.pack(side='top', fill='x') +song = sys.argv[1] +root.title("Curemaster 2000MX - %s" % song) + +musicfilename = "/my/music/projects/sharlyn2004/%s.wav" % song +maxtime = wavelength(musicfilename) +dispatcher.send("max time",maxtime=maxtime) +dispatcher.connect(lambda: maxtime, "get max time",weak=0) curveset.load(basename="curves/"+song) subterms = [] +subterm_adder(root, curveset, subterms, root).pack(side='top',fill='x') for line in file("subterms/"+song): subname,expr = line.strip().split(" ",1) - term = Subterm() - - sexpr = Subexpr(curveset) - sexpr.expr = expr + term = add_one_subterm(subname, curveset, subterms, root, expr) - term.sub = Submaster.Submaster(subname) - term.subexpr = sexpr - subterms.append(term) - - stv=Subtermview(root,term) - stv.pack(side='top',fill='x') + # stv=Subtermview(root,term) + # stv.pack(side='top',fill='x') out = Output(subterms) -#save(song,subterms,curveset) +def savekey(*args): + print "saving",song + savesubterms("subterms/"+song,subterms) + curveset.save(basename="curves/"+song) + print "saved" + + +root.bind("",savekey) create_status_lines(root) @@ -396,6 +582,13 @@ def update2(t): out.send_dmx(t) update() +#def logprint(msg): +# print "log",msg +#twisted.python.log.addObserver(logprint) + +root.bind("",lambda ev: reactor.stop) +root.bind("",lambda ev: reactor.stop) +root.protocol('WM_DELETE_WINDOW', reactor.stop) tksupport.install(root,ms=10) if 0: sys.path.append("/home/drewp/projects/editor/pour") diff --git a/flax/dmxchanedit.py b/flax/dmxchanedit.py --- a/flax/dmxchanedit.py +++ b/flax/dmxchanedit.py @@ -12,7 +12,7 @@ import Patch from uihelpers import make_frame, colorlabel, eventtoparent from dispatch import dispatcher -stdfont = ('Arial', 10) +stdfont = ('Arial', 12) class Onelevel(tk.Frame): """a name/level pair""" @@ -53,20 +53,23 @@ class Onelevel(tk.Frame): self.desc_lab.config(bg='cyan') self._start_y=ev.y self._start_lev=self.currentlevel -# self.bind("",b1down) def b1motion(ev): delta=self._start_y-ev.y self.changelevel(self._start_lev+delta*.005) -# self.bind("",b1motion) def b1up(ev): self.desc_lab.config(bg='black') -# self.bind("",b1up) + def b3up(ev): + self.changelevel(0.0) + def b3down(ev): + self.changelevel(1.0) # make the buttons work in the child windows for w in self.winfo_children(): for e,func in (('',b1down), ('',b1motion), - ('',b1up)): + ('',b1up), + ('', b3up), + ('', b3down)): w.bind(e,func) # w.bind(e,lambda ev,e=e: eventtoparent(ev,e)) diff --git a/flax/kcclient b/flax/kcclient new file mode 100644 --- /dev/null +++ b/flax/kcclient @@ -0,0 +1,17 @@ +#!/usr/bin/env python + +"""send KeyboardComposer a fade request, for use from the shell""" + +import sys,xmlrpclib + +subname = sys.argv[1] +level = float(sys.argv[2]) +fadesecs = 0 +if len(sys.argv)>3: + fadesecs = float(sys.argv[3]) + +levelserver = xmlrpclib.ServerProxy("http://localhost:8050") + +levelserver.fadesub(subname,level,fadesecs) + + diff --git a/flax/zoomcontrol.py b/flax/zoomcontrol.py --- a/flax/zoomcontrol.py +++ b/flax/zoomcontrol.py @@ -4,6 +4,8 @@ from dispatch import dispatcher class Zoomcontrol(object,tk.Canvas): + mintime=-5 + def maxtime(): doc = "seconds at the right edge of the bar" def fget(self): return self._maxtime @@ -15,7 +17,7 @@ class Zoomcontrol(object,tk.Canvas): def start(): def fget(self): return self._start - def fset(self,v): self._start = max(0,v) + def fset(self,v): self._start = max(self.mintime,v) return locals() start = property(**start()) @@ -39,26 +41,67 @@ class Zoomcontrol(object,tk.Canvas): self.updatewidget() self.bind("",self.updatewidget) - self.bind("",lambda ev: setattr(self,'lastx',ev.x)) - self.tag_bind(self.leftbrack,"", - lambda ev: self.adjust('start',ev)) - self.tag_bind(self.rightbrack,"", - lambda ev: self.adjust('end',ev)) - self.tag_bind(self.shade,"", - lambda ev: self.adjust('offset',ev)) + if 0: + # works, but you have to stay in the widget while you drag + self.bind("",self.press) + self.tag_bind(self.leftbrack,"", + lambda ev: self.adjust(ev,'start')) + self.tag_bind(self.rightbrack,"", + lambda ev: self.adjust(ev,'end')) + self.tag_bind(self.shade,"", + lambda ev: self.adjust(ev,'offset')) + else: + # works better + # bind to buttonpress wasnt working, but Enter is good enough + self.tag_bind(self.leftbrack,"", + lambda ev: self.press(ev,'start')) + self.tag_bind(self.shade,"", + lambda ev: self.press(ev,'offset')) + self.tag_bind(self.rightbrack,"", + lambda ev: self.press(ev,'end')) + self.bind("",self.adjust) + self.bind("",self.release) + dispatcher.connect(lambda: (self.start,self.end),"zoom area",weak=0) dispatcher.connect(self.input_time,"input time") - dispatcher.connect(lambda maxtime: (setattr(self,'maxtime',maxtime), - self.updatewidget()),"max time",weak=0) + dispatcher.connect(lambda maxtime: (setattr(self,'maxtime',maxtime+15), + self.updatewidget()), + "max time",weak=0) + dispatcher.connect(self.zoom_about_mouse,"zoom about mouse") + dispatcher.connect(self.see_time,"see time") self.created=1 + def zoom_about_mouse(self,t,factor): + self.start = t - factor*(t-self.start) + self.end = t + factor*(self.end-t) + self.updatewidget() + dispatcher.send("zoom changed") + def see_time(self,t): + margin = (self.end-self.start)*.5 # centering is nicest + if tself.end: + self.offset+=(t-self.end)+margin + self.updatewidget() + dispatcher.send("zoom changed") + def input_time(self,val): t=val x=self.can_for_t(t) self.coords(self.time,x,0,x,self.winfo_height()) + def press(self,ev,attr): + self.adjustingattr = attr + + def release(self,ev): + if hasattr(self,'adjustingattr'): del self.adjustingattr + if hasattr(self,'lastx'): del self.lastx + def adjust(self,ev,attr=None): - def adjust(self,attr,ev): + if not hasattr(self,'adjustingattr'): + return + attr = self.adjustingattr + if not hasattr(self,'lastx'): - return + self.lastx = ev.x new = self.can_for_t(getattr(self,attr)) + (ev.x - self.lastx) self.lastx = ev.x setattr(self,attr,self.t_for_can(new)) @@ -77,9 +120,9 @@ class Zoomcontrol(object,tk.Canvas): offset = property(**offset()) def can_for_t(self,t): - return t/self.maxtime*(self.winfo_width()-30)+20 + return (t-self.mintime)/(self.maxtime-self.mintime)*(self.winfo_width()-30)+20 def t_for_can(self,x): - return (x-20)/(self.winfo_width()-30)*self.maxtime + return (x-20)/(self.winfo_width()-30)*(self.maxtime-self.mintime)+self.mintime def updatewidget(self,*args): """redraw pieces based on start/end""" @@ -90,7 +133,7 @@ class Zoomcontrol(object,tk.Canvas): ecan = self.can_for_t(self.end) self.coords(self.leftbrack,scan+lip,y1,scan,y1,scan,y2,scan+lip,y2) self.coords(self.rightbrack,ecan-lip,y1,ecan,y1,ecan,y2,ecan-lip,y2) - self.coords(self.shade,scan+3,y1+lip,ecan-3,y2-lip) + self.coords(self.shade,scan+5,y1+lip,ecan-5,y2-lip) self.delete("tics") lastx=-1000 for t in range(0,int(self.maxtime)):