Files @ 499ef7c133eb
Branch filter:

Location: light9/light9/uihelpers.py

drewp@bigasterisk.com
make rule for checkpoints
Ignore-this: aeafb51e41657120125f7d896217ce4
"""all the tiny tk helper functions"""

from __future__ import nested_scopes
#from Tkinter import Button
import logging, time
from rdflib import Literal
from Tix import Button, Toplevel, Tk, IntVar, Entry, DoubleVar
import Tkinter
from light9.namespaces import L9

log = logging.getLogger("toplevel")

windowlocations = {
    'sub' : '425x738+00+00',
    'console' : '168x24+848+000',
    'leveldisplay' : '144x340+870+400',
    'cuefader' : '314x212+546+741',
    'effect' : '24x24+0963+338',
    'stage' : '823x683+37+030',
    'scenes' : '504x198+462+12',
}

def bindkeys(root,key, func):
    root.bind(key, func)
    for w in root.winfo_children():
        w.bind(key, func)


def toplevel_savegeometry(tl,name):
    try:
        geo = tl.geometry()
        if not geo.startswith("1x1"):
            f=open(".light9-window-geometry-%s" % name.replace(' ','_'),'w')
            f.write(tl.geometry())
        # else the window never got mapped
    except Exception, e:
        # it's ok if there's no saved geometry
        pass

def toplevelat(name, existingtoplevel=None, graph=None, session=None):
    tl = existingtoplevel or Toplevel()
    tl.title(name)

    lastSaved = [None]
    setOnce = [False]
    graphSetTime = [0]
    def setPosFromGraphOnce():
        """
        the graph is probably initially empty, but as soon as it gives
        us one window position, we stop reading them
        """
        if setOnce[0]:
            return
        geo = graph.value(session, L9.windowGeometry)
        log.debug("setPosFromGraphOnce %s", geo)

        setOnce[0] = True
        graphSetTime[0] = time.time()
        if geo is not None and geo != lastSaved[0]:
            tl.geometry(geo)
            lastSaved[0] = geo

    def savePos(ev):
        geo = tl.geometry()
        if not isinstance(ev.widget, (Tk, Tkinter.Tk)):
            # I think these are due to internal widget size changes,
            # not the toplevel changing
            return
        # this is trying to not save all the startup automatic window
        # sizes. I don't have a better plan for this yet.
        if graphSetTime[0] == 0 or time.time() < graphSetTime[0] + 3:
            return
        if not setOnce[0]:
            return
        lastSaved[0] = geo
        log.debug("saving position %s", geo)
        graph.patchObject(session, session, L9.windowGeometry, Literal(geo))

    if graph is not None and session is not None:
        graph.addHandler(setPosFromGraphOnce)

    if name in windowlocations:
        tl.geometry(positionOnCurrentDesktop(windowlocations[name]))

    if graph is not None:
        tl._toplevelat_funcid = tl.bind("<Configure>",
                                        lambda ev,tl=tl,name=name: savePos(ev))

    return tl

def positionOnCurrentDesktop(xform, screenWidth=1920, screenHeight=1440):
    size, x, y = xform.split('+')
    x = int(x) % screenWidth
    y = int(y) % screenHeight
    return "%s+%s+%s" % (size, x, y)


def toggle_slider(s):
    if s.get() == 0:
        s.set(100)
    else:
        s.set(0)

# for lambda callbacks
def printout(t):
    print t

def printevent(ev):
    for k in dir(ev):
        if not k.startswith('__'):
            print k,getattr(ev,k)
    print ""

def eventtoparent(ev,sequence):
    "passes an event to the parent, screws up TixComboBoxes"

    wid_class = str(ev.widget.__class__)
    if wid_class == 'Tix.ComboBox' or wid_class == 'Tix.TixSubWidget':
        return

    evdict={}
    for x in ['state', 'time', 'y', 'x', 'serial']:
        evdict[x]=getattr(ev,x)
#    evdict['button']=ev.num
    par=ev.widget.winfo_parent()
    if par!=".":
        ev.widget.nametowidget(par).event_generate(sequence,**evdict)
    #else the event made it all the way to the top, unhandled

def colorlabel(label):
    """color a label based on its own text"""
    txt=label['text'] or "0"
    lev=float(txt)/100
    low=(80,80,180)
    high=(255,55,050)
    out = [int(l+lev*(h-l)) for h,l in zip(high,low)]
    col="#%02X%02X%02X" % tuple(out)
    label.config(bg=col)

# TODO: get everyone to use this
def colorfade(low, high, percent):
    '''not foolproof.  make sure 0 < percent < 1'''
    out = [int(l+percent*(h-l)) for h,l in zip(high,low)]
    col="#%02X%02X%02X" % tuple(out)
    return col

def colortotuple(anytkobj, colorname):
    'pass any tk object and a color name, like "yellow"'
    rgb = anytkobj.winfo_rgb(colorname)
    return [v / 256 for v in rgb]

class Togglebutton(Button):
    """works like a single radiobutton, but it's a button so the
    label's on the button face, not to the side. the optional command
    callback is called on button set, not on unset. takes a variable
    just like a checkbutton"""
    def __init__(self,parent,variable=None,command=None,downcolor='red',**kw):

        self.oldcommand = command
        Button.__init__(self,parent,command=self.invoke,**kw)

        self._origbkg = self.cget('bg')
        self.downcolor = downcolor

        self._variable = variable
        if self._variable:
            self._variable.trace('w',self._varchanged)
            self._setstate(self._variable.get())
        else:
            self._setstate(0)

        self.bind("<Return>",self.invoke)
        self.bind("<1>",self.invoke)
        self.bind("<space>",self.invoke)

    def _varchanged(self,*args):
        self._setstate(self._variable.get())

    def invoke(self,*ev):
        if self._variable:
            self._variable.set(not self.state)
        else:
            self._setstate(not self.state)

        if self.oldcommand and self.state: # call command only when state goes to 1
            self.oldcommand()
        return "break"

    def _setstate(self,newstate):
        self.state = newstate
        if newstate: # set
            self.config(bg=self.downcolor,relief='sunken')
        else: # unset
            self.config(bg=self._origbkg,relief='raised')
        return "break"


class FancyDoubleVar(DoubleVar):
    def __init__(self,master=None):
        DoubleVar.__init__(self,master)
        self.callbacklist = {} # cbname : mode
        self.namedtraces = {} # name : cbname
    def trace_variable(self,mode,callback):
        """Define a trace callback for the variable.

        MODE is one of "r", "w", "u" for read, write, undefine.
        CALLBACK must be a function which is called when
        the variable is read, written or undefined.

        Return the name of the callback.
        """
        cbname = self._master._register(callback)
        self._tk.call("trace", "variable", self._name, mode, cbname)

        # we build a list of the trace callbacks (the py functrions and the tcl functionnames)
        self.callbacklist[cbname] = mode
#        print "added trace:",callback,cbname

        return cbname
    trace=trace_variable
    def disable_traces(self):
        for cb,mode in self.callbacklist.items():
#            DoubleVar.trace_vdelete(self,v[0],k)
            self._tk.call("trace", "vdelete", self._name, mode,cb)
            # but no master delete!

    def recreate_traces(self):
        for cb,mode in self.callbacklist.items():
#            self.trace_variable(v[0],v[1])
            self._tk.call("trace", "variable", self._name, mode,cb)

    def trace_named(self, name, callback):
        if name in self.namedtraces:
            print "FancyDoubleVar: already had a trace named %s - replacing it" % name
            self.delete_named(name)

        cbname = self.trace_variable('w',callback) # this will register in self.callbacklist too

        self.namedtraces[name] = cbname
        return cbname

    def delete_named(self, name):
        if name in self.namedtraces:

            cbname = self.namedtraces[name]

            self.trace_vdelete('w',cbname)
	    #self._tk.call("trace","vdelete",self._name,'w',cbname)
            print "FancyDoubleVar: successfully deleted trace named %s" % name
        else:
            print "FancyDoubleVar: attempted to delete named %s which wasn't set to any function" % name

def get_selection(listbox):
    'Given a listbox, returns first selection as integer'
    selection = int(listbox.curselection()[0]) # blech
    return selection

if __name__=='__main__':
    root=Tk()
    root.tk_focusFollowsMouse()
    iv=IntVar()
    def cb():
        print "cb!"
    t = Togglebutton(root,text="testbutton",command=cb,variable=iv)
    t.pack()
    Entry(root,textvariable=iv).pack()
    root.mainloop()