diff --git a/flax/CueFaders.py b/flax/CueFaders.py --- a/flax/CueFaders.py +++ b/flax/CueFaders.py @@ -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("", button_press) - scale.scale.bind("", 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 "" % (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("", 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() diff --git a/flax/cues/cuelist1 b/flax/cues/cuelist1 --- a/flax/cues/cuelist1 +++ b/flax/cues/cuelist1 @@ -1,167 +1,109 @@ - + - - - - - - - - - - - - - - - - + + + + + - - - - - - - - + + + + - - - + + + + + + + - - - - + - - - - - - - - + + + + - - - - - - - - + + + + - - - - - - - - + + + + - - - - - - - - + + + + + + + + + - - - - - - - - + + + + - - - - - - - - + + + + - - - - - - - - + + - - - - + - - - + + - - + - - - + + - - + - + + - - - - + - + + - - - - + - + + - - - - + - + + - - - - + - - - - - - + + + + - + + - - - - +