Changeset - 6e9b5ed1e863
[Not reviewed]
default
0 2 0
Drew Perttula - 13 years ago 2012-06-09 10:28:06
drewp@bigasterisk.com
finesse ui. add some mouse events on the points in a curve
Ignore-this: 69ed00763985926b92e776fc14298b45
2 files changed with 23 insertions and 15 deletions:
0 comments (0 inline, 0 general)
bin/curvecalc
Show inline comments
 
@@ -48,65 +48,67 @@ def makeGraph():
 
class Main(object):
 
    def __init__(self, graph, opts, song, curveset, subterms):
 
        self.graph = graph
 
        wtree = gtk.Builder()
 
        wtree.add_from_file(sibpath(__file__, "../light9/curvecalc/curvecalc.glade"))
 
        mainwin = wtree.get_object("MainWindow")
 
        mainwin.connect("destroy", gtk.main_quit)
 
        wtree.connect_signals(self)
 
        mainwin.show_all()
 

	
 
        mainwin.connect("delete-event", lambda *args: reactor.crash())
 
        mainwin.set_title("curvecalc - %s" % graph.label(song))
 
        
 
        self.add_subterms_for_song(song, curveset, subterms,
 
                                   wtree.get_object("subterms")
 
                                   )
 

	
 
        def refreshCurveView():
 
            m = os.path.getmtime(curveview.__file__.replace('.pyc', '.py'))
 

	
 
            if not hasattr(self, 'curvesetView') or self.curvesetView._mtime != m:
 
                print "reload curveview.py"
 
                curvesVBox = wtree.get_object("curves")
 
                [curvesVBox.remove(c) for c in curvesVBox.get_children()]
 
                try:
 
                reload(curveview)
 
                # mem problem somewhere; need to hold a ref to this
 
                self.curvesetView = curveview.Curvesetview(
 
                    curvesVBox, curveset)
 
                self.curvesetView._mtime = m
 

	
 
                # curvesetview must already exist, since this makes 'add_curve'
 
                # signals for all the initial curves
 
                curveset.load(basename=os.path.join(
 
                    showconfig.curvesDir(),
 
                    showconfig.songFilenameFromURI(song)),
 
                              skipMusic=opts.skip_music)
 
                # this is scheduled after some tk shuffling, to try to minimize
 
                # the number of times we redraw the curve at startup. If tk is
 
                # very slow, it's ok. You'll just get some wasted redraws.
 
                self.curvesetView.goLive()
 
                
 
                except Exception, e:
 
                    print "reload failed:", e
 
            reactor.callLater(1, refreshCurveView)
 
        refreshCurveView()       
 
        
 
        self.makeStatusLines(wtree.get_object("status"))
 

	
 
        #zc = Zoomcontrol(root)
 

	
 
    def onSave(self, *args):
 
        savekey(song, subterms, curveset)
 

	
 
    def add_subterms_for_song(self, song, curveset, subterms, master):
 
        for st in self.graph.objects(song, L9['subterm']):
 
            log.info("song %s has subterm %s", song, st)
 
            add_one_subterm(self.graph,
 
                            self.graph.value(st, L9['sub']),
 
                            curveset,
 
                            subterms,
 
                            master,
 
                            self.graph.value(st, L9['expression']))
 
        master.show_all()
 

	
 
    def makeStatusLines(self, master):
 
        """various labels that listen for dispatcher signals"""
 
        for row, (signame, textfilter) in enumerate([
light9/curvecalc/curveview.py
Show inline comments
 
@@ -55,71 +55,75 @@ class Sketch:
 
                to_remove.append(i)
 

	
 
        for i in to_remove:
 
            self.curveview.curve.points.remove(pts[i])
 

	
 
        # the simplified curve may now be too far away from some of
 
        # the points, so we'll put them back. this has an unfortunate
 
        # bias toward reinserting the earlier points
 
        for i in to_remove:
 
            p = pts[i]
 
            if abs(self.curveview.curve(p[0]) - p[1]) > .1:
 
                self.curveview.add_point(p)
 
            
 
        self.curveview.update_curve()
 

	
 
class Curveview(object):
 
    """
 
    graphical curve widget only. Please pack .widget
 
    """
 
    def __init__(self, curve, knobEnabled=False, isMusic=False, **kw):
 
        """knobEnabled=True highlights the previous key and ties it to a
 
        hardware knob"""
 
        self.widget = goocanvas.Canvas()
 
        self.widget.set_property("background-color", "gray20")
 
        self.widget.set_size_request(-1, 130)
 
        self.widget.set_size_request(-1, 100)
 
        self.root = self.widget.get_root_item()
 

	
 
        self.redrawsEnabled = False
 
        self.curve = curve
 
        self.knobEnabled = knobEnabled
 
        self._isMusic = isMusic
 
        self._time = 0
 
        self.last_mouse_world = None
 
        self.selected_points=[] # idx of points being dragged
 
        # self.bind("<Enter>",self.focus)
 
        dispatcher.connect(self.input_time, "input time")
 
        dispatcher.connect(self.update_curve, "zoom changed")
 
        dispatcher.connect(self.update_curve, "points changed",
 
                           sender=self.curve)
 
        dispatcher.connect(self.update_curve, "mute changed", 
 
                           sender=self.curve)
 
        dispatcher.connect(self.select_between, "select between")
 
        if self.knobEnabled:
 
            dispatcher.connect(self.knob_in, "knob in")
 
            dispatcher.connect(self.slider_in, "set key")
 

	
 
        self.widget.connect("size-allocate", self.update_curve)
 

	
 
        self.root.connect("motion-notify-event", self.dotmotion)
 
        self.root.connect("button-release-event", self.dotrelease)
 
        
 
        if 0:
 

	
 
            for x in range(1, 6):
 
                def add_kb_marker_point(evt, x=x):
 
                    self.add_point((self.current_time(), (x - 1) / 4.0))
 

	
 
                self.bind("<Key-%s>" % 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("<ButtonPress-%s>" % butnum, onMouseWheel)
 
            self.bind("<Key-Escape>", lambda ev:
 
                      dispatcher.send("see time",
 
                                      t=self.current_time()))
 
            self.bind("<Shift-Escape>", lambda ev:
 
                      dispatcher.send("see time until end",
 
@@ -171,49 +175,49 @@ class Curveview(object):
 
    def knob_in(self, curve, value):
 
        """user turned a hardware knob, which edits the point to the
 
        left of the current time"""
 
        if curve != self.curve:
 
            return
 
        idx = self.curve.index_before(self.current_time())
 
        if idx is not None:
 
            pos = self.curve.points[idx]
 
            self.curve.points[idx] = (pos[0], value)
 
            self.update_curve()
 

	
 
    def slider_in(self, curve, value=None):
 
        """user pushed on a slider. make a new key.  if value is None,
 
        the value will be the same as the last."""
 
        if curve != self.curve:
 
            return
 

	
 
        if value is None:
 
            value = self.curve.eval(self.current_time())
 

	
 
        self.curve.insert_pt((self.current_time(), value))
 
        self.update_curve()
 

	
 
    def print_state(self, msg=""):
 
        if 0:
 
        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_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")
 
        if self.dragging_dots:
 
            return
 
        if not self.selecting:
 
            self.selecting = True
 
            self.select_start = self.world_from_screen(ev.x,0)[0]
 
            cursors.push(self,"gumby")
 
        
 
    def select_motion(self,ev):
 
        if not self.selecting:
 
@@ -268,49 +272,49 @@ class Curveview(object):
 
        #    return
 
        return
 
        t=val
 
        pts = self.screen_from_world((val,0))+self.screen_from_world((val,1))
 
        self.delete('timecursor')
 
        self.create_line(*pts,**dict(width=2,fill='red',tags=('timecursor',)))
 
        self.have_time_line = True
 
        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 update_curve(self, _widget=None, _rect=None):
 
        if not self.redrawsEnabled:
 
            return
 
        self.size = self.widget.get_allocation()
 

	
 
        self.zoom = 0, 3#dispatcher.send("zoom area")[0][1]
 
        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])
 

	
 
        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)
 

	
 
        if 0:
 
            if self.curve.muted:
 
                self['bg'] = 'grey20'
 
            else:
 
                self['bg'] = 'black'
 

	
 
        if self.size.height < .40:
 
            self._draw_gradient()
 
        else:
 
            self._draw_markers(visible_x)
 
            self._draw_line(visible_points)
 
@@ -362,111 +366,113 @@ class Curveview(object):
 
        mark = self._draw_one_marker
 

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

	
 
        endtimes = dispatcher.send("get max time")
 
        if endtimes:
 
            endtime = endtimes[0][1]
 
            mark(endtime, "end %.1f"%endtime)
 
            mark(endtime - postPad, "post %.1f" % (endtime - postPad))
 
        
 
    def _draw_one_marker(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,
 
                                    stroke_color='white')
 
                                    line_width=.5,
 
                                    stroke_color='gray70')
 
        goocanvas.Text(parent=self.curveGroup,
 
                       fill_color="white",
 
                       x=x, y=ht-20,
 
                       font="ubuntu 9",
 
                       x=x+3, y=ht-20,
 
                       text=label)
 

	
 
    def _draw_line(self,visible_points):
 
        linepts=[]
 
        step=1
 
        linewidth=2
 
        linewidth = 3
 
        # 800? maybe this should be related to self.width
 
        if len(visible_points)>800:
 
            step = int(len(visible_points)/800)
 
            linewidth=1
 
            linewidth = .5
 
        for p in visible_points[::step]:
 
            linepts.append(self.screen_from_world(p))
 
        if len(linepts)<4:
 
            return
 

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

	
 
        self.pl = goocanvas.Polyline(parent=self.curveGroup,
 
                                     points=goocanvas.Points(linepts),
 
                                     width=linewidth,
 
                                     line_width=linewidth,
 
                                     stroke_color=fill,
 
                                     )
 
            
 
        # canvas doesnt have keyboard focus, so i can't easily change the
 
        # cursor when ctrl is pressed
 
        #        def curs(ev):
 
        #            print ev.state
 
        #        self.bind("<KeyPress>",curs)
 
        #        self.bind("<KeyRelease-Control_L>",lambda ev: curs(0))
 
        if 0:
 
            self.tag_bind(line,"<Control-ButtonPress-1>",self.new_point_at_mouse)
 

	
 

	
 
    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=p[0] - rad, y=p[1] - rad,
 
                                 width=rad * 2, height=rad * 2,
 
                                 stroke_color='gray20',
 
                                 stroke_color='gray90',
 
                                 fill_color='blue',
 
                                 line_width=1,
 
                                 #tags=('curve','point', 'handle%d' % i)
 
                                 )
 
            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)
 
                                         )
 
            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
 

	
 
        def delpoint(ev):
 
            # had a hard time tag_binding to the points, so i trap at
 
            # the widget level (which might be nice anyway when there
 
            # are multiple pts selected)
 
            if self.selected_points:
 
                self.remove_point_idx(*self.selected_points)
 
        #self.bind("<Key-Delete>", delpoint)
 

	
 
        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")
 
@@ -496,98 +502,98 @@ class Curveview(object):
 
                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.selected_points[:] = newsel
 
            idxs[:] = newidxs
 
            
 
        self.update_curve()
 

	
 
    def highlight_selected_dots(self):
 
        return
 
        for i,d in self.dots.items():
 
            if i in self.selected_points:
 
                self.itemconfigure(d,fill='red')
 
            else:
 
                self.itemconfigure(d,fill='blue')
 
        
 
    def dotpress(self,ev,dotidx):
 
    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.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.selected_points = self.curve.indices_between(start,end)
 
        self.highlight_selected_dots()
 

	
 
    def dotmotion(self,ev):
 
    def dotmotion(self, group, hitObject, ev):
 
        if not self.dragging_dots:
 
            return
 
        if not ev.state & 256:
 
            return # not lmb-down
 
        cp = self.curve.points
 
        moved=0
 

	
 
        cur = self.world_from_screen(ev.x, ev.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.selected_points=[]
 
        self.highlight_selected_dots()
 
        
 
    def dotrelease(self,ev):
 
    def dotrelease(self, group, hitObject, ev):
 
        self.print_state("dotrelease")
 
        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)
 

	
 
    please pack self.box
 
    """
 
    def __init__(self, name, curve, slider, knobEnabled):
 

	
 
        self.box = gtk.HandleBox()
 
        self.box.set_border_width(1)
 

	
 
        cols = gtk.HBox()
 
        self.box.add(cols)
 
        
 
        controls = gtk.Frame()
 
        controls.set_size_request(115, -1)
 
        controls.set_shadow_type(gtk.SHADOW_OUT)
 
        cols.pack_start(controls, expand=False)
0 comments (0 inline, 0 general)