changeset 239:1d41d8658a36

initial zoom-to-range feature in curveview
author drewp@bigasterisk.com
date Mon, 13 Jun 2005 00:43:41 +0000
parents fca9832d207f
children 76e326329610
files light9/curve.py light9/zoomcontrol.py
diffstat 2 files changed, 125 insertions(+), 26 deletions(-) [+]
line wrap: on
line diff
--- a/light9/curve.py	Mon Jun 13 00:42:20 2005 +0000
+++ b/light9/curve.py	Mon Jun 13 00:43:41 2005 +0000
@@ -49,6 +49,87 @@
         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("<Control-Alt-%s>" % evtype, method)
+            if evtype != "ButtonPress-1":
+                canvas.bind("<%s>" % evtype, method)
+        canvas.bind("<Leave>", 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 @@
         self.bind("<Key-Escape>",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 @@
         
         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
--- a/light9/zoomcontrol.py	Mon Jun 13 00:42:20 2005 +0000
+++ b/light9/zoomcontrol.py	Mon Jun 13 00:43:41 2005 +0000
@@ -11,19 +11,26 @@
         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 @@
         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("<Configure>",self.updatewidget)
+        self.redrawzoom()
+        self.bind("<Configure>",self.redrawzoom)
 
         if 0:
             # works, but you have to stay in the widget while you drag
@@ -65,16 +72,22 @@
         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 @@
         # 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 @@
         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 @@
     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 @@
                 self.create_text(x,self.winfo_height()-1,anchor='s',
                                  text=txt,tags=('tics',),font='6x13')
                 lastx = x
+