# HG changeset patch # User Drew Perttula # Date 2012-06-10 05:33:25 # Node ID d12bc8919d6e3fd202de22c7bca1538a20546f8c # Parent 2aac2ef23495d8887df0fedfb7f818ad899b3652 ported the zoom control Ignore-this: edce8350a21280aebf2a0e69cd7335ea diff --git a/bin/curvecalc b/bin/curvecalc --- a/bin/curvecalc +++ b/bin/curvecalc @@ -26,7 +26,6 @@ log = logging.getLogger() import run_local from light9 import showconfig, prof -from light9.curvecalc.zoomcontrol import Zoomcontrol from light9.curvecalc.curve import Curveset from light9.curvecalc import curveview from light9.curvecalc.musicaccess import Music, currentlyPlayingSong @@ -64,19 +63,26 @@ class Main(object): ) def refreshCurveView(): - m = os.path.getmtime(curveview.__file__.replace('.pyc', '.py')) + mtimes = [os.path.getmtime(f) for f in [ + 'light9/curvecalc/curveview.py', + 'light9/curvecalc/zoomcontrol.py', + ]] - if not hasattr(self, 'curvesetView') or self.curvesetView._mtime != m: + if (not hasattr(self, 'curvesetView') or + self.curvesetView._mtimes != mtimes): print "reload curveview.py" curvesVBox = wtree.get_object("curves") + zoomControlBox = wtree.get_object("zoomControlBox") [curvesVBox.remove(c) for c in curvesVBox.get_children()] + [zoomControlBox.remove(c) for c in + zoomControlBox.get_children()] try: linecache.clearcache() reload(curveview) # mem problem somewhere; need to hold a ref to this self.curvesetView = curveview.Curvesetview( - curvesVBox, curveset) - self.curvesetView._mtime = m + curvesVBox, zoomControlBox, curveset) + self.curvesetView._mtimes = mtimes # curvesetview must already exist, since this # makes 'add_curve' signals for all the initial @@ -97,7 +103,14 @@ class Main(object): self.makeStatusLines(wtree.get_object("status")) - #zc = Zoomcontrol(root) + def onSeeCurrentTime(self, item): + dispatcher.send("see time") + + def onSeeTimeUntilEnd(self, item): + dispatcher.send("see time until end") + + def onZoomAll(self, item): + dispatcher.send("show all") def onPlayPause(self, item): # since the X coord in a curveview affects the handling, one diff --git a/light9/curvecalc/curvecalc.glade b/light9/curvecalc/curvecalc.glade --- a/light9/curvecalc/curvecalc.glade +++ b/light9/curvecalc/curvecalc.glade @@ -1,6 +1,7 @@ - + + False @@ -13,9 +14,9 @@ False - False True False + False _Curvecalc True @@ -25,9 +26,9 @@ gtk-save - False True False + False True True @@ -35,9 +36,9 @@ gtk-quit - False True False + False True True @@ -48,9 +49,9 @@ - False True False + False _Edit True @@ -60,9 +61,9 @@ gtk-cut - False True False + False True True @@ -70,9 +71,9 @@ gtk-copy - False True False + False True True @@ -80,9 +81,9 @@ gtk-paste - False True False + False True True @@ -93,9 +94,9 @@ - False True False + False _View True @@ -104,45 +105,51 @@ False - False True False - See current time (esc) + False + See current time True + + - False True False - See from current time -> end (S-Esc) + False + See from current time -> end True + + - False True False - Zoom all (C-Esc) + False + Zoom all True + + - False True False + False Zoom in (wheel up) True - False True False + False Zoom out (wheel down) True @@ -153,9 +160,9 @@ - False True False + False _Playback True @@ -164,9 +171,9 @@ False - False True False + False _Play/pause True @@ -179,9 +186,9 @@ - False True False + False Poin_ts True @@ -191,9 +198,9 @@ False - False True False + False Delete (del) True @@ -249,6 +256,8 @@ True False False + True + True True @@ -264,6 +273,29 @@ + + True + False + + + True + False + [zoom control] + + + False + True + 0 + + + + + False + True + 1 + + + True True @@ -293,7 +325,7 @@ True True - 1 + 2 @@ -325,7 +357,6 @@ True True - always True @@ -343,42 +374,6 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - @@ -397,7 +392,6 @@ Reload subs (C-r) - False True True True @@ -429,6 +423,10 @@ True True + False + False + True + True False @@ -486,24 +484,6 @@ - - - - - - - - - - - - - - - - - - @@ -608,131 +588,6 @@ Available in functions: nsin/ncos period False gtk-refresh - - False - - - True - False - - - True - False - - - True - False - - - True - False - 5 - 0 - 0 - out - - - True - False - - - True - False - curve 'music' - - - False - False - 0 - - - - - True - False - - - C - False - True - True - False - False - 0 - True - - - True - True - 0 - - - - - M - False - True - True - False - False - 0 - True - - - True - True - 1 - - - - - False - False - 1 - - - - - - - - - - False - False - 0 - - - - - True - False - gtk-media-forward - - - True - True - 1 - - - - - - - True - True - 0 - - - - - - - - - - - song01(t) diff --git a/light9/curvecalc/curveview.py b/light9/curvecalc/curveview.py --- a/light9/curvecalc/curveview.py +++ b/light9/curvecalc/curveview.py @@ -71,12 +71,12 @@ class Curveview(object): """ graphical curve widget only. Please pack .widget """ - def __init__(self, curve, knobEnabled=False, isMusic=False, **kw): + def __init__(self, curve, knobEnabled=False, isMusic=False, zoomControl=None, **kw): """knobEnabled=True highlights the previous key and ties it to a hardware knob""" - print "new curveview" + print "new curveview" self.widget = goocanvas.Canvas() - self.widget.set_property("background-color", "gray20") + self.widget.set_property("background-color", "black") self.widget.set_size_request(-1, 100) self.root = self.widget.get_root_item() @@ -84,6 +84,7 @@ class Curveview(object): self.curve = curve self.knobEnabled = knobEnabled self._isMusic = isMusic + self.zoomControl = zoomControl self._time = 0 self.last_mouse_world = None self.entered = False # is the mouse currently over this widget @@ -106,6 +107,7 @@ class Curveview(object): self.widget.connect("leave-notify-event", self.onLeave) self.widget.connect("enter-notify-event", self.onEnter) self.widget.connect("motion-notify-event", self.onMotion) + self.widget.connect("scroll-event", self.onScroll) self.widget.connect("button-release-event", self.onRelease) if 0: @@ -116,25 +118,6 @@ class Curveview(object): self.bind("" % x, add_kb_marker_point) - - for butnum,factor in (5, 1.5),(4, 1/1.5): - def onMouseWheel(ev,factor=factor): - dispatcher.send("zoom about mouse", - t=self.world_from_screen(ev.x,0)[0], - factor=factor) - # this is supposed to make the canvases redraw more - # visibly, so we don't waste line redraws that never - # get seen. I'm not sure if it works. - self.update() - self.bind("" % butnum, onMouseWheel) - self.bind("", lambda ev: - dispatcher.send("see time", - t=self.current_time())) - self.bind("", lambda ev: - dispatcher.send("see time until end", - t=self.current_time())) - self.bind("", lambda ev: dispatcher.send("show all")) - # this binds on c-a-b1, etc if 0: self.regionzoom = RegionZoom(self, self.world_from_screen, @@ -167,14 +150,17 @@ class Curveview(object): user has pressed ctrl-p over a curve view, possibly this one. Returns the time under the mouse if we know it, or else None + + todo: there should be a faint timecursor line under the mouse + so it's more obvious that we use that time for some + events. Rt-click should include Ctrl+P as 'play/pause from + here' """ - print id(self), "checking", self.entered + # maybe self.widget.get_pointer would be ok for this? i didn't try it if self.entered: t = self.world_from_screen(self.lastMouseX, 0)[0] - print self.lastMouseX, t return t return None - #dispatcher.send("music seek", t=self.world_from_screen(ev.x,0)[0]) def goLive(self): """this is for startup performance only, since the curves were @@ -265,14 +251,14 @@ class Curveview(object): return self._time def screen_from_world(self,p): - start,end = self.zoom + z = self.zoomControl ht = self.size.height - return (p[0]-start)/(end-start)*self.size.width, (ht-5)-p[1]*(ht-10) + return (p[0]-z.start)/(z.end-z.start)*self.size.width, (ht-5)-p[1]*(ht-10) def world_from_screen(self,x,y): - start,end = self.zoom + z = self.zoomControl ht = self.size.height - return x/self.size.width*(end-start)+start, ((ht-5)-y)/(ht-10) + return x/self.size.width*(z.end-z.start)+z.start, ((ht-5)-y)/(ht-10) def input_time(self, val, forceUpdate=False): # i tried various things to make this not update like crazy, @@ -310,9 +296,7 @@ class Curveview(object): return self.size = self.widget.get_allocation() - self.zoom = 0, 228#dispatcher.send("zoom area")[0][1] cp = self.curve.points - visible_x = (self.world_from_screen(0,0)[0], self.world_from_screen(self.size.width, 0)[0]) @@ -345,7 +329,7 @@ class Curveview(object): """are we one of the music curves (which might be drawn a bit differently)""" return self._isMusic - + def _draw_gradient(self): print "no grad" return @@ -408,18 +392,19 @@ class Curveview(object): stroke_color='gray70') goocanvas.Text(parent=self.curveGroup, fill_color="white", - font="ubuntu 9", + anchor=gtk.ANCHOR_SOUTH, + font="ubuntu 7", x=x+3, y=ht-20, text=label) def _draw_line(self,visible_points): linepts=[] step=1 - linewidth = 3 - # 800? maybe this should be related to self.width - if len(visible_points) > 800: - step = int(len(visible_points) / 800) - linewidth = .5 + linewidth = 1.5 + maxPointsToDraw = self.size.width / 2 + if len(visible_points) > maxPointsToDraw: + step = int(len(visible_points) / maxPointsToDraw) + linewidth = .8 for p in visible_points[::step]: linepts.append(self.screen_from_world(p)) @@ -459,15 +444,15 @@ class Curveview(object): ) if worldp[1] == 0: rad += 3 - dot2 = goocanvas.Ellipse(parent=self.curveGroup, - center_x=p[0], - center_y=p[1], - radius_x=rad, - radius_y=rad, - line_width=.8, - stroke_color='darkgreen', - #tags=('curve','point', 'handle%d' % i) - ) + goocanvas.Ellipse(parent=self.curveGroup, + center_x=p[0], + center_y=p[1], + radius_x=rad, + radius_y=rad, + line_width=2, + stroke_color='#00a000', + #tags=('curve','point', 'handle%d' % i) + ) dot.connect("button-press-event", self.dotpress, i) #self.tag_bind('handle%d' % i,"", # lambda ev,i=i: self.dotpress(ev,i)) @@ -596,6 +581,11 @@ class Curveview(object): def unselect(self): self.selected_points=[] self.highlight_selected_dots() + + def onScroll(self, widget, event): + t = self.world_from_screen(event.x, 0)[0] + self.zoomControl.zoom_about_mouse( + t, factor=1.5 if event.direction == gtk.gdk.SCROLL_DOWN else 1/1.5) def onRelease(self, widget, event): self.print_state("dotrelease") @@ -610,8 +600,7 @@ class CurveRow(object): please pack self.box """ - def __init__(self, name, curve, slider, knobEnabled): - + def __init__(self, name, curve, slider, knobEnabled, zoomControl): self.box = gtk.HandleBox() self.box.set_border_width(1) @@ -625,7 +614,8 @@ class CurveRow(object): self.setupControls(controls, name, curve, slider) self.curveView = Curveview(curve, knobEnabled=knobEnabled, - isMusic=name in ['music', 'smooth_music']) + isMusic=name in ['music', 'smooth_music'], + zoomControl=zoomControl) cols.pack_start(self.curveView.widget, expand=True) def setupControls(self, controls, name, curve, slider): @@ -711,14 +701,21 @@ class Curvesetview(object): """ """ - def __init__(self, curvesVBox, curveset): + def __init__(self, curvesVBox, zoomControlBox, curveset): self.curvesVBox = curvesVBox self.curveset = curveset self.allCurveRows = set() + import light9.curvecalc.zoomcontrol + reload(light9.curvecalc.zoomcontrol) + self.zoomControl = light9.curvecalc.zoomcontrol.ZoomControl() + zoomControlBox.add(self.zoomControl.widget) + self.zoomControl.widget.show_all() + dispatcher.connect(self.add_curve, "add_curve", sender=self.curveset) self.newcurvename = gtk.EntryBuffer("", 0) + return entry = tk.Entry(f, textvariable=self.newcurvename) @@ -728,6 +725,7 @@ class Curvesetview(object): dispatcher.connect(self.focus_entry, "focus new curve") + def focus_entry(self): self.entry.focus() @@ -737,7 +735,7 @@ class Curvesetview(object): def add_curve(self, name, slider=None, knobEnabled=False): curve = self.curveset.curves[name] - f = CurveRow(name, curve, slider, knobEnabled) + f = CurveRow(name, curve, slider, knobEnabled, self.zoomControl) self.curvesVBox.pack_end(f.box) f.box.show_all() self.allCurveRows.add(f) diff --git a/light9/curvecalc/zoomcontrol.py b/light9/curvecalc/zoomcontrol.py --- a/light9/curvecalc/zoomcontrol.py +++ b/light9/curvecalc/zoomcontrol.py @@ -1,9 +1,12 @@ from __future__ import division -import Tkinter as tk +import gtk, goocanvas import louie as dispatcher from light9.curvecalc import cursors -class Zoomcontrol(object,tk.Canvas): +class ZoomControl(object): + """ + please pack .widget + """ mintime = 0 @@ -48,51 +51,57 @@ class Zoomcontrol(object,tk.Canvas): return locals() offset = property(**offset()) - def __init__(self,master,**kw): - self.maxtime=370 + def __init__(self, **kw): + self.widget = goocanvas.Canvas(bounds_padding=5) + self.widget.set_property("background-color", "gray60") + self.widget.set_size_request(-1, 30) + + endtimes = dispatcher.send("get max time") + if endtimes: + self.maxtime = endtimes[0][1] + else: + self.maxtime = 0 + self.start=0 self.end=20 - tk.Canvas.__init__(self,master,width=250,height=30, - relief='raised',bd=1,bg='gray60',**kw) - self.leftbrack = self.create_line(0,0,0,0,0,0,0,0,width=5) - self.rightbrack = self.create_line(0,0,0,0,0,0,0,0,width=5) - self.shade = self.create_rectangle(0,0,0,0,fill='gray70',outline=None) - self.time = self.create_line(0,0,0,0,fill='red',width=2) - self.redrawzoom() - self.bind("",self.redrawzoom) - 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) + self.root = self.widget.get_root_item() + self.leftbrack = goocanvas.Polyline(parent=self.root, + line_width=5, stroke_color='black') + self.rightbrack = goocanvas.Polyline(parent=self.root, + line_width=5, stroke_color='black') + self.shade = goocanvas.Rect(parent=self.root, + fill_color='gray70', + line_width=.5) + self.time = goocanvas.Polyline(parent=self.root, + line_width=2, + stroke_color='red') + self.redrawzoom() + self.widget.connect("size-allocate", self.redrawzoom) + + self.widget.connect("motion-notify-event", self.adjust) + self.widget.connect("button-release-event", self.release) + self.leftbrack.connect("button-press-event", + lambda i, t, ev: self.press(ev, 'start')) + self.rightbrack.connect("button-press-event", + lambda i, t, ev: self.press(ev, 'end')) + self.shade.connect("button-press-event", + lambda i, t, ev: self.press(ev, 'offset')) - 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.redrawzoom()), - "max time",weak=0) + dispatcher.connect(self.max_time, "max time") dispatcher.connect(self.zoom_about_mouse, "zoom about mouse") dispatcher.connect(self.see_time, "see time") dispatcher.connect(self.see_time_until_end, "see time until end") dispatcher.connect(self.show_all, "show all") dispatcher.connect(self.zoom_to_range, "zoom to range") self.created=1 + self.lastTime = 0 + + def max_time(self, maxtime): + self.maxtime = maxtime + self.redrawzoom() + def zoom_to_range(self,start,end): self.start = start self.end = end @@ -108,7 +117,10 @@ class Zoomcontrol(object,tk.Canvas): self.end = t + factor*(self.end-t) self.redrawzoom() - def see_time(self, t): + def see_time(self, t=None): + """defaults to current time""" + if t is None: + t = self.lastTime vis_seconds = self.end - self.start margin = vis_seconds * .1 if t < self.start or t > (self.end - vis_seconds * .3): @@ -116,24 +128,32 @@ class Zoomcontrol(object,tk.Canvas): self.redrawzoom() - def see_time_until_end(self, t): + def see_time_until_end(self, t=None): + """defaults to current time""" + if t is None: + t = self.lastTime self.start = t - 2 self.end = self.maxtime self.redrawzoom() def input_time(self,val): - t=val - x=self.can_for_t(t) - self.coords(self.time,x,0,x,self.winfo_height()) + self.lastTime = val + x = self.can_for_t(self.lastTime) + self.time.set_property("points", + goocanvas.Points([(x, 0), + (x, self.size.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 release(self, widget, ev): + if hasattr(self,'adjustingattr'): + del self.adjustingattr + if hasattr(self,'lastx'): + del self.lastx - def adjust(self,ev,attr=None): + def adjust(self, widget, ev): if not hasattr(self,'adjustingattr'): return @@ -147,36 +167,63 @@ class Zoomcontrol(object,tk.Canvas): self.redrawzoom() def can_for_t(self,t): - return (t-self.mintime)/(self.maxtime-self.mintime)*(self.winfo_width()-30)+20 + a, b = self.mintime, self.maxtime + return (t - a) / (b - a) * (self.size.width - 30) + 20 def t_for_can(self,x): - return (x-20)/(self.winfo_width()-30)*(self.maxtime-self.mintime)+self.mintime + a, b = self.mintime, self.maxtime + return (x - 20) / (self.size.width - 30) * (b - a) + a def redrawzoom(self,*args): """redraw pieces based on start/end""" + self.size = self.widget.get_allocation() dispatcher.send("zoom changed") - if not hasattr(self,'created'): return - y1,y2=3,self.winfo_height()-3 + if not hasattr(self,'created'): + return + y1, y2 = 3, self.size.height - 3 lip = 6 scan = self.can_for_t(self.start) 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+5,y1+lip,ecan-5,y2-lip) + + self.leftbrack.set_property("points", goocanvas.Points([ + (scan + lip, y1), + (scan, y1), + (scan, y2), + (scan + lip, y2)])) + self.rightbrack.set_property("points", goocanvas.Points([ + (ecan - lip, y1), + (ecan, y1), + (ecan, y2), + (ecan - lip, y2)])) + self.shade.set_properties( + x=scan + 5, + y=y1 + lip, + width=max(0, ecan - 5 - (scan + 5)), + height=max(0, y2 - lip - (y1 + lip))) + self.redrawTics() def redrawTics(self): - self.delete("tics") - lastx=-1000 + if hasattr(self, 'ticsGroup'): + self.ticsGroup.remove() + self.ticsGroup = goocanvas.Group(parent=self.root) + + lastx =- 1000 + for t in range(0,int(self.maxtime)): x = self.can_for_t(t) - if 0 < x < self.winfo_width() and x-lastx>30: - txt=str(t) - if lastx==-1000: - 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='arial 7') + if 0 < x < self.size.width and x - lastx > 30: + txt = str(t) + if lastx == -1000: + txt = txt + "sec" + goocanvas.Polyline(parent=self.ticsGroup, + points=goocanvas.Points([(x, 0), (x, 15)]), + line_width=.8, + stroke_color='black') + goocanvas.Text(parent=self.ticsGroup, + x=x, y=self.size.height-1, + anchor=gtk.ANCHOR_SOUTH, + text=txt, + font='ubuntu 7') lastx = x