Changeset - 603383733d7c
[Not reviewed]
default
0 1 0
drewp@bigasterisk.com - 13 years ago 2012-06-18 00:42:48
drewp@bigasterisk.com
default to collapsed curves (with a display bug)
Ignore-this: 4f556f9781ea3a9406d4c219896f571b
1 file changed with 4 insertions and 0 deletions:
0 comments (0 inline, 0 general)
light9/curvecalc/curveview.py
Show inline comments
 
@@ -612,559 +612,563 @@ class Curveview(object):
 

	
 
        if not getattr(self, 'timelineLine', None):
 
            self.timelineGroup = goocanvas.Group(parent=self.root)
 
            self.timelineLine = goocanvas.Polyline(
 
                parent=self.timelineGroup,
 
                points=goocanvas.Points([(0,0), (0,0)]),
 
                line_width=2, stroke_color='red')
 

	
 
        self.timelineLine.set_property('points', goocanvas.Points([
 
            self.screen_from_world((t, 0)),
 
            self.screen_from_world((t, 1))]))
 
        
 
        self._time = t
 
        if self.knobEnabled:
 
            self.delete('knob')
 
            prevKey = self.curve.point_before(t)
 
            if prevKey is not None:
 
                pos = self.screen_from_world(prevKey)
 
                self.create_oval(pos[0] - 8, pos[1] - 8,
 
                                 pos[0] + 8, pos[1] + 8,
 
                                 outline='#800000',
 
                                 tags=('knob',))
 
                dispatcher.send("knob out", value=prevKey[1], curve=self.curve)
 

	
 
    def canvasIsVisible(self):
 
        if not hasattr(self, "scrollWin"):
 
            self.scrollWin = self.canvas
 
            while not isinstance(self.scrollWin, gtk.ScrolledWindow):
 
                self.scrollWin = self.scrollWin.get_parent()
 

	
 
        sw = self.scrollWin
 
        top = sw.get_toplevel()
 
        visy1 = sw.translate_coordinates(top, 0, 0)[1]
 
        visy2 = visy1 + sw.get_allocation().height
 

	
 
        coords = self.canvas.translate_coordinates(top, 0, 0)
 
        if not coords: # probably broken after a reload()
 
            return False
 
        cany1 = coords[1]
 
        cany2 = cany1 + self.canvas.get_allocation().height
 
        return not (cany2 < visy1 or cany1 > visy2)
 
        
 
    def update_curve(self, *args):
 
        if not self.redrawsEnabled:
 
            return
 

	
 
        if not self.canvasIsVisible():
 
            self.culled = True
 
            return
 
        self.culled = False
 
        
 
        self.size = self.canvas.get_allocation()
 
 
 
        cp = self.curve.points
 
        visible_x = (self.world_from_screen(0,0)[0],
 
                     self.world_from_screen(self.size.width, 0)[0])
 

	
 
        visible_idxs = self.curve.indices_between(visible_x[0], visible_x[1],
 
                                                  beyond=1)
 
        visible_points = [cp[i] for i in visible_idxs]
 

	
 
        if getattr(self, 'curveGroup', None):
 
            self.curveGroup.remove()
 
        self.curveGroup = goocanvas.Group(parent=self.root)
 

	
 
        # this makes gtk quietly stop working. Getting called too early?
 
        #self.canvas.set_property("background-color",
 
        #                         "gray20" if self.curve.muted else "black")
 

	
 
        self.update_time_bar(self._time)
 
        if self.size.height < 40:
 
            self._draw_line(visible_points, area=True)
 
        else:
 
            self._draw_time_tics(visible_x)
 
            self._draw_line(visible_points)
 
            self._draw_markers(
 
                self.markers.points[i] for i in
 
                self.markers.indices_between(visible_x[0], visible_x[1]))
 

	
 
            self.dots = {} # idx : canvas rectangle
 

	
 
            if len(visible_points) < 50 and not self.curve.muted:
 
                self._draw_handle_points(visible_idxs,visible_points)
 

	
 
        self.selectionChanged()
 

	
 
    def is_music(self):
 
        """are we one of the music curves (which might be drawn a bit
 
        differently)"""
 
        return self._isMusic
 

	
 
    def _draw_markers(self, pts):
 
        colorMap = {
 
            'q':'#598522',
 
            'w':'#662285',
 
            'e':'#852922',
 
            'r':'#85225C',
 
            't':'#856B22',
 
            'y':'#227085',
 
            }
 
        for t, name in pts:
 
            x = int(self.screen_from_world((t,0))[0]) + .5
 
            goocanvas.polyline_new_line(self.curveGroup,
 
                                        x, 0, x, self.size.height,
 
                                        line_width=.4 if name in 'rty' else .8,
 
                                        stroke_color=colorMap.get(name, 'gray'))
 

	
 
    def _draw_time_tics(self,visible_x):
 
        tic = self._draw_one_tic
 

	
 
        tic(0, "0")
 
        t1,t2=visible_x
 
        if t2-t1<30:
 
            for t in range(int(t1),int(t2)+1):
 
                tic(t,str(t))
 
        tic(introPad, str(introPad))
 

	
 
        endtimes = dispatcher.send("get max time")
 
        if endtimes:
 
            endtime = endtimes[0][1]
 
            tic(endtime, "end %.1f"%endtime)
 
            tic(endtime - postPad, "post %.1f" % (endtime - postPad))
 
        
 
    def _draw_one_tic(self,t,label):
 
        x = self.screen_from_world((t,0))[0]
 
        ht = self.size.height
 
        if not 0 <= x < self.size.width:
 
            return
 
        x = max(5, x) # cheat left-edge stuff onscreen
 
        goocanvas.polyline_new_line(self.curveGroup,
 
                                    x, ht,
 
                                    x, ht - 20,
 
                                    line_width=.5,
 
                                    stroke_color='gray70')
 
        goocanvas.Text(parent=self.curveGroup,
 
                       fill_color="white",
 
                       anchor=gtk.ANCHOR_SOUTH,
 
                       font="ubuntu 7",
 
                       x=x+3, y=ht-20,
 
                       text=label)
 

	
 
    def _draw_line(self, visible_points, area=False):
 
        linepts=[]
 
        step=1
 
        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]:
 
            x,y = self.screen_from_world(p)
 
            linepts.append((int(x) + .5, int(y) + .5))
 

	
 
        if self.curve.muted:
 
            fill = 'grey34'
 
        else:
 
            fill = 'white'
 

	
 
        if area:
 
            base = self.screen_from_world((0, 0))[1]
 
            base = base + linewidth / 2
 
            goocanvas.Polyline(parent=self.curveGroup,
 
                               points=goocanvas.Points(
 
                                   [(linepts[0][0], base)] +
 
                                   linepts +
 
                                   [(linepts[-1][0], base)]),
 
                               close_path=True,
 
                               line_width=0,
 
                               fill_color="green",
 
                               )
 

	
 
        self.pl = goocanvas.Polyline(parent=self.curveGroup,
 
                                     points=goocanvas.Points(linepts),
 
                                     line_width=linewidth,
 
                                     stroke_color=fill,
 
                                     )
 
                
 
            
 
    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 = goocanvas.Rect(parent=self.curveGroup,
 
                                 x=int(p[0] - rad) + .5,
 
                                 y=int(p[1] - rad) + .5,
 
                                 width=rad * 2, height=rad * 2,
 
                                 stroke_color='gray90',
 
                                 fill_color='blue',
 
                                 line_width=1,
 
                                 #tags=('curve','point', 'handle%d' % i)
 
                                 )
 

	
 
            if worldp[1] == 0:
 
                rad += 3
 
                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,"<ButtonPress-1>",
 
            #              lambda ev,i=i: self.dotpress(ev,i))
 
            #self.tag_bind('handle%d' % i, "<Key-d>",
 
            #              lambda ev, i=i: self.remove_point_idx(i))
 
                      
 
            self.dots[i]=dot
 

	
 
        self.highlight_selected_dots()
 

	
 
    def find_index_near(self,x,y):
 
        tags = self.gettags(self.find_closest(x, y))
 
        try:
 
            handletags = [t for t in tags if t.startswith('handle')]
 
            return int(handletags[0][6:])
 
        except IndexError:
 
            raise ValueError("no point found")
 
        
 
    def new_point_at_mouse(self, ev):
 
        p = self.world_from_screen(ev.x,ev.y)
 
        x = p[0]
 
        y = self.curve.eval(x)
 
        self.add_point((x, y))
 

	
 
    def add_points(self, pts):
 
        idxs = [self.curve.insert_pt(p) for p in pts]
 
        self.update_curve()
 
        self.select_indices(idxs)
 
        
 
    def add_point(self, p):
 
        self.add_points([p])
 

	
 
    def add_marker(self, p):
 
        self.markers.insert_pt(p)
 
        self.update_curve()
 
        
 
    def remove_point_idx(self, *idxs):
 
        idxs = list(idxs)
 
        while idxs:
 
            i = idxs.pop()
 

	
 
            self.curve.points.pop(i)
 
            newsel = []
 
            newidxs = []
 
            for si in range(len(self.selected_points)):
 
                sp = self.selected_points[si]
 
                if sp == i:
 
                    continue
 
                if sp > i:
 
                    sp -= 1
 
                newsel.append(sp)
 
            for ii in range(len(idxs)):
 
                if ii > i:
 
                    ii -= 1
 
                newidxs.append(idxs[ii])
 

	
 
            self.select_indices(newsel)
 
            idxs[:] = newidxs
 
            
 
        self.update_curve()
 

	
 
    def highlight_selected_dots(self):
 
        if not self.redrawsEnabled:
 
            return
 

	
 
        for i,d in self.dots.items():
 
            if i in self.selected_points:
 
                d.set_property('fill_color', 'red')
 
            else:
 
                d.set_property('fill_color', 'blue')
 
        
 
    def dotpress(self, r1, r2, ev, dotidx):
 
        self.print_state("dotpress")
 
        if dotidx not in self.selected_points:
 
            self.select_indices([dotidx])
 

	
 
        self.last_mouse_world = self.world_from_screen(ev.x, ev.y)
 
        self.dragging_dots = True
 

	
 
    def select_between(self,start,end):
 
        if start > end:
 
            start, end = end, start
 
        self.select_indices(self.curve.indices_between(start,end))
 

	
 
    def onEnter(self, widget, event):
 
        self.entered = True
 

	
 
    def onLeave(self, widget, event):
 
        self.entered = False
 

	
 
    def onMotion(self, widget, event):
 
        self.lastMouseX = event.x
 

	
 
        if event.state & gtk.gdk.SHIFT_MASK and 1: # and B1
 
            self.sketch_motion(event)
 
            return
 

	
 
        self.select_motion(event)
 
        
 
        if not self.dragging_dots:
 
            return
 
        if not event.state & 256:
 
            return # not lmb-down
 
        cp = self.curve.points
 
        moved=0
 

	
 
        cur = self.world_from_screen(event.x, event.y)
 
        if self.last_mouse_world:
 
            delta = (cur[0] - self.last_mouse_world[0],
 
                     cur[1] - self.last_mouse_world[1])
 
        else:
 
            delta = 0,0
 
        self.last_mouse_world = cur
 
        
 
        for idx in self.selected_points:
 

	
 
            newp = [cp[idx][0] + delta[0], cp[idx][1] + delta[1]]
 
            
 
            newp[1] = max(0,min(1,newp[1]))
 
            
 
            if idx>0 and newp[0] <= cp[idx-1][0]:
 
                continue
 
            if idx<len(cp)-1 and newp[0] >= cp[idx+1][0]:
 
                continue
 
            moved=1
 
            cp[idx] = tuple(newp)
 
        if moved:
 
            self.update_curve()
 

	
 
    def unselect(self):
 
        self.select_indices([])
 

	
 
    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")
 

	
 
        if event.state & gtk.gdk.SHIFT_MASK: # relese-B1
 
            self.sketch_release(event)
 
            return
 

	
 
        self.select_release(event)
 
 
 
        if not self.dragging_dots:
 
            return
 
        self.last_mouse_world = None
 
        self.dragging_dots = False
 

	
 
class CurveRow(object):
 
    """
 
    one of the repeating curve rows (including widgets on the left)
 

	
 
    i wish these were in a list-style TreeView so i could set_reorderable on it
 

	
 
    please pack self.box
 
    """
 
    def __init__(self, name, curve, markers, slider, knobEnabled, zoomControl):
 
        self.name = name
 
        self.box = gtk.VBox()
 
        self.box.set_border_width(1)
 

	
 
        self.cols = gtk.HBox()
 
        self.box.add(self.cols)
 
        
 
        controls = gtk.Frame()
 
        controls.set_size_request(115, -1)
 
        controls.set_shadow_type(gtk.SHADOW_OUT)
 
        self.cols.pack_start(controls, expand=False)
 
        self.setupControls(controls, name, curve, slider)
 

	
 
        self.curveView = Curveview(curve, markers, knobEnabled=knobEnabled,
 
                                   isMusic=name in ['music', 'smooth_music'],
 
                                   zoomControl=zoomControl)
 
        
 
        self.initCurveView()
 
        dispatcher.connect(self.rebuild, "all curves rebuild")
 

	
 
        # incomplete: this makes the row start with a small size
 
        # request, but doesn't update the row widgets
 
        self.collapsed.props.active = True
 

	
 
    def rebuild(self):
 
        self.curveView.rebuild()
 
        self.initCurveView()
 
        self.update_ui_to_collapsed_state()
 

	
 
    def initCurveView(self):
 
        self.curveView.widget.show()
 
        self.curveView.widget.set_size_request(-1, 100)
 
        self.cols.pack_start(self.curveView.widget, expand=True)       
 
        
 
    def setupControls(self, controls, name, curve, slider):
 
        box = gtk.VBox()
 
        controls.add(box)
 
        
 
        txt = "curve '%s'" % name
 
        if len(name) > 7:
 
            txt = name
 
        curve_name_label = gtk.Label(txt)
 
        box.pack_start(curve_name_label)
 

	
 
        bools = gtk.HBox()
 
        box.pack_start(bools)
 
        self.collapsed = gtk.CheckButton("C")
 
        bools.pack_start(self.collapsed)
 
        self.collapsed.connect("toggled", self.update_ui_to_collapsed_state)
 
        self.hideWhenCollapsed = [bools]
 
        self.muted = gtk.CheckButton("M")
 
        
 
        bools.pack_start(self.muted)
 
        self.muted.connect("toggled", self.sync_mute_to_curve)
 
        dispatcher.connect(self.mute_changed, 'mute changed', sender=curve)
 

	
 
        self.sliderLabel = None
 
        if slider is not None:
 
            # slider should have a checkbutton, defaults to off for
 
            # music tracks
 
            self.sliderLabel = gtk.Label("Slider %s" % slider)
 
            box.pack_start(self.sliderLabel)
 

	
 
        # widgets that need recoloring when we tint the row:
 
        #self.widgets = [leftside, self.collapsed, self.muted,
 
        #                curve_name_label, self]
 
        #if self.sliderLabel:
 
        #    self.widgets.append(self.sliderLabel)
 

	
 
    def onDelete(self):
 
        self.curveView.onDelete()
 
        
 
    def update_ui_to_collapsed_state(self, *args):
 
        if self.collapsed.get_active():
 
            self.curveView.widget.set_size_request(-1, 25)
 
            [w.hide() for w in self.hideWhenCollapsed]
 
        else:
 
            self.curveView.widget.set_size_request(-1, 100)
 
            [w.show() for w in self.hideWhenCollapsed]
 

	
 
    def sync_mute_to_curve(self, *args):
 
        """send value from CheckButton to the master attribute inside Curve"""
 
        new_mute = self.muted.get_active()
 
        self.curveView.curve.muted = new_mute
 

	
 
    def update_mute_look(self):
 
        """set colors on the widgets in the row according to self.muted.get()"""
 
        # not yet ported for gtk
 
        return
 
        if self.curveView.curve.muted:
 
            new_bg = 'grey20'
 
        else:
 
            new_bg = 'normal'
 

	
 
        for widget in self.widgets:
 
            widget['bg'] = new_bg
 

	
 
    def mute_changed(self):
 
        """call this if curve.muted changed"""
 
        self.muted.set_active(self.curveView.curve.muted)
 
        #self.update_mute_look()
 

	
 

	
 
class Curvesetview(object):
 
    """
 
    
 
    """
 
    def __init__(self, curvesVBox, zoomControlBox, curveset):
 
        self.live = True
 
        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()
 

	
 
        for c in curveset.curveNamesInOrder():
 
            self.add_curve(c) 
 

	
 
        dispatcher.connect(self.add_curve, "add_curve", sender=self.curveset)
 
        
 
        self.newcurvename = gtk.EntryBuffer("", 0)
 

	
 
        eventBox = self.curvesVBox.get_parent()
 
        eventBox.connect("key-press-event", self.onKeyPress)
 
        eventBox.connect("button-press-event", self.takeFocus)
 

	
 
    def __del__(self):
 
        print "del curvesetview", id(self) 
 

	
 
    def takeFocus(self, *args):
 
        """the whole curveset's eventbox is what gets the focus, currently, so
 
        keys like 'c' can work in it"""
 
        dispatcher.send("all curves lose selection")
 
        self.curvesVBox.get_parent().grab_focus()
 

	
 
    def onKeyPress(self, widget, event):
 
        if not self.live: # workaround for old instances living past reload()
 
            return
 

	
 
        if event.string == 'c':
 
            r = self.row_under_mouse()
 
            # calling toggled() had no effect; don't know why
 
            r.collapsed.set_active(not r.collapsed.get_active())
 
 
 
    def row_under_mouse(self):
 
        x, y = self.curvesVBox.get_pointer()
 
        for r in self.allCurveRows:
 
            inRowX, inRowY = self.curvesVBox.translate_coordinates(r.box, x, y)
 
            _, _, w, h = r.box.get_allocation()
 
            if 0 <= inRowX < w and 0 <= inRowY < h:
 
                return r
 
        raise ValueError("no curveRow is under the mouse")
 

	
 
    def focus_entry(self):
 
        self.entry.focus()
 

	
 
    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(name, curve, self.curveset.markers,
 
                     slider, knobEnabled, self.zoomControl)
 
        self.curvesVBox.pack_start(f.box)
 
        f.box.show_all()
 
        self.allCurveRows.add(f)
 
        f.curveView.goLive()
 

	
 
    def row(self, name):
 
        matches = [r for r in self.allCurveRows if r.name == name]
 
        if not matches:
 
            raise ValueError("no curveRow named %r" % name)
 
        return matches[0]
 

	
 
    def goLive(self):
 
        """for startup performance, none of the curves redraw
 
        themselves until this is called once (and then they're normal)"""
 
        
 
        for cr in self.allCurveRows:
 
            cr.curveView.goLive()
 

	
 
    def onDelete(self):
 
        for r in self.allCurveRows:
 
            r.onDelete()
 

	
 
    def collapseAll(self):
 
        for r in self.allCurveRows:
 
            r.collapsed.set_active(True)
 

	
 
    def collapseNone(self):
 
        for r in self.allCurveRows:
 
            r.collapsed.set_active(False)
 

	
 
        
0 comments (0 inline, 0 general)