diff --git a/light9/curve.py b/light9/curve.py --- a/light9/curve.py +++ b/light9/curve.py @@ -49,6 +49,87 @@ class Curve: self.points.insert(i,new_pt) __call__=eval +class RegionZoom: + """rigs c-a-b1 to drag out an area to zoom to.""" + def __init__(self, canvas, world_from_screen, screen_from_world): + self.canvas, self.world_from_screen = canvas, world_from_screen + self.screen_from_world = screen_from_world + + for evtype, method in [("ButtonPress-1",self.press), + ("Motion",self.motion), + ("ButtonRelease",self.release)]: + canvas.bind("" % evtype, method) + if evtype != "ButtonPress-1": + canvas.bind("<%s>" % evtype, method) + canvas.bind("", self.finish) + self.start_t = None + + def press(self,ev): + if self.start_t is not None: + self.finish() + + self.start_t = self.end_t = self.world_from_screen(ev.x,0)[0] + self.start_x = ev.x + can = self.canvas + + for pos in ('start_t','end_t','hi','lo'): + can.create_line(0,0,50,50, width=3, fill='black', + tags=("regionzoom",pos)) + # if updatelines isn't called here, subsequent updatelines + # will fail for reasons i don't understand + self.updatelines() + + self.old_cursor = can.cget("cursor") + #xcursorgen + can.config(cursor="@/home/drewp/projects/light9/cout red") + + def updatelines(self): + can = self.canvas + pos_x = {} + height = can.winfo_height() + for pos in ('start_t', 'end_t'): + pos_x[pos] = x = self.screen_from_world((getattr(self,pos),0))[0] + cid = can.find_withtag("regionzoom && %s" % pos) + can.coords(cid, x, 0, x, height) + + for tag,frac in [('hi',.1),('lo',.9)]: + cid = can.find_withtag("regionzoom && %s" % tag) + can.coords(cid, pos_x['start_t'], frac * height, + pos_x['end_t'], frac * height) + + def motion(self,ev): + if self.start_t is None: + return + + self.end_t = self.world_from_screen(ev.x,0)[0] + self.updatelines() + + def release(self,ev): + if self.start_t is None: + return + + if abs(self.start_x - ev.x) < 10: + # clicked + factor = 1/1.5 + if ev.state & 1: + factor = 1.5 # c-s-a-b1 zooms out + dispatcher.send("zoom about mouse", + t=self.start_t, + factor=factor) + + self.finish() + return + + dispatcher.send("zoom to range", + start=min(self.start_t, self.end_t), + end=max(self.start_t, self.end_t)) + self.finish() + + def finish(self, *ev): + self.canvas.delete("regionzoom") + self.start_t = None + self.canvas.config(cursor=self.old_cursor) + class Curveview(tk.Canvas): def __init__(self,master,curve,**kw): self.curve=curve @@ -80,6 +161,8 @@ class Curveview(tk.Canvas): self.bind("",lambda ev: dispatcher.send("see time", t=self.current_time())) + RegionZoom(self, self.world_from_screen, self.screen_from_world) + def current_time(self): return self._time @@ -114,24 +197,27 @@ class Curveview(tk.Canvas): self.delete('curve') - for x in range(0,self.winfo_width(),3): + if self.winfo_height() < 30: + self._draw_gradient() + else: + self._draw_markers(visible_x) + self._draw_line(visible_points) + + self.dots = {} # idx : canvas rectangle + + if len(visible_points)<50: + self._draw_handle_points(visible_idxs,visible_points) + + def _draw_gradient(self): + gradient_res = 3 + for x in range(0,self.winfo_width(),gradient_res): wx = self.world_from_screen(x,0)[0] mag = self.curve.eval(wx) self.create_line(x,0, x,70, fill=gradient(mag, low=(20,10,50), high=(255,187,255)), - width=3, tags='curve') - - - self._draw_markers(visible_x) - - self._draw_line(visible_points) - - self.dots = {} # idx : canvas rectangle - - if len(visible_points)<50: - self._draw_handle_points(visible_idxs,visible_points) + width=gradient_res, tags='curve') def _draw_markers(self,visible_x): mark = self._draw_one_marker diff --git a/light9/zoomcontrol.py b/light9/zoomcontrol.py --- a/light9/zoomcontrol.py +++ b/light9/zoomcontrol.py @@ -11,19 +11,26 @@ class Zoomcontrol(object,tk.Canvas): def fget(self): return self._maxtime def fset(self, value): self._maxtime = value - self.updatewidget() + self.redrawzoom() return locals() maxtime = property(**maxtime()) - + + _end = _start = 0 def start(): def fget(self): return self._start - def fset(self,v): self._start = max(self.mintime,v) + def fset(self,v): + v = max(self.mintime,v) + if v < self._end: + self._start = v return locals() start = property(**start()) def end(): def fget(self): return self._end - def fset(self,v): self._end = min(self.maxtime,v) + def fset(self,v): + v = min(self.maxtime,v) + if v > self._start: + self._end = v return locals() end = property(**end()) @@ -38,8 +45,8 @@ class Zoomcontrol(object,tk.Canvas): 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.updatewidget() - self.bind("",self.updatewidget) + self.redrawzoom() + self.bind("",self.redrawzoom) if 0: # works, but you have to stay in the widget while you drag @@ -65,16 +72,22 @@ class Zoomcontrol(object,tk.Canvas): 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+15), - self.updatewidget()), + self.redrawzoom()), "max time",weak=0) dispatcher.connect(self.zoom_about_mouse,"zoom about mouse") dispatcher.connect(self.see_time,"see time") + dispatcher.connect(self.zoom_to_range,"zoom to range") self.created=1 + def zoom_to_range(self,start,end): + self.start = start + self.end = end + self.redrawzoom() + 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") + self.redrawzoom() + def see_time(self,t): vis_seconds = self.end - self.start margin = vis_seconds * .9 # left side is nicest @@ -83,8 +96,7 @@ class Zoomcontrol(object,tk.Canvas): # t doesn't have to be ALL the way off-screen if t > (self.end - vis_seconds * .3): self.offset += (t - self.end) + margin - self.updatewidget() - dispatcher.send("zoom changed") + self.redrawzoom() def input_time(self,val): t=val @@ -107,8 +119,7 @@ class Zoomcontrol(object,tk.Canvas): new = self.can_for_t(getattr(self,attr)) + (ev.x - self.lastx) self.lastx = ev.x setattr(self,attr,self.t_for_can(new)) - self.updatewidget() - dispatcher.send("zoom changed") + self.redrawzoom() def offset(): doc = "virtual attr that adjusts start and end together" @@ -126,8 +137,9 @@ class Zoomcontrol(object,tk.Canvas): def t_for_can(self,x): return (x-20)/(self.winfo_width()-30)*(self.maxtime-self.mintime)+self.mintime - def updatewidget(self,*args): + def redrawzoom(self,*args): """redraw pieces based on start/end""" + dispatcher.send("zoom changed") if not hasattr(self,'created'): return y1,y2=3,self.winfo_height()-3 lip = 6 @@ -149,3 +161,4 @@ class Zoomcontrol(object,tk.Canvas): self.create_text(x,self.winfo_height()-1,anchor='s', text=txt,tags=('tics',),font='6x13') lastx = x +