Changeset - 990a9474d0e7
[Not reviewed]
default
0 2 0
dmcc - 21 years ago 2003-07-06 16:33:06

early cue stuff. the CueList will supply the CueFader with the cues to
early cue stuff. the CueList will supply the CueFader with the cues to
work with, and will do crossfading sooner or later. the format of cues
is still very open. cuelist1 is a bogus cuelist for testing.
2 files changed with 160 insertions and 701 deletions:
0 comments (0 inline, 0 general)
flax/CueFaders.py
Show inline comments
 
@@ -2,137 +2,53 @@ from __future__ import division, nested_
 
import Tix as Tk
 
import time
 
from TreeDict import TreeDict, allow_class_to_be_pickled
 
from TLUtility import enumerate
 
import Submaster, dmxclient
 

	
 
cue_state_indicator_colors = {
 
             # bg       fg
 
    'prev' : ('blue',   'white'),
 
    'cur' :  ('yellow', 'black'),
 
    'next' : ('red',    'white'),
 
}
 

	
 
# TODO 
 
# FIXE pause fades, set new time to be remaining about of time in the fade so 
 
#           fade can continue properly
 
# FIXE make fades work properly: the set_next / prev bug
 
# WONT find cue by page ("not necessawy!")
 
# WONT CueFader controls KeyboardController?  unlikely
 
# FIXE AutoSave loop
 

	
 
class LabelledScale(Tk.Frame):
 
    """Scale with two labels: a name and current value"""
 
    def __init__(self, master, label, **opts):
 
        Tk.Frame.__init__(self, master, bd=2, relief='raised', bg='black')
 
        self.labelformatter = opts.get('labelformatter')
 
        try:
 
            del opts['labelformatter']
 
        except KeyError:
 
            pass
 

	
 
        Tk.Frame.__init__(self, master, bd=2, relief='raised')
 
        opts.setdefault('variable', Tk.DoubleVar())
 
        opts.setdefault('showvalue', 0)
 

	
 
        self.normaltrough = opts.get('troughcolor', 'black')
 
        self.flashtrough = opts.get('flashtroughcolor', 'red')
 
        try:
 
            del opts['flashtroughcolor']
 
        except KeyError:
 
            pass
 

	
 
        self.scale_var = opts['variable']
 
        self.scale = Tk.Scale(self, **opts)
 
        self.scale.pack(side='top', expand=1, fill='both')
 
        self.name = Tk.Label(self, text=label, bg='black', fg='white')
 
        self.name = Tk.Label(self, text=label)
 
        self.name.pack(side='bottom')
 
        self.scale_value = Tk.Label(self, bg='black', fg='white')
 
        self.scale_value = Tk.Label(self, width=6)
 
        self.scale_value.pack(side='bottom')
 
        self.scale_var.trace('w', self.update_value_label)
 
        self.update_value_label()
 
        self.disabled = (self.scale['state'] == 'disabled')
 
    def set_label(self, label):
 
        self.name['text'] = label
 
    def update_value_label(self, *args):
 
        val = self.scale_var.get() * 100
 
        if self.labelformatter:
 
            format = self.labelformatter(val)
 
        else:
 
            format = "%0.2f" % val
 
        self.scale_value['text'] = format
 
        if val != 0:
 
            self.scale['troughcolor'] = self.flashtrough
 
        else:
 
            self.scale['troughcolor'] = self.normaltrough
 
    def disable(self):
 
        if not self.disabled:
 
            self.scale['state'] = 'disabled'
 
            self.scale_var.set(0)
 
            self.disabled = 1
 
    def enable(self):
 
        if self.disabled:
 
            self.scale['state'] = 'normal'
 
            self.disabled = 0
 
        self.scale_value['text'] = "%0.2f" % val
 

	
 
class TimedGoButton(Tk.Frame):
 
    """Go button, fade time entry, and time fader"""
 
    def __init__(self, master, name, scale_to_fade, **kw):
 
        Tk.Frame.__init__(self, master, bg='black')
 
    def __init__(self, master, name, scale_to_fade):
 
        Tk.Frame.__init__(self, master)
 
        self.name = name
 
        self.scale_to_fade = scale_to_fade
 
        self.button = Tk.Button(self, text=name, command=self.start_fade, **kw)
 
        self.button = Tk.Button(self, text=name, command=self.start_fade)
 
        self.button.pack(fill='both', expand=1, side='left')
 

	
 
        self.timer_var = Tk.DoubleVar()
 
        self.timer_entry = Tk.Control(self, step=0.5, min=0, integer=0, 
 
            variable=self.timer_var, selectmode='immediate')
 
        for widget in (self.timer_entry, self.timer_entry.entry, 
 
            self.timer_entry.incr, self.timer_entry.decr, self.button, self):
 
            widget.bind("<4>", self.wheelscroll)
 
            widget.bind("<5>", self.wheelscroll)
 
        self.timer_entry.entry.configure(width=5, bg='black', fg='white')
 
        self.timer_var = Tk.StringVar()
 
        self.timer_entry = Tk.Entry(self, textvariable=self.timer_var, width=5)
 
        self.timer_entry.pack(fill='y', side='left')
 
        self.timer_var.set(2)
 
        self.disabled = (self.button['state'] == 'disabled')
 
        self.start_time = 0
 
        self.fading = 0
 
        self.last_after_key = 0
 
    def manual_override(self, *args):
 
        self.end_fade()
 
    def wheelscroll(self, event):
 
        """Mouse wheel increments or decrements timer."""
 
        if event.num == 4: # scroll up
 
            self.timer_entry.increment()
 
        else:            # scroll down
 
            self.timer_entry.decrement()
 
        self.timer_var.set("2")
 
    def start_fade(self, end_level=1):
 
        self.last_start_time = self.start_time
 
        try:
 
            fade_time = float(self.timer_var.get())
 
        except ValueError:
 
            # TODO figure out how to handle this
 
            print "can't fade -- bad time"
 
            return
 

	
 
        self.start_time = time.time()
 
        self.start_level = self.scale_to_fade.scale_var.get()
 
        self.end_level = end_level
 

	
 
        if self.fading == 1: # if we're already fading
 
            self.fading = 'paused'
 
            # new fade should be as long as however much was left
 
            self.fade_length = self.fade_length - \
 
                (time.time() - self.last_start_time)
 
            self.button['text'] = 'Unpause'
 
            self.after_cancel(self.last_after_key)
 
        else:
 
            try:
 
                fade_time = float(self.timer_var.get())
 
            except ValueError:
 
                # since we use a TixControl now, i don't think we need to worry 
 
                # about validation any more.
 
                print ">>> Can't fade -- bad time", self.timer_var.get()
 
                return
 

	
 
            # if we're not already fading, we get our time from the entry
 
            if self.fading != 'paused':
 
                self.fade_length = fade_time
 

	
 
            self.button['text'] = 'Pause'
 
            self.fading = 1
 
            self.do_fade()
 
        self.fade_length = fade_time
 
        self.do_fade()
 
    def do_fade(self):
 
        diff = time.time() - self.start_time
 
        if diff < self.fade_length:
 
@@ -142,277 +58,88 @@ class TimedGoButton(Tk.Frame):
 
            self.scale_to_fade.scale_var.set(newlevel)
 

	
 
            if newlevel != self.end_level:
 
                self.last_after_key = self.after(10, self.do_fade)
 
            else:
 
                self.end_fade()
 
                self.after(10, self.do_fade)
 
        else:
 
            self.scale_to_fade.scale_var.set(self.end_level)
 
            self.end_fade()
 
    def end_fade(self):
 
        self.button['text'] = self.name
 
        self.fading = 0
 
        self.after_cancel(self.last_after_key)
 
    def disable(self):
 
        if not self.disabled:
 
            self.button['state'] = 'disabled'
 
            self.disabled = 1
 
    def enable(self):
 
        if self.disabled:
 
            self.button['state'] = 'normal'
 
            self.disabled = 0
 
    def set_time(self, time):
 
        self.timer_var.set(time)
 
    def get_time(self):
 
        return self.timer_var.get()
 
    def is_fading(self):
 
        return self.fading
 

	
 
class CueFader(Tk.Frame):
 
    def __init__(self, master, cuelist):
 
        Tk.Frame.__init__(self, master, bg='black')
 
        Tk.Frame.__init__(self, master)
 
        self.cuelist = cuelist
 
        self.cuelist.set_fader(self)
 

	
 
        self.last_levels_sent = 0
 
        self.current_dmx_levels = [0] * 68
 
        self.after(0, self.send_dmx_levels_loop) # start DMX sending loop
 

	
 
        # this is a mechanism to stop Tk from autoshifting too much.
 
        # if this variable is true, the mouse button is down.  we don't want
 
        # to shift until they release it.  when it is released, we will
 
        # set it to false and then call autoshift.
 
        self.no_shifts_until_release = 0
 
        self.auto_shift = 0
 

	
 
        self.scales = {}
 
        self.shift_buttons = {}
 
        self.go_buttons = {}
 

	
 
        topframe = Tk.Frame(self)
 
        self.current_cues = Tk.Label(topframe)
 
        self.current_cues.pack()
 
        self.update_cue_display()
 
        topframe.pack()
 
        
 
        topframe = Tk.Frame(self, bg='black')
 

	
 
        self.set_prev_button = Tk.Button(topframe, text='Set Prev',
 
            command=lambda: cuelist.set_selection_as_prev(),
 
            fg='white', bg='blue')
 
        self.set_prev_button.pack(side='left')
 

	
 
        self.auto_shift = Tk.IntVar()
 
        self.auto_shift.set(1)
 

	
 
        self.auto_shift_checkbutton = Tk.Checkbutton(topframe, 
 
            variable=self.auto_shift, text='Autoshift', 
 
            command=self.toggle_autoshift, bg='black', fg='white',
 
            highlightbackground='black')
 
        self.auto_shift_checkbutton.pack(fill='both', side='left')
 

	
 
        self.auto_load_times = Tk.IntVar()
 
        self.auto_load_times.set(1)
 

	
 
        self.auto_load_times_checkbutton = Tk.Checkbutton(topframe, 
 
            variable=self.auto_load_times, text='Autoload Times', 
 
            bg='black', fg='white', 
 
            highlightbackground='black')
 
        self.auto_load_times_checkbutton.pack(fill='both', side='left')
 
        for name, start, end, side in (('Prev', 1, 0, 'left'),
 
                                       ('Next', 0, 1, 'right')):
 

	
 
        self.mute = Tk.IntVar()
 
        self.mute.set(0)
 

	
 
        self.mutebutton = Tk.Checkbutton(topframe, 
 
            variable=self.mute, text='Mute', 
 
            bg='black', fg='white', 
 
            highlightbackground='black',
 
            command=self.send_dmx_levels)
 
        self.mutebutton.pack(fill='both', side='left')
 

	
 
        self.set_next_button = Tk.Button(topframe, text='Set Next',
 
            command=lambda: cuelist.set_selection_as_next(),
 
            fg='white', bg='red')
 
        self.set_next_button.pack(side='left')
 

	
 
        topframe.pack(side='top')
 

	
 
        faderframe = Tk.Frame(self, bg='black')
 
        self.direction_info = (('Prev', 1, 0, 'left', 'blue'),
 
                               ('Next', 0, 1, 'right', 'red'))
 
        for name, start, end, side, color in self.direction_info:
 
            frame = Tk.Frame(self, bg='black')
 
            shift = Tk.Button(self, text="Shift %s" % name, state='disabled',
 
                command=lambda name=name: self.shift(name))
 
            shift.pack(side=side, fill='both', expand=1)
 
            
 
            frame = Tk.Frame(self)
 
            scale = LabelledScale(frame, name, from_=start, to_=end, 
 
                res=0.0001, orient='horiz', flashtroughcolor=color,
 
                labelformatter=lambda val, name=name: self.get_scale_desc(val, 
 
                                                                          name))
 
            scale.pack(fill='x', expand=0)
 
            go = TimedGoButton(frame, 'Go %s' % name, scale, bg=color, 
 
                fg='white', width=10)
 
                res=0.01, orient='horiz')
 
            scale.pack(fill='both', expand=1)
 
            go = TimedGoButton(frame, 'Go %s' % name, scale)
 
            go.pack(fill='both', expand=1)
 
            frame.pack(side=side, fill='both', expand=1)
 
        
 
            shift = Tk.Button(frame, text="Shift %s" % name, state='disabled',
 
                command=lambda name=name: self.shift(name), fg=color, 
 
                bg='black')
 

	
 
            self.scales[name] = scale
 
            self.shift_buttons[name] = shift
 
            self.go_buttons[name] = go
 

	
 
            scale.scale_var.trace('w', \
 
                lambda x, y, z, name=name, scale=scale: self.xfade(name, scale))
 
            go.timer_var.trace('w',
 
                lambda x, y, z, scale=scale: scale.update_value_label())
 

	
 
            def button_press(event, name=name, scale=scale):
 
                self.no_shifts_until_release = 1 # prevent shifts until release
 
            def button_release(event, name=name, scale=scale):
 
                self.no_shifts_until_release = 0
 
                self.autoshift(name, scale)
 

	
 
            scale.scale.bind("<ButtonPress>", button_press)
 
            scale.scale.bind("<ButtonRelease>", button_release)
 
        faderframe.pack(side='bottom', fill='both', expand=1)
 

	
 
        self.current_dir = 'Next'
 
        self.cues_as_subs = {}
 
        self.update_cue_cache()
 
    def reload_cue_times(self):
 
        prev, cur, next = self.cuelist.get_current_cues()
 
        self.go_buttons['Next'].set_time(next.time)
 
    def update_cue_cache(self, compute_dmx_levels=1):
 
        """Rebuilds subs from the current cues.  As this is expensive, we don't
 
        do it unless necessary (i.e. whenever we shift or a cue is edited)"""
 
        # print "update_cue_cache"
 
        # load the subs to fade between
 
        for cue, name in zip(self.cuelist.get_current_cues(), 
 
                             ('Prev', 'Cur', 'Next')):
 
            self.cues_as_subs[name] = cue.get_levels_as_sub()
 
        if compute_dmx_levels:
 
            self.compute_dmx_levels()
 
    def compute_dmx_levels(self):
 
        """Compute the DMX levels to send.  This should get called whenever the
 
        DMX levels could change: either during a crossfade or when a cue is
 
        edited.  Since this is called when we know that a change might occur,
 
        we will send the new levels too."""
 
        cur_sub = self.cues_as_subs.get('Cur')
 
        if cur_sub:
 
            scale = self.scales[self.current_dir]
 
            scale_val = scale.scale_var.get() 
 

	
 
            other_sub = self.cues_as_subs[self.current_dir]
 
            current_levels_as_sub = cur_sub.crossfade(other_sub, scale_val)
 
            self.current_dmx_levels = current_levels_as_sub.get_dmx_list()
 
            self.send_dmx_levels()
 

	
 
            # print "compute_dmx_levels: fade at", scale_val
 
            # print "between", cur_sub.name, 
 
            # print "and", other_sub.name
 
            # print 
 
    def send_dmx_levels(self, *args):
 
        # print "send_dmx_levels", self.current_dmx_levels
 
        if self.mute.get():
 
            dmxclient.outputlevels([0] * 68)
 
        else:
 
            dmxclient.outputlevels(self.current_dmx_levels)
 
        self.last_levels_sent = time.time()
 
    def send_dmx_levels_loop(self):
 
        diff = time.time() - self.last_levels_sent
 
        if diff >= 2: # too long since last send
 
            self.send_dmx_levels()
 
            self.after(200, self.send_dmx_levels_loop)
 
        else:
 
            self.after(int((2 - diff) * 100), self.send_dmx_levels_loop)
 
    def get_scale_desc(self, val, name):
 
        """Returns a description to the TimedGoButton"""
 
        go_button = self.go_buttons.get(name)
 
        if go_button:
 
            time = go_button.get_time()
 
            return "%0.2f%%, %0.1fs left" % (val, time - ((val / 100.0) * time))
 
        else:
 
            return "%0.2f%%" % val
 
    def toggle_autoshift(self):
 
        for name, button in self.shift_buttons.items():
 
            if not self.auto_shift.get():
 
                button.pack(side='bottom', fill='both', expand=1)
 
            else:
 
                button.pack_forget()
 
    def shift(self, name):
 
        # to prevent overshifting
 
        if self.no_shifts_until_release:
 
            return
 
        # print "shift", name
 

	
 
        for scale in self.scales.values():
 
            scale.scale_var.set(0)
 
            if name == 'Next': scale.update()
 
        print "shift", name
 
        self.cuelist.shift((-1, 1)[name == 'Next'])
 
        self.update_cue_cache(compute_dmx_levels=0)
 
        for scale_name, scale in self.scales.items():
 
            # print "shift: setting scale to 0", scale_name
 
            scale.scale.set(0)
 
            self.go_buttons[scale_name].manual_override()
 
        self.update_cue_cache(compute_dmx_levels=1)
 

	
 
        if self.auto_load_times.get():
 
            self.reload_cue_times()
 
    def autoshift(self, name, scale):
 
        self.update_cue_display()
 
    def update_cue_display(self):
 
        current_cues = [cue.name for cue in self.cuelist.get_current_cues()]
 
        self.current_cues['text'] = ', '.join(current_cues)
 
    def xfade(self, name, scale):
 
        scale_val = scale.scale_var.get() 
 

	
 
        if scale_val == 1:
 
            if self.auto_shift.get():
 
            if self.auto_shift:
 
                self.shift(name)
 
    def xfade(self, name, scale):
 
        if self.auto_shift.get():
 
            self.autoshift(name, scale)
 
            scale_val = scale.scale_var.get() 
 
            else:
 
                self.shift_buttons[name]['state'] = 'normal'
 
        else:
 
            scale_val = scale.scale_var.get() 
 
            if scale_val == 1:
 
                self.shift_buttons[name]['state'] = 'normal'
 
            else:
 
                # disable any dangerous shifting
 
                self.shift_buttons[name]['state'] = 'disabled'
 
            # disable any dangerous shifting
 
            self.shift_buttons[name]['state'] = 'disabled'
 

	
 
        d = self.opposite_direction(name)
 
        if scale_val != 0:
 
            # disable illegal three part crossfades
 
            self.scales[d].disable()
 
            self.go_buttons[d].disable()
 
            # TODO:
 
            # if name == 'Next':
 
            #   disable go_prev button and slider, lock slider at 0
 
            pass
 
        else:
 
            # undo above work
 
            self.scales[d].enable()
 
            self.go_buttons[d].enable()
 
            # undo above changes
 

	
 
        self.current_dir = name
 
        self.compute_dmx_levels()
 
    def opposite_direction(self, d):
 
        if d == 'Next':
 
            return 'Prev'
 
        else:
 
            return 'Next'
 
            # Actually, TimedGoButton and LabelledScale can have enable/disable
 
            # methods which will only do the Tk calls if necessary
 
            pass
 

	
 
class Cue:
 
    """A Cue has a name, a time, and any number of other attributes."""
 
    def __init__(self, name, time=3, sub_levels='', **attrs):
 
    def __init__(self, name, time=3, **attrs):
 
        self.name = name
 
        self.time = time
 
        self.sub_levels = sub_levels
 
        self.__dict__.update(attrs)
 
    def __repr__(self):
 
        return "<Cue %s, length %s>" % (self.name, self.time)
 
    def get_levels_as_sub(self):
 
        """Get this Cue as a combined Submaster, normalized.  This method
 
        should not be called constantly, since it is somewhat expensive.  It
 
        will reload the submasters from disk, combine all subs together, and
 
        then compute the normalized form."""
 
        subdict = {}
 
        for line in self.sub_levels.split(','):
 
            try:
 
                line = line.strip()
 
                if not line: 
 
                    continue
 
                sub, scale = line.split(':')
 
                sub = sub.strip()
 
                scale = float(scale)
 
                subdict[sub] = scale
 
            except ValueError:
 
                print "Parsing error for '%s' in %s" % (self.sub_levels, self)
 

	
 
        s = Submaster.Submasters()
 
        newsub = Submaster.sub_maxes(*[s[sub] * scale 
 
            for sub, scale in subdict.items()])
 
        return newsub.get_normalized_copy()
 

	
 
empty_cue = Cue('empty')
 

	
 
@@ -428,8 +155,8 @@ class CueList:
 
        except IOError:
 
            self.treedict.cues = []
 
        self.cues = self.treedict.cues
 
        self.current_cue_index = -1
 
        self.next_pointer = 0
 
        self.current_cue_index = 0
 
        self.next_pointer = None
 
        self.prev_pointer = None
 

	
 
        import atexit
 
@@ -460,255 +187,45 @@ class CueList:
 
    def set_prev(self, index):
 
        self.prev_pointer = index
 
    def bound_index(self, index):
 
        if not self.cues or index < 0:
 
        if not self.cues:
 
            return None
 
        else:
 
            return min(index, len(self.cues) - 1)
 
            return max(0, min(index, len(self.cues)))
 
    def get_current_cue_indices(self):
 
        """Returns a list of the indices of three cues: the previous cue,
 
        the current cue, and the next cue."""
 
        cur = self.current_cue_index
 
        return [self.bound_index(index) for index in
 
                    (self.prev_pointer or cur - 1, 
 
                     cur, 
 
                     self.next_pointer or cur + 1)]
 
    def get_current_cues(self):
 
        """Returns a list of three cues: the previous cue, the current cue,
 
        and the next cue."""
 
        # print "get_current_cue_indices", self.get_current_cue_indices()
 
        return [self.get_cue_by_index(index) 
 
            for index in self.get_current_cue_indices()]
 
    def get_cue_by_index(self, index):
 
        try:
 
        # print "get_cue_by_index", index
 
        if index:
 
            return self.cues[self.bound_index(index)]
 
        except TypeError:
 
        else:
 
            return empty_cue
 
    def __del__(self):
 
        self.save()
 
    def save(self, backup=0):
 
        if backup:
 

	
 
            backupfilename = "%s-backup" % self.filename
 
            print time.asctime(), "Saving backup version of cues to", \
 
                backupfilename
 
            self.treedict.save(backupfilename)
 
        else:
 
            print time.asctime(), "Saving cues to", self.filename
 
            self.treedict.save(self.filename)
 
    def reload(self):
 
        # TODO: we probably will need to make sure that indices still make
 
        # sense, etc.
 
        self.treedict.load(self.filename)
 

	
 
class TkCueList(CueList, Tk.Frame):
 
    def __init__(self, master, filename):
 
        CueList.__init__(self, filename)
 
        Tk.Frame.__init__(self, master, bg='black')
 
        self.fader = None
 
        
 
        self.edit_tl = Tk.Toplevel()
 
        self.editor = CueEditron(self.edit_tl, 
 
            changed_callback=self.cue_changed)
 
        self.editor.pack(fill='both', expand=1)
 

	
 
        def edit_cue(index):
 
            index = int(index)
 
            self.editor.set_cue_to_edit(self.cues[index])
 
            
 
        self.columns = ('name', 'time', 'page', 'cuenum', 'desc', 'sub_levels')
 
        self.scrolled_hlist = Tk.ScrolledHList(self,
 
            options='hlist.columns %d hlist.header 1' % len(self.columns))
 
        self.hlist = self.scrolled_hlist.hlist
 
        self.hlist.configure(fg='white', bg='black', 
 
            command=self.select_callback, browsecmd=edit_cue)
 
        self.hlist.bind("<4>", self.wheelscroll)
 
        self.hlist.bind("<5>", self.wheelscroll)
 
        self.scrolled_hlist.pack(fill='both', expand=1)
 

	
 
        boldfont = self.tk.call('tix', 'option', 'get', 
 
            'bold_font')
 
        header_style = Tk.DisplayStyle('text', refwindow=self,
 
            anchor='center', padx=8, pady=2, font=boldfont)
 

	
 
        for count, header in enumerate(self.columns):
 
            self.hlist.header_create(count, itemtype='text',
 
                text=header, style=header_style)
 

	
 
        self.cue_label_windows = {}
 
        for count, cue in enumerate(self.cues):
 
            self.display_cue(count, cue)
 
        self.update_cue_indicators()
 
        self.save_loop()
 
    def set_fader(self, fader):
 
        self.fader = fader
 
    def wheelscroll(self, evt):
 
        """Perform mouse wheel scrolling"""
 
        if evt.num == 4: # scroll down
 
            amount = -2
 
        else:            # scroll up
 
            amount = 2
 
        self.hlist.yview('scroll', amount, 'units')
 
    def cue_changed(self, cue):
 
        path = self.cues.index(cue)
 
        for col, header in enumerate(self.columns):
 
            try:
 
                text = getattr(cue, header)
 
            except AttributeError:
 
                text = ''
 

	
 
            if col == 0:
 
                self.cue_label_windows[path]['text'] = text
 
            else:
 
                self.hlist.item_configure(path, col, text=text)
 

	
 
        if cue in self.get_current_cues() and self.fader:
 
            self.fader.update_cue_cache()
 
            self.fader.reload_cue_times()
 
    def display_cue(self, path, cue):
 
        for col, header in enumerate(self.columns):
 
            try:
 
                text = getattr(cue, header)
 
            except AttributeError:
 
                text = ''
 

	
 
            if col == 0:
 
                lab = Tk.Label(self.hlist, text=text, fg='white', bg='black')
 
                def select_and_highlight(event):
 
                    self.select_callback(path)
 
                    self.hlist.selection_clear()
 
                    self.hlist.selection_set(path)
 

	
 
                lab.bind("<Double-1>", select_and_highlight)
 
                self.hlist.add(path, itemtype='window', window=lab)
 
                self.cue_label_windows[path] = lab
 
            else:
 
                self.hlist.item_create(path, col, text=text)
 
    def reset_cue_indicators(self, cue_indices=None):
 
        """If cue_indices is None, we'll reset all of them."""
 
        cue_indices = cue_indices or self.cue_label_windows.keys()
 
        for key in cue_indices:
 
            if key is None:
 
                continue
 
            window = self.cue_label_windows[key]
 
            window.configure(fg='white', bg='black')
 
    def update_cue_indicators(self):
 
        states = dict(zip(self.get_current_cue_indices(), 
 
                     ('prev', 'cur', 'next')))
 

	
 
        for count, state in states.items():
 
            if count is None:
 
                continue
 
            window = self.cue_label_windows[count]
 
            bg, fg = cue_state_indicator_colors[state]
 
            window.configure(bg=bg, fg=fg)
 
    def shift(self, diff):
 
        self.reset_cue_indicators(self.get_current_cue_indices())
 
        CueList.shift(self, diff)
 
        self.update_cue_indicators()
 
        # try to see all indices, but next takes priority over all, and cur
 
        # over prev
 
        for index in self.get_current_cue_indices():
 
            if index is not None:
 
                self.hlist.see(index)
 
    def select_callback(self, index):
 
        new_next = int(index)
 
        self.set_next(new_next)
 
    def set_next(self, index):
 
        prev, cur, next = self.get_current_cue_indices()
 
        self.reset_cue_indicators((next,))
 
        CueList.set_next(self, index)
 
        self.update_cue_indicators()
 

	
 
        if self.fader: # XXX this is untested
 
            self.fader.update_cue_cache()
 
    def set_prev(self, index):
 
        prev, cur, next = self.get_current_cue_indices()
 
        self.reset_cue_indicators((prev,))
 
        CueList.set_prev(self, index)
 
        self.update_cue_indicators()
 

	
 
        if self.fader: # XXX this is untested
 
            self.fader.update_cue_cache()
 
    def set_selection_as_prev(self):
 
        sel = self.hlist.info_selection()
 
        if sel:
 
            self.set_prev(int(sel[0]))
 
    def set_selection_as_next(self):
 
        sel = self.hlist.info_selection()
 
        if sel:
 
            self.set_next(int(sel[0]))
 
    def save_loop(self):
 
        """This saves the CueList every minute."""
 
        self.save(backup=1)
 
        self.after(60000, self.save_loop)
 

	
 
class CueEditron(Tk.Frame):
 
    def __init__(self, master, changed_callback=None, cue=None):
 
        Tk.Frame.__init__(self, master, bg='black')
 
        self.master = master
 
        self.cue = cue
 
        self.changed_callback = changed_callback
 
        self.enable_callbacks = 1
 

	
 
        self.setup_editing_forms()
 
        self.set_cue_to_edit(cue)
 
    def set_cue_to_edit(self, cue):
 
        if cue != self.cue:
 
            self.cue = cue
 
            self.fill_in_cue_info()
 
            self.set_title()
 
    def set_title(self):
 
            try:
 
                self.master.title("Editing '%s'" % self.cue.name)
 
            except AttributeError:
 
                pass
 
    def setup_editing_forms(self):
 
        self.variables = {}
 
        for row, field in enumerate(('name', 'time', 'page', 'cuenum', 'desc', 
 
            'sub_levels')):
 
            lab = Tk.Label(self, text=field, fg='white', bg='black')
 
            lab.grid(row=row, column=0, sticky='nsew')
 

	
 
            entryvar = Tk.StringVar()
 
            entry = Tk.Entry(self, fg='white', bg='black', 
 
                textvariable=entryvar, insertbackground='white',
 
                highlightcolor='red') # TODO this red/black is backwards
 
            entry.grid(row=row, column=1, sticky='nsew')
 

	
 
            self.variables[field] = entryvar
 

	
 
            def field_changed(x, y, z, field=field, entryvar=entryvar):
 
                if self.cue:
 
                    setattr(self.cue, field, entryvar.get())
 
                    if self.enable_callbacks and self.changed_callback:
 
                        self.changed_callback(self.cue)
 
                if field == 'name':
 
                    self.set_title()
 

	
 
            entryvar.trace('w', field_changed)
 
        self.columnconfigure(1, weight=1)
 
    def fill_in_cue_info(self):
 
        self.enable_callbacks = 0
 
        for row, field in enumerate(('name', 'time', 'page', 'cuenum', 'desc', 
 
            'sub_levels')):
 
            text = ''
 
            if self.cue:
 
                try:
 
                    text = getattr(self.cue, field)
 
                except AttributeError:
 
                    pass
 
            self.variables[field].set(text)
 
        self.enable_callbacks = 1
 
    def save(self):
 
        self.treedict.save(self.filename)
 

	
 
if __name__ == "__main__":
 
    root = Tk.Tk()
 
    root.title("ShowMaster 9000")
 
    root.geometry("600x670")
 
    cl = TkCueList(root, 'cues/dolly')
 
    cl.pack(fill='both', expand=1)
 
    if 0:
 
        z = CueList('cues/cuelist1')
 
        z.add_cue(Cue('cue %s' % time.asctime(), time=2, a=7, b=8))
 
        print 'cues', z.cues
 
    else:
 
        cl = CueList('cues/cuelist1')
 

	
 
    fader = CueFader(root, cl)
 
    fader.pack(fill='both', expand=1)
 
    try:
 
        # to populate cue list
 
        if 0:
 
            for x in range(20):
 
                cl.add_cue(Cue('cue %d' % x, time=x, some_attribute=3))
 

	
 
        root = Tk.Tk()
 
        fader = CueFader(root, cl)
 
        fader.pack(fill='both', expand=1)
 
        Tk.mainloop()
 
    except KeyboardInterrupt:
 
        root.destroy()
flax/cues/cuelist1
Show inline comments
 
<?xml version="1.0"?>
 
<!DOCTYPE PyObject SYSTEM "PyObjects.dtd">
 
<PyObject family="obj" type="builtin_wrapper"  class="_EmptyClass">
 
<attr name="__toplevel__" family="map" type="__compound__" extra="None TreeDict" id="138506468" >
 
<attr name="__toplevel__" family="map" type="__compound__" extra="None TreeDict" id="136600876" >
 
  <entry>
 
    <key type="string" value="cues" />
 
    <val type="list" id="139971548" >
 
      <item type="PyObject" id="140423764" class="Cue">
 
        <attr name="name" type="string" value="tevya special" />
 
        <attr name="desc" type="string" value="whoa - this works" />
 
        <attr name="page" type="string" value="3.2" />
 
        <attr name="sub_levels" type="string" value="green : 1.0" />
 
        <attr name="time" type="string" value="2" />
 
      </item>
 
      <item type="PyObject" id="139862804" class="Cue">
 
        <attr name="subdict" type="dict" id="139863044" >
 
        </attr>
 
        <attr name="name" type="string" value="lady luck" />
 
        <attr name="time" type="string" value="1" />
 
        <attr name="page" type="string" value="1.1.5" />
 
        <attr name="sub_levels" type="string" value="blue : 1.0, green : 0.5" />
 
        <attr name="desc" type="string" value="music flourish" />
 
    <val type="list" id="138285116" >
 
      <item type="PyObject" id="138619532" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 0" />
 
        <attr name="time" type="numeric" value="0" />
 
      </item>
 
      <item type="PyObject" id="139862732" class="Cue">
 
        <attr name="subdict" type="dict" id="139852060" >
 
        </attr>
 
        <attr name="name" type="string" value="dolly solo" />
 
        <attr name="time" type="string" value="2" />
 
        <attr name="page" type="string" value="1.2.10" />
 
        <attr name="sub_levels" type="string" value="blue : 0.1, green : 0.1" />
 
        <attr name="desc" type="string" value="tevya: &quot;what&apos;s happening to the tradition?&quot;" />
 
      <item type="PyObject" id="138231364" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 1" />
 
        <attr name="time" type="numeric" value="1" />
 
      </item>
 
      <item type="PyObject" id="139915236" class="Cue">
 
        <attr name="subdict" type="dict" id="139852964" >
 
        </attr>
 
      <item type="PyObject" id="138597492" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 2" />
 
        <attr name="time" type="numeric" value="2" />
 
      </item>
 
      <item type="PyObject" id="138228508" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 3" />
 
        <attr name="time" type="string" value="4" />
 
        <attr name="page" type="string" value="1.2.14" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="desc" type="string" value="the third cue" />
 
        <attr name="time" type="numeric" value="3" />
 
      </item>
 
      <item type="PyObject" id="139851228" class="Cue">
 
        <attr name="subdict" type="dict" id="139863188" >
 
        </attr>
 
        <attr name="name" type="string" value="heart" />
 
        <attr name="time" type="string" value="10" />
 
        <attr name="page" type="string" value="1.3.13" />
 
        <attr name="sub_levels" type="string" value="frontwhite : 0.2, red : 0.7" />
 
        <attr name="desc" type="string" value="&quot;you&apos;ve gotta have heart&quot;" />
 
      <item type="PyObject" id="138308908" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 4" />
 
        <attr name="time" type="numeric" value="4" />
 
      </item>
 
      <item type="PyObject" id="139863332" class="Cue">
 
        <attr name="subdict" type="dict" id="139841476" >
 
        </attr>
 
        <attr name="name" type="string" value="more musical refs" />
 
        <attr name="time" type="string" value="5" />
 
        <attr name="page" type="string" value="1.3.17" />
 
        <attr name="sub_levels" type="string" value="blue : 1.0" />
 
        <attr name="desc" type="string" value="etc." />
 
      <item type="PyObject" id="138228692" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 5" />
 
        <attr name="time" type="numeric" value="5" />
 
      </item>
 
      <item type="PyObject" id="139855188" class="Cue">
 
        <attr name="subdict" type="dict" id="139855428" >
 
        </attr>
 
        <attr name="name" type="string" value="rainbow shimmer" />
 
        <attr name="time" type="string" value="6" />
 
        <attr name="page" type="string" value="1.4.2" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="desc" type="string" value="curtain close" />
 
      <item type="PyObject" id="138295972" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 6" />
 
        <attr name="time" type="numeric" value="6" />
 
      </item>
 
      <item type="PyObject" id="139873788" class="Cue">
 
        <attr name="subdict" type="dict" id="139855956" >
 
        </attr>
 
        <attr name="name" type="string" value="fade up" />
 
        <attr name="time" type="string" value="7" />
 
        <attr name="page" type="string" value="2.1.1" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="desc" type="string" value="stage manager: &quot;worklights, please&quot;" />
 
      <item type="PyObject" id="138255924" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 7" />
 
        <attr name="time" type="numeric" value="7" />
 
      </item>
 
      <item type="PyObject" id="136392732" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 8" />
 
        <attr name="time" type="numeric" value="8" />
 
      </item>
 
      <item type="PyObject" id="139927324" class="Cue">
 
        <attr name="subdict" type="dict" id="139854356" >
 
        </attr>
 
        <attr name="name" type="string" value="blackout" />
 
        <attr name="time" type="string" value="8" />
 
        <attr name="page" type="string" value="2.1.2" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="desc" type="string" value="&quot;lights out!&quot;" />
 
      <item type="PyObject" id="136982068" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 9" />
 
        <attr name="time" type="numeric" value="9" />
 
      </item>
 
      <item type="PyObject" id="139864660" class="Cue">
 
        <attr name="subdict" type="dict" id="139857340" >
 
        </attr>
 
        <attr name="name" type="string" value="sill" />
 
        <attr name="time" type="string" value="4.2" />
 
        <attr name="page" type="string" value="2.2.4" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="desc" type="string" value="another description" />
 
      <item type="PyObject" id="138256044" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 10" />
 
        <attr name="time" type="numeric" value="10" />
 
      </item>
 
      <item type="PyObject" id="139855916" class="Cue">
 
        <attr name="name" type="string" value="front only" />
 
        <attr name="desc" type="string" value="mr. cue 10" />
 
        <attr name="page" type="string" value="2.7.3" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="10" />
 
      </item>
 
      <item type="PyObject" id="139163156" class="Cue">
 
      <item type="PyObject" id="138232364" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 11" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="11" />
 
        <attr name="time" type="numeric" value="11" />
 
      </item>
 
      <item type="PyObject" id="139854140" class="Cue">
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
      <item type="PyObject" id="138232548" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 12" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="2.1" />
 
        <attr name="time" type="numeric" value="12" />
 
      </item>
 
      <item type="PyObject" id="139841764" class="Cue">
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
      <item type="PyObject" id="138232788" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 13" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="13" />
 
        <attr name="time" type="numeric" value="13" />
 
      </item>
 
      <item type="PyObject" id="139854308" class="Cue">
 
      <item type="PyObject" id="138296188" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 14" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="14" />
 
        <attr name="time" type="numeric" value="14" />
 
      </item>
 
      <item type="PyObject" id="139858580" class="Cue">
 
      <item type="PyObject" id="136857940" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 15" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="15" />
 
        <attr name="time" type="numeric" value="15" />
 
      </item>
 
      <item type="PyObject" id="139856100" class="Cue">
 
      <item type="PyObject" id="138232828" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 16" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="16" />
 
        <attr name="time" type="numeric" value="16" />
 
      </item>
 
      <item type="PyObject" id="139856180" class="Cue">
 
      <item type="PyObject" id="138232868" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 17" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="17" />
 
        <attr name="time" type="numeric" value="17" />
 
      </item>
 
      <item type="PyObject" id="139846156" class="Cue">
 
        <attr name="name" type="string" value="some name" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="18" />
 
      <item type="PyObject" id="138608260" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 18" />
 
        <attr name="time" type="numeric" value="18" />
 
      </item>
 
      <item type="PyObject" id="139841948" class="Cue">
 
      <item type="PyObject" id="138229964" class="Cue">
 
        <attr name="some_attribute" type="numeric" value="3" />
 
        <attr name="name" type="string" value="cue 19" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="19" />
 
        <attr name="time" type="numeric" value="19" />
 
      </item>
 
    </val>
 
  </entry>
0 comments (0 inline, 0 general)