Changeset - f8b5cb5fbeed
[Not reviewed]
default
0 2 0
dmcc - 21 years ago 2003-07-09 03:59:40

- CueFader is hopefully done:
- CueFader is hopefully done:
- The TimedGoButton accepts a wheel to change the times. You can also
enter times directly.
- TimedGoButton really has a default starting time of 2 now. (there was
a variable attached to the wrong widget before)
- We send DMX levels with dmxclient now.
- Autoload Times is a new option.
- We load times from the next cue if Autoload Times is true.
- Time predictions in the LabelledScale are slightly better. You still
can change the time of an active fade.
- Cue cache and DMX level computing now have their own functions, which
get called at (hopefully) All The Right Times.
- There are even some docs now!
- Cues: sub_level parsing is better, will only throw out one line if
it encounters problems (instead of the rest of the cue)
- CueList: lots of 0 vs. None bugs fixed.
- TkCueList: stores a reference to the controlling fader so it can alert
it about changed cues.
- CueEditron: You can edit sub_levels now.
- cuelist1 was edited, checking it in for consistency's sake
2 files changed with 167 insertions and 82 deletions:
0 comments (0 inline, 0 general)
flax/CueFaders.py
Show inline comments
 
from __future__ import division, nested_scopes
 
import Tix as Tk
 
import time
 
from TreeDict import TreeDict, allow_class_to_be_pickled
 
from TLUtility import enumerate
 
import Submaster
 
import Submaster, dmxclient
 

	
 
cue_state_indicator_colors = {
 
             # bg       fg
 
    'prev' : ('blue',   'white'),
 
    'cur' :  ('yellow', 'black'),
 
    'next' : ('red',    'white'),
 
@@ -72,25 +72,36 @@ class TimedGoButton(Tk.Frame):
 
        self.name = name
 
        self.scale_to_fade = scale_to_fade
 
        self.button = Tk.Button(self, text=name, command=self.start_fade, **kw)
 
        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)
 
        self.timer_entry.entry.configure(textvariable=self.timer_var, width=5, 
 
            bg='black', fg='white')
 
        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_entry.pack(fill='y', side='left')
 
        self.timer_var.set(2)
 
        self.disabled = (self.button['state'] == 'disabled')
 
        self.fading = 0
 
    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()
 
    def start_fade(self, end_level=1):
 
        try:
 
            fade_time = float(self.timer_var.get())
 
        except ValueError:
 
            # since we use a control now, i don't think we need to worry about
 
            # validation any more.
 
            print "can't fade -- bad time"
 
            print ">>> Can't fade -- bad time", self.timer_var.get()
 
            return
 

	
 
        self.start_time = time.time()
 
        self.start_level = self.scale_to_fade.scale_var.get()
 
        self.end_level = end_level
 
        self.fade_length = fade_time
 
@@ -127,14 +138,17 @@ class TimedGoButton(Tk.Frame):
 
        return self.fading
 

	
 
class CueFader(Tk.Frame):
 
    def __init__(self, master, cuelist):
 
        Tk.Frame.__init__(self, master, bg='black')
 
        self.cuelist = cuelist
 
        self.auto_shift = Tk.IntVar()
 
        self.auto_shift.set(1)
 
        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
 
@@ -147,18 +161,30 @@ class CueFader(Tk.Frame):
 

	
 
        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', 
 
            command=self.toggle_autoshift, bg='black', fg='white', 
 
            highlightbackground='black')
 
        self.auto_load_times_checkbutton.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')
 
@@ -185,24 +211,66 @@ class CueFader(Tk.Frame):
 
            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):
 
        """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)"""
 
        # 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()
 
        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()
 
    def send_dmx_levels(self):
 
        # print "send_dmx_levels", self.current_dmx_levels
 
        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
 
@@ -218,17 +286,15 @@ class CueFader(Tk.Frame):
 
            return
 

	
 
        for scale_name, scale in self.scales.items():
 
            scale.scale.set(0)
 
        self.cuelist.shift((-1, 1)[name == 'Next'])
 

	
 
        # now 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()
 
        print "cues_as_subs", self.cues_as_subs
 
        self.update_cue_cache()
 
        if self.auto_load_times.get():
 
            self.reload_cue_times()
 
    def autoshift(self, name, scale):
 
        scale_val = scale.scale_var.get() 
 

	
 
        if scale_val == 1:
 
            if self.auto_shift.get():
 
                self.shift(name)
 
@@ -251,19 +317,14 @@ class CueFader(Tk.Frame):
 
            self.go_buttons[d].disable()
 
        else:
 
            # undo above work
 
            self.scales[d].enable()
 
            self.go_buttons[d].enable()
 

	
 
        cur_sub = self.cues_as_subs.get('Cur')
 
        if cur_sub:
 
            other_sub = self.cues_as_subs[name]
 
            # print 'fade between %s and %s (%.2f)' % (cur_sub, other_sub, scale_val)
 
            self.current_levels_as_sub = cur_sub.crossfade(other_sub, scale_val)
 
            print "current levels", self.current_levels_as_sub.get_dmx_list()
 
            # print
 
        self.current_dir = name
 
        self.compute_dmx_levels()
 
    def opposite_direction(self, d):
 
        if d == 'Next':
 
            return 'Prev'
 
        else:
 
            return 'Next'
 

	
 
@@ -279,25 +340,23 @@ class Cue:
 
    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 = {}
 
        try:
 
            print self, self.sub_levels
 
            for line in self.sub_levels.split(','):
 
        for line in self.sub_levels.split(','):
 
            try:
 
                line = line.strip()
 
                # print 'line', line
 
                if not line: 
 
                    continue
 
                sub, scale = line.split(':')
 
                # print 'sub', sub, 'scale', scale
 
                sub = sub.strip()
 
                scale = float(scale)
 
                subdict[sub] = scale
 
            # print 'subdict', subdict
 
        except (ValueError, AttributeError):
 
            print "parsing error when computing sub for", self
 
            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()
 

	
 
@@ -344,29 +403,33 @@ class CueList:
 
            self.current_cue_index = old_index + diff
 
    def set_next(self, index):
 
        self.next_pointer = index
 
    def set_prev(self, index):
 
        self.prev_pointer = index
 
    def bound_index(self, index):
 
        if not self.cues:
 
        if not self.cues or index < 0:
 
            return None
 
        else:
 
            return max(0, min(index, len(self.cues) - 1))
 
            return min(index, len(self.cues) - 1)
 
    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."""
 
        return [self.get_cue_by_index(index) 
 
            for index in self.get_current_cue_indices()]
 
    def get_cue_by_index(self, index):
 
        if index:
 
        try:
 
            return self.cues[self.bound_index(index)]
 
        else:
 
        except TypeError:
 
            return empty_cue
 
    def __del__(self):
 
        self.save()
 
    def save(self):
 
        print "Saving cues to", self.filename
 
        self.treedict.save(self.filename)
 
@@ -376,15 +439,17 @@ class CueList:
 
        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.redraw_cue)
 
        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])
 
            
 
@@ -408,30 +473,37 @@ class TkCueList(CueList, Tk.Frame):
 
                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()
 
    def set_fader(self, fader):
 
        self.fader = fader
 
    def wheelscroll(self, evt):
 
        """Perform mouse wheel scrolling"""
 
        amount = 2
 
        if evt.num == 4:
 
        if evt.num == 4: # scroll down
 
            amount = -2
 
        else:            # scroll up
 
            amount = 2
 
        self.hlist.yview('scroll', amount, 'units')
 
    def redraw_cue(self, cue):
 
    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 = ''
 
@@ -449,30 +521,35 @@ class TkCueList(CueList, Tk.Frame):
 
            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():
 
            self.hlist.see(index)
 
            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,))
 
@@ -511,13 +588,14 @@ class CueEditron(Tk.Frame):
 
            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', 'desc', 'sub_levels')):
 
        for row, field in enumerate(('name', 'time', 'page', '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)
 
@@ -534,13 +612,14 @@ class CueEditron(Tk.Frame):
 
                    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', 'desc', 'sub_levels')):
 
        for row, field in enumerate(('name', 'time', 'page', 'desc', 
 
            'sub_levels')):
 
            text = ''
 
            if self.cue:
 
                try:
 
                    text = getattr(self.cue, field)
 
                except AttributeError:
 
                    pass
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="138432956" >
 
<attr name="__toplevel__" family="map" type="__compound__" extra="None TreeDict" id="138506468" >
 
  <entry>
 
    <key type="string" value="cues" />
 
    <val type="list" id="139794108" >
 
      <item type="PyObject" id="140312100" class="Cue">
 
    <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="name" type="string" value="tevya special" />
 
        <attr name="sub_levels" type="string" value="green : 1.0" />
 
        <attr name="time" type="string" value="0" />
 
        <attr name="time" type="string" value="2" />
 
      </item>
 
      <item type="PyObject" id="139843388" class="Cue">
 
        <attr name="subdict" type="dict" id="140307292" >
 
      <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" />
 
      </item>
 
      <item type="PyObject" id="139773172" class="Cue">
 
        <attr name="subdict" type="dict" id="139774164" >
 
      <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>
 
      <item type="PyObject" id="139793260" class="Cue">
 
        <attr name="subdict" type="dict" id="139774508" >
 
      <item type="PyObject" id="139915236" class="Cue">
 
        <attr name="subdict" type="dict" id="139852964" >
 
        </attr>
 
        <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" />
 
      </item>
 
      <item type="PyObject" id="139775180" class="Cue">
 
        <attr name="subdict" type="dict" id="139774764" >
 
      <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="42" />
 
        <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>
 
      <item type="PyObject" id="140308580" class="Cue">
 
        <attr name="subdict" type="dict" id="140311956" >
 
      <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>
 
      <item type="PyObject" id="139772700" class="Cue">
 
        <attr name="subdict" type="dict" id="140311684" >
 
      <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>
 
      <item type="PyObject" id="139774908" class="Cue">
 
        <attr name="subdict" type="dict" id="140310884" >
 
      <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>
 
      <item type="PyObject" id="140311076" class="Cue">
 
        <attr name="subdict" type="dict" id="140310300" >
 
      <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>
 
      <item type="PyObject" id="140304700" class="Cue">
 
        <attr name="subdict" type="dict" id="140302268" >
 
      <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>
 
      <item type="PyObject" id="140309860" class="Cue">
 
      <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="name" type="string" value="front only" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="10" />
 
      </item>
 
      <item type="PyObject" id="140310788" class="Cue">
 
      <item type="PyObject" id="139163156" class="Cue">
 
        <attr name="name" type="string" value="cue 11" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 11" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="11" />
 
      </item>
 
      <item type="PyObject" id="139762676" class="Cue">
 
        <attr name="sub_levels" type="string" value="" />
 
      <item type="PyObject" id="139854140" class="Cue">
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 12" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="2.1" />
 
      </item>
 
      <item type="PyObject" id="140301540" class="Cue">
 
        <attr name="sub_levels" type="string" value="" />
 
      <item type="PyObject" id="139841764" class="Cue">
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 13" />
 
        <attr name="sub_levels" type="string" value="" />
 
        <attr name="time" type="string" value="13" />
 
      </item>
 
      <item type="PyObject" id="140310636" class="Cue">
 
      <item type="PyObject" id="139854308" class="Cue">
 
        <attr name="name" type="string" value="cue 14" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 14" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="14" />
 
      </item>
 
      <item type="PyObject" id="140307748" class="Cue">
 
      <item type="PyObject" id="139858580" class="Cue">
 
        <attr name="name" type="string" value="cue 15" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 15" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="15" />
 
      </item>
 
      <item type="PyObject" id="140309436" class="Cue">
 
      <item type="PyObject" id="139856100" class="Cue">
 
        <attr name="name" type="string" value="cue 16" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 16" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="16" />
 
      </item>
 
      <item type="PyObject" id="140290764" class="Cue">
 
      <item type="PyObject" id="139856180" class="Cue">
 
        <attr name="name" type="string" value="cue 17" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 17" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="17" />
 
      </item>
 
      <item type="PyObject" id="140300196" class="Cue">
 
      <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="name" type="string" value="some name" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="18" />
 
      </item>
 
      <item type="PyObject" id="140301756" class="Cue">
 
      <item type="PyObject" id="139841948" class="Cue">
 
        <attr name="name" type="string" value="cue 19" />
 
        <attr name="desc" type="string" value="" />
 
        <attr name="page" type="string" value="" />
 
        <attr name="name" type="string" value="cue 19" />
 
        <attr name="sub_levels" type="string" value="red : 1" />
 
        <attr name="time" type="string" value="19" />
 
      </item>
 
    </val>
 
  </entry>
 
</attr>
 
</PyObject>
0 comments (0 inline, 0 general)