Files @ ed0db55f604c
Branch filter:

Location: light9/light9/FlyingFader.py

drewp@bigasterisk.com
use undefined for 'nothing selected' insetad of null
from tkinter import tix
from time import time


class Mass:

    def __init__(self):
        self.x = 0  # position
        self.xgoal = 0  # position goal

        self.v = 0  # velocity
        self.maxspeed = .8  # maximum speed, in position/second
        self.maxaccel = 3  # maximum acceleration, in position/second^2
        self.eps = .03  # epsilon - numbers within this much are considered the same

        self._lastupdate = time()
        self._stopped = 1

    def equal(self, a, b):
        return abs(a - b) < self.eps

    def stop(self):
        self.v = 0
        self.xgoal = self.x
        self._stopped = 1

    def update(self):
        t0 = self._lastupdate
        tnow = time()
        self._lastupdate = tnow

        dt = tnow - t0

        self.x += self.v * dt
        # hitting the ends stops the slider
        if self.x > 1:
            self.v = max(self.v, 0)
            self.x = 1
        if self.x < 0:
            self.v = min(self.v, 0)
            self.x = 0

        if self.equal(self.x, self.xgoal):
            self.x = self.xgoal  # clean up value
            self.stop()
            return

        self._stopped = 0
        dir = (-1.0, 1, 0)[self.xgoal > self.x]

        if abs(self.xgoal - self.x) < abs(self.v * 5 * dt):
            # apply the brakes on the last 5 steps
            dir *= -.5

        self.v += dir * self.maxaccel * dt  # velocity changes with acceleration in the right direction
        self.v = min(max(self.v, -self.maxspeed),
                     self.maxspeed)  # clamp velocity

        #print "x=%+.03f v=%+.03f a=%+.03f %f" % (self.x,self.v,self.maxaccel,self.xgoal)

    def goto(self, newx):
        self.xgoal = newx

    def ismoving(self):
        return not self._stopped


class FlyingFader(tix.Frame):

    def __init__(self,
                 master,
                 variable,
                 label,
                 fadedur=1.5,
                 font=('Arial', 8),
                 labelwidth=12,
                 **kw):
        tix.Frame.__init__(self, master)
        self.name = label
        self.variable = variable

        self.mass = Mass()

        self.config({'bd': 1, 'relief': 'raised'})
        scaleopts = {
            'variable': variable,
            'showvalue': 0,
            'from': 1.0,
            'to': 0,
            'res': 0.001,
            'width': 20,
            'length': 200,
            'orient': 'vert'
        }
        scaleopts.update(kw)
        if scaleopts['orient'] == 'vert':
            side2 = tix.BOTTOM
        else:
            side2 = tix.LEFT

        self.scale = tix.Scale(self, **scaleopts)
        self.vlabel = tix.Label(self, text="0.0", width=6, font=font)
        self.label = tix.Label(self,
                               text=label,
                               font=font,
                               anchor='w',
                               width=labelwidth)  #wraplength=40, )

        self.oldtrough = self.scale['troughcolor']

        self.scale.pack(side=side2, expand=1, fill=tix.BOTH, anchor='c')
        self.vlabel.pack(side=side2, expand=0, fill=tix.X)
        self.label.pack(side=side2, expand=0, fill=tix.X)

        for k in range(1, 10):
            self.scale.bind(
                "<Key-%d>" % k, lambda evt, k=k: self.newfade(k / 10.0, evt))

        self.scale.bind("<Key-0>", lambda evt: self.newfade(1.0, evt))
        self.scale.bind("<grave>", lambda evt: self.newfade(0, evt))

        self.scale.bind("<1>", self.cancelfade)
        self.scale.bind("<2>", self.cancelfade)
        self.scale.bind("<3>", self.mousefade)

        self.trace_ret = self.variable.trace('w', self.updatelabel)
        self.bind("<Destroy>", self.ondestroy)

    def ondestroy(self, *ev):
        self.variable.trace_vdelete('w', self.trace_ret)

    def cancelfade(self, evt):
        self.fadegoal = self.variable.get()
        self.fadevel = self.fadeacc = 0

        self.scale['troughcolor'] = self.oldtrough

    def mousefade(self, evt):
        target = float(self.tk.call(self.scale, 'get', evt.x, evt.y))
        self.newfade(target, evt)

    def ismoving(self):
        return self.fadevel != 0 or self.fadeacc != 0

    def newfade(self, newlevel, evt=None, length=None):

        # these are currently unused-- Mass needs to accept a speed input
        mult = 1
        if evt.state & 8 and evt.state & 4: mult = 0.25  # both
        elif evt.state & 8: mult = 0.5  # alt
        elif evt.state & 4: mult = 2  # control # noqa

        self.mass.x = self.variable.get()
        self.mass.goto(newlevel)

        self.gofade()

    def gofade(self):
        self.mass.update()
        self.variable.set(self.mass.x)

        if not self.mass.ismoving():
            self.scale['troughcolor'] = self.oldtrough
            return

        # blink the trough while the thing's moving
        if time() % .4 > .2:
            # self.scale.config(troughcolor=self.oldtrough)
            self.scale.config(troughcolor='orange')
        else:
            # self.scale.config(troughcolor='white')
            self.scale.config(troughcolor='yellow')

#        colorfade(self.scale, percent)
        self.after(30, self.gofade)

    def updatelabel(self, *args):
        if self.variable:
            self.vlabel['text'] = "%.3f" % self.variable.get()


#        if self.fadetimes[1] == 0: # no fade
#            self.vlabel['fg'] = 'black'
#        elif self.curfade[1] > self.curfade[0]:
#            self.vlabel['fg'] = 'red'
#        else:
#            self.vlabel['fg'] = 'blue'

    def get(self):
        return self.scale.get()

    def set(self, val):
        self.scale.set(val)


def colorfade(scale, lev):
    low = (255, 255, 255)
    high = (0, 0, 0)
    out = [int(l + lev * (h - l)) for h, l in zip(high, low)]
    col = "#%02X%02X%02X" % tuple(out)
    scale.config(troughcolor=col)


if __name__ == '__main__':
    root = tix.Tk()
    root.tk_focusFollowsMouse()

    FlyingFader(root, variable=tix.DoubleVar(),
                label="suck").pack(side=tix.LEFT, expand=1, fill=tix.BOTH)
    FlyingFader(root, variable=tix.DoubleVar(),
                label="moof").pack(side=tix.LEFT, expand=1, fill=tix.BOTH)
    FlyingFader(root, variable=tix.DoubleVar(),
                label="zarf").pack(side=tix.LEFT, expand=1, fill=tix.BOTH)
    FlyingFader(root,
                variable=tix.DoubleVar(),
                label="long name goes here.  got it?").pack(side=tix.LEFT,
                                                            expand=1,
                                                            fill=tix.BOTH)

    root.mainloop()