Changeset - 348b68723238
[Not reviewed]
default
0 3 0
Drew Perttula - 13 years ago 2012-06-12 09:46:04
drewp@bigasterisk.com
collapse mode. reload() hacks. rebuild key
Ignore-this: 641f70b7b09a7e58e3bed66199ee8295
3 files changed with 128 insertions and 43 deletions:
0 comments (0 inline, 0 general)
bin/curvecalc
Show inline comments
 
@@ -6,25 +6,25 @@ now launches like this:
 

	
 

	
 

	
 
todo: curveview should preserve more objects, for speed maybe
 

	
 
"""
 
from __future__ import division
 

	
 
from twisted.internet import gtk2reactor
 
gtk2reactor.install()
 
from twisted.internet import reactor
 

	
 
import time, textwrap, os, optparse, urllib2, gtk, gobject, linecache, signal
 
import time, textwrap, os, optparse, urllib2, gtk, gobject, linecache, signal, traceback
 
import louie as dispatcher 
 
from twisted.python.util import sibpath
 
from rdflib import URIRef, Graph, Literal, RDF, RDFS
 
import logging
 
log = logging.getLogger()
 

	
 
import run_local
 
from light9 import showconfig, prof
 
from light9.curvecalc.curve import Curveset
 
from light9.curvecalc import curveview 
 
from light9.curvecalc.musicaccess import Music, currentlyPlayingSong
 
from light9.wavelength import wavelength
 
@@ -189,36 +189,42 @@ class Main(object):
 

	
 
        if (not hasattr(self, 'curvesetView') or
 
            self.curvesetView._mtimes != mtimes):
 
            print "reload curveview.py"
 
            curvesVBox = wtree.get_object("curves")
 
            zoomControlBox = wtree.get_object("zoomControlBox")
 
            [curvesVBox.remove(c) for c in curvesVBox.get_children()]
 
            [zoomControlBox.remove(c) for c in
 
             zoomControlBox.get_children()]
 
            try:
 
                linecache.clearcache()
 
                reload(curveview)
 

	
 
                # old ones are not getting deleted right
 
                if hasattr(self, 'curvesetView'):
 
                    self.curvesetView.live = False
 

	
 
                # mem problem somewhere; need to hold a ref to this
 
                self.curvesetView = curveview.Curvesetview(
 
                    curvesVBox, zoomControlBox, self.curveset)
 
                self.curvesetView._mtimes = mtimes
 

	
 
                # 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
 
                print "reload failed:"
 
                traceback.print_exc()
 
        if self.opts.reload:
 
            reactor.callLater(1, self.refreshCurveView)
 

	
 
    def onReloadSubs(self, *args): # wants to be ctrl-r  too
 
        dispatcher.send('reload all subs')
 

	
 

	
 
def main():
 
    startTime = time.time()
 
    parser = optparse.OptionParser()
 
    parser.set_usage("%prog [opts] [songURI]")
 
    parser.add_option("--sliders", action='store_true',
 
@@ -239,27 +245,24 @@ def main():
 
    log.debug("startup: music %s", time.time() - startTime)
 
    try:
 
        song = URIRef(args[0])
 
    except IndexError:
 
        song = currentlyPlayingSong()
 

	
 
    music = Music()
 
    graph = makeGraph()
 

	
 
    curveset = Curveset(sliders=opts.sliders)
 
    subterms = []
 

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

	
 
    subtermPath = graphPathForSubterms(song)
 
    try:
 
        graph.parse(subtermPath, format='n3')
 
    except urllib2.URLError, e:
 
        if e.reason.errno != 2:
 
            raise
 
        log.info("%s not found, starting with empty graph" % subtermPath)
light9/curvecalc/curvecalc.glade
Show inline comments
 
@@ -681,24 +681,25 @@
 
        </child>
 
      </object>
 
    </child>
 
  </object>
 
  <object class="GtkAccelGroup" id="accelgroup1"/>
 
  <object class="GtkAdjustment" id="adjustment1">
 
    <property name="upper">100</property>
 
    <property name="step_increment">1</property>
 
    <property name="page_increment">10</property>
 
  </object>
 
  <object class="GtkTextBuffer" id="help">
 
    <property name="text">Mousewheel zoom; C-p play/pause music at mouse
 
Over a curve: C to collapse; R to rebuild canvas widget
 
Curve point bindings: B1 drag point; C-B1 curve add point; S-B1 sketch points; 1..5 add point at time; B1 drag select points
 
Available in functions: nsin/ncos period=amp=1; within(a,b) bef(x) aft(x) compare to time; smoove(x) cubic smoothstep; chan(name); curvename(t) eval curve</property>
 
  </object>
 
  <object class="GtkImage" id="image2">
 
    <property name="visible">True</property>
 
    <property name="can_focus">False</property>
 
    <property name="stock">gtk-refresh</property>
 
  </object>
 
  <object class="GtkListStore" id="liststore1"/>
 
  <object class="GtkDialog" id="newCurve">
 
    <property name="can_focus">False</property>
 
    <property name="border_width">5</property>
light9/curvecalc/curveview.py
Show inline comments
 
@@ -70,59 +70,50 @@ class Sketch:
 
            
 
        self.curveview.update_curve()
 
        self.curveview.select_points(finalPoints)
 

	
 
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.widget = goocanvas.Canvas()
 
        self.widget.set_property("background-color", "black")
 
        self.size = self.widget.get_allocation()
 
        self.root = self.widget.get_root_item()
 

	
 
        self.rebuild()
 

	
 
        self.redrawsEnabled = False
 
        self.curve = curve
 
        self.knobEnabled = knobEnabled
 
        self._isMusic = isMusic
 
        self.zoomControl = zoomControl
 
        self._time = 0
 
        self._time = -999
 
        self.last_mouse_world = None
 
        self.culled = False # have we been putting off updates?
 
        self.entered = False # is the mouse currently over this widget
 
        self.selected_points=[] # idx of points being dragged
 
        # self.bind("<Enter>",self.focus)
 
        dispatcher.connect(self.playPause, "onPlayPause")
 
        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.widget.connect("leave-notify-event", self.onLeave)
 
        self.widget.connect("enter-notify-event", self.onEnter)
 
        self.widget.connect("motion-notify-event", self.onMotion)
 
        self.widget.connect("scroll-event", self.onScroll)
 
        self.widget.connect("button-release-event", self.onRelease)
 
        self.root.connect("button-press-event", self.onCanvasPress)
 

	
 
        # todo: hold control to get a [+] cursor
 
        #        def curs(ev):
 
        #            print ev.state
 
        #        self.bind("<KeyPress>",curs)
 
        #        self.bind("<KeyRelease-Control_L>",lambda ev: curs(0))
 
        
 
        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))
 

	
 
@@ -140,24 +131,54 @@ class Curveview(object):
 
        
 
        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()
 
            print "rebuilding canvas"
 

	
 
        self.timelineLine = self.curveGroup = None
 
        self.widget = goocanvas.Canvas()
 
        self.widget.set_property("background-color", "black")
 
        self.size = self.widget.get_allocation()
 
        self.root = self.widget.get_root_item()
 

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

	
 
        self.widget.connect("leave-notify-event", self.onLeave)
 
        self.widget.connect("enter-notify-event", self.onEnter)
 
        self.widget.connect("motion-notify-event", self.onMotion)
 
        self.widget.connect("scroll-event", self.onScroll)
 
        self.widget.connect("button-release-event", self.onRelease)
 
        self.root.connect("button-press-event", self.onCanvasPress)
 

	
 
    def onExpose(self, *args):
 
        if self.culled:
 
            self.update_curve()
 

	
 
    def onDelete(self):
 
        if self.selected_points:
 
            self.remove_point_idx(*self.selected_points)
 
        
 
            
 
    def onCanvasPress(self, item, target_item, event):
 
        # 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
 
        if event.get_state() & gtk.gdk.CONTROL_MASK:
 
@@ -275,40 +296,44 @@ class Curveview(object):
 
    def sketch_motion(self,ev):
 
        if self.sketch:
 
            self.sketch.motion(ev)
 

	
 
    def sketch_release(self,ev):
 
        if self.sketch:
 
            self.sketch.release(ev)
 
            self.sketch = None
 

	
 
    def current_time(self):
 
        return self._time
 

	
 
    def screen_from_world(self,p):
 
        z = self.zoomControl
 
        ht = self.size.height
 
        return (p[0]-z.start)/(z.end-z.start)*self.size.width, (ht-5)-p[1]*(ht-10)
 

	
 
    def world_from_screen(self,x,y):
 
    def _coords(self):
 
        z = self.zoomControl
 
        ht = self.size.height
 
        return x/self.size.width*(z.end-z.start)+z.start, ((ht-5)-y)/(ht-10)
 
        marginBottom = 3 if ht > 40 else 0
 
        marginTop = marginBottom
 
        return z, ht, marginBottom, marginTop
 
        
 
    def screen_from_world(self,p):
 
        z, ht, marginBottom, marginTop = self._coords()
 
        return ((p[0] - z.start) / (z.end - z.start) * self.size.width,
 
                (ht - marginBottom) - p[1] * (ht - (marginBottom + marginTop)))
 

	
 
    def world_from_screen(self,x,y):
 
        z, ht, marginBottom, marginTop = self._coords()
 
        return (x / self.size.width * (z.end - z.start) + z.start,
 
                ((ht - marginBottom) - y) / (ht - (marginBottom + marginTop)))
 

	
 
    def input_time(self, val, forceUpdate=False):
 
        # i tried various things to make this not update like crazy,
 
        # but the timeline was always missing at startup, so i got
 
        # scared that things were getting built in a funny order.        
 
        #if self._time == val:
 
        #    return
 
        if self._time == val:
 
            return
 
        t=val
 

	
 
        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((val,0)),
 
            self.screen_from_world((val,1))]))
 
@@ -316,64 +341,88 @@ class Curveview(object):
 
        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):
 
    def canvasIsVisible(self):
 
        if not hasattr(self, "scrollWin"):
 
            self.scrollWin = self.widget
 
            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.widget.translate_coordinates(top, 0, 0)
 
        if not coords: # probably broken after a reload()
 
            return False
 
        cany1 = coords[1]
 
        cany2 = cany1 + self.widget.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.widget.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.widget.set_property("background-color",
 
        #                         "gray20" if self.curve.muted else "black")
 

	
 
        if self.size.height < .40:
 
            self._draw_gradient()
 
        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)
 

	
 
    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):
 
        print "no grad"
 
        return
 
        # 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)
 
@@ -420,47 +469,61 @@ class Curveview(object):
 
        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):
 
    def _draw_line(self, visible_points, area=False):
 
        linepts=[]
 
        step=1
 
        linewidth = 1.5
 
        maxPointsToDraw = self.size.width / 2
 
        maxPointsToDraw = self.size.width / 3
 
        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, y))
 
            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,
 
@@ -624,38 +687,46 @@ 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, slider, knobEnabled, zoomControl):
 
        self.name = name
 
        self.box = gtk.VBox()
 
        self.box.set_border_width(1)
 

	
 
        cols = gtk.HBox()
 
        self.box.add(cols)
 
        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)
 
        cols.pack_start(controls, expand=False)
 
        self.cols.pack_start(controls, expand=False)
 
        self.setupControls(controls, name, curve, slider)
 

	
 
        self.curveView = Curveview(curve, knobEnabled=knobEnabled,
 
                                   isMusic=name in ['music', 'smooth_music'],
 
                                   zoomControl=zoomControl)
 
        self.initCurveView()
 

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

	
 
    def initCurveView(self):
 
        self.curveView.widget.show()
 
        self.curveView.widget.set_size_request(-1, 100)
 
        cols.pack_start(self.curveView.widget, expand=True)       
 
        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()
 
@@ -713,55 +784,65 @@ class CurveRow(object):
 

	
 
    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"""
 
        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()
 
        if event.string == 'c':
 
            # calling toggled() had no effect; don't know why
 
            r.collapsed.set_active(not r.collapsed.get_active())
 
        if event.string == 'r':
 
            r = self.row_under_mouse()
 
            r.rebuild()
 

	
 
    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()
0 comments (0 inline, 0 general)