Changeset - 1b81e55ebb73
[Not reviewed]
default
0 3 0
Drew Perttula - 13 years ago 2012-06-13 08:39:33
drewp@bigasterisk.com
new selectmanip can translate points in X. previous multi-point dragging has been lost along the way
Ignore-this: 1364fa68e7963e7de12a6349d23d3be8
3 files changed with 142 insertions and 67 deletions:
0 comments (0 inline, 0 general)
light9/curvecalc/curveview.py
Show inline comments
 
@@ -68,24 +68,122 @@ class Sketch:
 
                self.curveview.add_point(p)
 
                finalPoints.append(p)
 
            
 
        self.curveview.update_curve()
 
        self.curveview.select_points(finalPoints)
 

	
 
class SelectManip(object):
 
    """
 
    selection manipulator is created whenever you have a selection. It
 
    draws itself on the canvas and edits the points when you drag
 
    various parts
 
    """
 
    def __init__(self, parent, getSelectedIndices, getWorldPoint, getScreenPoint, getCanvasSize, setPoints, getWorldTime):
 
        """parent goocanvas group"""
 
        self.getSelectedIndices = getSelectedIndices
 
        self.getWorldPoint = getWorldPoint
 
        self.getScreenPoint = getScreenPoint
 
        self.getCanvasSize = getCanvasSize
 
        self.setPoints = setPoints
 
        self.getWorldTime = getWorldTime
 
        self.grp = goocanvas.Group(parent=parent)
 
        
 
        self.title = goocanvas.Text(parent=self.grp, text="selectmanip", x=10, y=10, fill_color='white', font="ubuntu 10")
 

	
 
        self.bbox = goocanvas.Rect(parent=self.grp,
 
                                   stroke_color='yellow',
 
                                   fill_color_rgba=0xffff0030,
 
                                   line_width=.7)
 

	
 
        self.xTrans = goocanvas.Polyline(parent=self.grp, close_path=True,
 
                                         fill_color_rgba=0xffffff88,
 
                                         )
 
        self.xTrans.connect("button-press-event", self.onXPress)
 
        self.xTrans.connect("button-release-event", self.onXRelease)
 
        self.xTrans.connect("motion-notify-event", self.onXMotion)
 
        self.update()
 

	
 
    def onXPress(self, item, target_item, event):
 
        self.dragStartTime = self.getWorldTime(event.x)
 
        self.origPoints = [self.getWorldPoint(i) for i in
 
                           self.getSelectedIndices()]
 
        return True
 

	
 
    def onXMotion(self, item, target_item, event):
 
        if hasattr(self, 'dragStartTime'):
 
            dt = self.getWorldTime(event.x) - self.dragStartTime
 
            self.setPoints(
 
                (i, (orig[0] + dt, orig[1]))
 
                for i, orig in zip(self.getSelectedIndices(), self.origPoints))
 
            
 

	
 
    def onXRelease(self, item, target_item, event):
 
        del self.dragStartTime
 

	
 
    def update(self):
 
        """if the view or selection or selected point positions
 
        change, call this to redo the layout of the manip"""
 
        idxs = self.getSelectedIndices()
 
        pts = [self.getScreenPoint(i) for i in idxs]
 
        
 
        b = self.bbox.props
 
        b.x = min(p[0] for p in pts) - 5
 
        b.y = min(p[1] for p in pts) - 5
 
        margin = 10 if len(pts) > 1 else 0
 
        b.width = max(p[0] for p in pts) - b.x + margin
 
        b.height = min(max(p[1] for p in pts) - b.y + margin,
 
                       self.getCanvasSize().height - b.y - 1)
 

	
 
        b.visibility = goocanvas.ITEM_VISIBLE if len(pts) > 1 else goocanvas.ITEM_HIDDEN
 

	
 
        self.title.props.text = "%s %s selected" % (
 
            len(idxs), "point" if len(idxs) == 1 else "points")
 

	
 
        centerX = b.x + b.width / 2
 

	
 
        midY = self.getCanvasSize().height / 2
 
        x1 = centerX - 30
 
        x2 = centerX - 20
 
        x3 = centerX + 20
 
        x4 = centerX + 30
 
        y1 = midY - 10
 
        y2 = midY - 5
 
        y3 = midY + 5
 
        y4 = midY + 10
 
        shape = [
 
            (x1, midY), # left tip
 
            (x2, y1),
 
            (x2, y2),
 
            
 
            (x3, y2),
 
            (x3, y1),
 
            (x4, midY), # right tip
 
            (x3, y4),
 
            (x3, y3),
 
            
 
            (x2, y3),
 
            (x2, y4)
 
            ]
 

	
 
        self.xTrans.props.points = goocanvas.Points(shape)
 

	
 
    def destroy(self):
 
        self.grp.remove()
 

	
 
class Curveview(object):
 
    """
 
    graphical curve widget only. Please pack .widget
 
    """
 
    def __init__(self, curve, knobEnabled=False, isMusic=False,
 
                 zoomControl=None):
 
        """knobEnabled=True highlights the previous key and ties it to a
 
        hardware knob"""
 
        self.redrawsEnabled = False
 

	
 
        self.rebuild()
 
        
 
        self.redrawsEnabled = False
 
        self.curve = curve
 
        self.knobEnabled = knobEnabled
 
        self._isMusic = isMusic
 
        self.zoomControl = zoomControl
 
        self._time = -999
 
        self.last_mouse_world = None
 
@@ -110,44 +208,33 @@ class Curveview(object):
 
        #        def curs(ev):
 
        #            print ev.state
 
        #        self.bind("<KeyPress>",curs)
 
        #        self.bind("<KeyRelease-Control_L>",lambda ev: curs(0))
 
      
 
        # this binds on c-a-b1, etc
 
        if 0:
 
        if 0: # unported
 
            self.regionzoom = RegionZoom(self, self.world_from_screen,
 
                                         self.screen_from_world)
 

	
 
        self.sketch = None # an in-progress sketch
 

	
 
        self.dragging_dots = False
 
        self.selecting = False
 
        
 
        if 0:
 
            self.bind("<ButtonPress-1>",#"<Alt-Key>",
 
                      self.select_press, add=True)
 
            self.bind("<Motion>", self.select_motion, add=True)
 
            self.bind("<ButtonRelease-1>", #"<Alt-KeyRelease>",
 
                      self.select_release, add=True)
 

	
 
            self.bind("<ButtonPress-1>", self.check_deselect, add=True)
 

	
 
            self.bind("<Key-m>", lambda *args: self.curve.toggleMute())
 

	
 
    def rebuild(self):
 
        """
 
        sometimes after windows get resized, canvas gets stuck with a
 
        weird offset. I can't find where it is, so for now we support
 
        rebuilding the canvas widget
 
        """
 
        if hasattr(self, 'widget'):
 
            self.widget.destroy()
 
            self._time = -999
 
            print "rebuilding canvas"
 

	
 
        self.timelineLine = self.curveGroup = None
 
        self.timelineLine = self.curveGroup = self.selectManip = None
 
        self.widget = gtk.EventBox()
 
        self.widget.set_can_focus(True)
 
        self.widget.add_events(gtk.gdk.KEY_PRESS_MASK |
 
                               gtk.gdk.FOCUS_CHANGE_MASK)
 
        self.onFocusOut()
 

	
 
@@ -175,26 +262,25 @@ class Curveview(object):
 
        self.root.connect("button-press-event", self.onCanvasPress)
 

	
 
        self.widget.connect("key-press-event", self.onKeyPress)
 

	
 
        self.widget.connect("focus-in-event", self.onFocusIn)
 
        self.widget.connect("focus-out-event", self.onFocusOut)
 
        self.widget.connect("event", self.onAny)
 
        #self.widget.connect("event", self.onAny)
 

	
 
    def onAny(self, w, event):
 
        print "   %s on %s" % (event, w)
 
        
 
    def onFocusIn(self, *args):
 
        self.widget.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("red"))
 

	
 
    def onFocusOut(self, widget=None, event=None):
 
        self.widget.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("gray30"))
 

	
 
    def onKeyPress(self, widget, event):
 
        print "canvas key", event
 
        if event.string in '12345':
 
        if event.string in list('12345'):
 
            x = int(event.string)
 
            self.add_point((self.current_time(), (x - 1) / 4.0))
 

	
 
    def onExpose(self, *args):
 
        if self.culled:
 
            self.update_curve()
 
@@ -208,15 +294,13 @@ class Curveview(object):
 
        # when we support multiple curves per canvas, this should find
 
        # the close one and add a point to that. Binding to the line
 
        # itself is probably too hard to hit. Maybe a background-color
 
        # really thick line would be a nice way to allow a sloppier
 
        # click
 

	
 
        print "focus on", self.widget
 
        self.widget.grab_focus()
 
        print "done grab"
 
        
 
        if event.get_state() & gtk.gdk.CONTROL_MASK:
 
            self.new_point_at_mouse(event)
 
        elif event.get_state() & gtk.gdk.SHIFT_MASK:
 
            self.sketch_press(event)
 
        else:
 
@@ -273,30 +357,48 @@ class Curveview(object):
 

	
 
    def print_state(self, msg=""):
 
        if 1:
 
            print "%s: dragging_dots=%s selecting=%s" % (
 
                msg, self.dragging_dots, self.selecting)
 

	
 
    def check_deselect(self,ev):
 
        try:
 
            self.find_index_near(ev.x, ev.y)
 
        except ValueError:
 
            self.selected_points[:] = []
 
            self.highlight_selected_dots()
 

	
 
    def select_points(self, pts):
 
        """set selection to the given point values (tuples, not indices)"""
 
        idxs = []
 
        for p in pts:
 
            idxs.append(self.curve.points.index(p))
 
        self.select_indices(idxs)
 

	
 
    def select_indices(self, idxs):
 
        """set selection to these point indices"""
 
        """set selection to these point indices. This is the only
 
        writer to self.selected_points"""
 
        self.selected_points = idxs
 
        self.highlight_selected_dots()
 
        if self.selected_points and not self.selectManip:
 
            self.selectManip = SelectManip(
 
                self.root,
 
                getSelectedIndices=lambda: self.selected_points,
 
                getWorldPoint=lambda i: self.curve.points[i],
 
                getScreenPoint=lambda i: self.screen_from_world(self.curve.points[i]),
 
                getWorldTime=lambda x: self.world_from_screen(x, 0)[0],
 
                getCanvasSize=lambda: self.size,
 
                setPoints=self.setPoints,
 
                )
 
        if not self.selected_points and self.selectManip:
 
            self.selectManip.destroy()
 
            self.selectManip = None
 

	
 
        self.selectionChanged()
 

	
 
    def setPoints(self, updates):
 
        for i, pt in updates:
 
            self.curve.points[i] = pt
 
        self.update_curve()
 
        
 
    def selectionChanged(self):
 
        if self.selectManip:
 
            self.selectManip.update()
 

	
 
    def select_press(self,ev):
 
        # todo: these select_ handlers are getting called on c-a-drag
 
        # zooms too. the dispatching should be more selective than
 
        # just calling both handlers all the time
 
        self.print_state("select_press")
 
@@ -430,59 +532,28 @@ class Curveview(object):
 

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

	
 
        if self.size.height < 40:
 
            #self._draw_gradient()
 
            self._draw_line(visible_points, area=True)
 
        else:
 
            self._draw_markers(visible_x)
 
            self._draw_line(visible_points)
 

	
 
            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_gradient(self):
 
        # not yet ported
 
        t1 = time.time()
 
        gradient_res = 6 if self.is_music() else 3
 
        startX = startColor = None
 
        rects = 0
 
        for x in range(0, self.size.width, gradient_res):
 
            wx = self.world_from_screen(x,0)[0]
 
            mag = self.curve.eval(wx, allow_muting=False)
 
            if self.curve.muted:
 
                low = (8, 8, 8)
 
                high = (60, 60, 60)
 
            else:
 
                low = (20, 10, 50)
 
                high = (255, 187, 255)
 
            color = gradient(mag, low=low, high=high)
 
            if color != startColor:
 
                if startColor is not None:
 
                    self._draw_gradient_slice(startX, x, startColor)
 
                    rects += 1
 
                startX = x
 
                startColor = color
 

	
 
        if startColor is not None:
 
            self._draw_gradient_slice(startX, self.size.width, startColor)
 
            rects += 1
 
        log.debug("redraw %s rects in %.02f ms", rects, 1000 * (time.time()-t1))
 

	
 
    def _draw_gradient_slice(self, x1, x2, color):
 
        self.create_rectangle(x1, 0, x2, 40,
 
                              fill=color, width=0, tags='curve')        
 

	
 
    def _draw_markers(self,visible_x):
 
        mark = self._draw_one_marker
 

	
 
        mark(0, "0")
 
        t1,t2=visible_x
 
@@ -624,29 +695,32 @@ class Curveview(object):
 
                newsel.append(sp)
 
            for ii in range(len(idxs)):
 
                if ii > i:
 
                    ii -= 1
 
                newidxs.append(idxs[ii])
 

	
 
            self.selected_points[:] = newsel
 
            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.selected_points=[dotidx]
 
        self.highlight_selected_dots()
 
            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
 
@@ -695,14 +769,13 @@ class Curveview(object):
 
            moved=1
 
            cp[idx] = tuple(newp)
 
        if moved:
 
            self.update_curve()
 

	
 
    def unselect(self):
 
        self.selected_points=[]
 
        self.highlight_selected_dots()
 
        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)
 
        
light9/curvecalc/zoomcontrol.py
Show inline comments
 
@@ -59,14 +59,14 @@ class ZoomControl(object):
 
        endtimes = dispatcher.send("get max time")
 
        if endtimes:
 
            self.maxtime = endtimes[0][1]
 
        else:
 
            self.maxtime = 0
 

	
 
        self.start=0
 
        self.end=20
 
        self.start = 0
 
        self.end = 250
 

	
 
        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')
readme
Show inline comments
 
@@ -25,12 +25,14 @@ argument to bin/curvecalc
 
--------------------------------
 

	
 
curvecalc upgrades:
 

	
 
draw handles for moving and resizing a fade with various anchor points
 

	
 
single click doesn't deselect
 

	
 
add vidref on mousemove. separate process? yes
 

	
 
sub autocomplete
 

	
 
fix fader UIs
 

	
0 comments (0 inline, 0 general)