comparison light8/ExtSliderMapper.py @ 80:5f0c6bc8e9de

for my next trick, i'll object-ize this properly
author dmcc
date Fri, 12 Jul 2002 10:39:21 +0000
parents 45b12307c695
children 5a162150b68d
comparison
equal deleted inserted replaced
79:233fe8cefa36 80:5f0c6bc8e9de
1 """ The External Slider Mapping widget determines which pots map to which 1 """some more of the panels"""
2 submasters. It tells you the status of each mapping and saves and loads
3 presets. The show is relying on this module! Do not lose it!
4
5 FUQ (frequently unasked question(s))
6
7 1. What's with all the *args?
8
9 It lets functions take any number of arguments and throw them away.
10 Callbacks do this, and we typically don't care about what they have to say. """
11
12 from Tix import * 2 from Tix import *
13 from uihelpers import FancyDoubleVar, get_selection
14
15 stdfont = ('Arial', 8)
16 colors = ('lightBlue', 'lightPink', 'lightGreen', 'aquamarine1')
17
18 class SliderMapping:
19 def __init__(self, color, default='disconnected', synced=0,
20 extinputlevel=0, sublevel=0):
21 self.color = color
22 self.subname = StringVar() # name of submaster we're connected to
23 self.subname.set(default)
24 self.sublevel = DoubleVar() # scalelevel variable of that submaster
25 # self.sublevel = FancyDoubleVar() # scalelevel variable of that submaster
26 self.sublevel.set(sublevel)
27 self.synced = BooleanVar() # currently synced
28 self.synced.set(synced)
29 self.extlevel = DoubleVar() # external slider's input
30 self.extlevel.set(extinputlevel)
31 self.widgets = [] # list of widgets drawn
32 self.sublabel = None # the label which represents a sub level.
33 # we hold on to it so we can change its
34 # textvariable
35 self.statuslabel = None # tells us sync status
36 self.lastbgcolor = None # last background color drawn to avoid
37 # unnecessary redraws
38 self.subnames = [] # we need to keep track of this for idiotic reasons
39 def sync(self, *args):
40 self.synced.set(1)
41 self.color_bg()
42 def unsync(self, *args):
43 self.synced.set(0)
44 self.color_bg()
45 def issynced(self):
46 return self.synced.get()
47 def disconnect(self, *args):
48 self.set_subname('disconnected') # a bit hack-like
49 # self.sublevel.delete_named('sync')
50 '''
51 try:
52 if self.sublevel.unsync_trace_cbname is not None:
53 # self.sublevel.trace_vdelete('w',
54 # self.sublevel.unsync_trace_cbname)
55 self.sublevel._tk.call('trace', 'vdelete', self.sublevel._name,
56 'w', self.sublevel.unsync_trace_cbname)
57 self.sublevel.unsync_trace_cbname = None
58 except AttributeError:
59 pass
60 '''
61
62 self.sublabel.configure(text="N/A")
63 self.color_bg()
64 def isdisconnected(self):
65 return self.subname.get() == 'disconnected' # a bit more hack-like
66 def check_synced(self, *args):
67 'If external level is near than the sublevel, it synces'
68 if self.isdisconnected():
69 self.unsync()
70 return
71
72 if abs(self.extlevel.get() - self.sublevel.get()) <= 0.02:
73 self.sync()
74 def changed_extinput(self, newlevel):
75 'When a new external level is received, this incorporates it'
76 self.extlevel.set(newlevel)
77 self.check_synced()
78 self.color_bg()
79 def set_subname(self, newname):
80 try:
81 self.listbox.listbox.select_clear(0, END)
82 except IndexError:
83 pass
84 try:
85 newindex = self.subnames.index(newname)
86 self.listbox.listbox.select_set(newindex)
87 self.listbox.listbox.see(newindex)
88 except ValueError:
89 pass
90
91 self.subname.set(newname)
92 self.unsync()
93 self.color_bg()
94 def color_bg(self):
95 if self.widgets:
96 if self.isdisconnected():
97 color = 'honeyDew4'
98 # stupid hack
99 # elif abs(self.extlevel.get() - self.sublevel.get()) <= 0.02:
100 elif self.issynced():
101 color = 'honeyDew2'
102 else: # unsynced
103 color = 'red'
104
105 if self.statuslabel: # more stupid hackery
106 if color == 'honeyDew2': # connected
107 self.statuslabel['text'] = 'Sync'
108 elif self.extlevel.get() < self.sublevel.get():
109 self.statuslabel['text'] = 'No sync (go up)'
110 else:
111 self.statuslabel['text'] = 'No sync (go down)'
112
113 # print "color", color, "lastbgcolor", self.lastbgcolor
114 if self.lastbgcolor == color: return
115 for widget in self.widgets:
116 widget.configure(bg=color)
117 self.lastbgcolor = color
118 def set_sublevel_var(self, newvar):
119 'newvar is one of the variables in scalelevels'
120
121 if newvar is not self.sublevel:
122 # self.sublevel.delete_named('sync')
123 self.sublevel = newvar
124 self.sublabel.configure(textvariable=newvar)
125 # self.sublevel.trace_named('sync', lambda *args: self.unsync(*args))
126 '''
127 try:
128 if self.sublevel.unsync_trace_cbname is not None:
129 # remove an old trace
130 self.sublevel.trace_vdelete('w',
131 self.sublevel.unsync_trace_cbname)
132 except AttributeError:
133 pass # it didn't have one
134
135 self.sublevel = newvar
136 self.sublevel.unsync_trace_cbname = self.sublevel.trace('w',
137 self.unsync)
138 '''
139
140 # if self.sublabel:
141 # self.sublabel.configure(textvariable=newvar)
142 self.check_synced()
143 def get_mapping(self):
144 'Get name of submaster currently mapped'
145 return self.subname.get()
146 def get_level_pair(self):
147 'Returns suitable output for ExtSliderMapper.get_levels()'
148 return (self.subname.get(), self.extlevel.get())
149 def listbox_cb(self, *args):
150 selection = get_selection(self.listbox.listbox)
151 self.disconnect()
152 self.subname.set(self.subnames[selection])
153 self.listbox.listbox.select_set(selection)
154 def draw_interface(self, master, subnames):
155 'Draw interface into master, given a list of submaster names'
156 self.subnames = subnames
157 frame = Frame(master, bg='black')
158 self.listbox = ScrolledListBox(frame, scrollbar='y', bg='black')
159 self.listbox.listbox.bind("<<ListboxSelect>>", self.listbox_cb, add=1)
160 self.listbox.listbox.configure(font=stdfont, exportselection=0,
161 selectmode=BROWSE, bg='black', fg='white',
162 selectbackground=self.color)
163 self.listbox.vsb.configure(troughcolor=self.color)
164 # self.listbox.listbox.insert(END, "disconnected")
165 for s in subnames:
166 self.listbox.listbox.insert(END, s)
167 statframe = Frame(frame, bg='black')
168
169 self.statuslabel = Label(statframe, text="No sync", width=15,
170 font=stdfont)
171 self.statuslabel.grid(columnspan=2, sticky=W)
172 ilabel = Label(statframe, text="Input", fg='blue', font=stdfont)
173 ilabel.grid(row=1, sticky=W)
174 extlabel = Label(statframe, textvariable=self.extlevel, width=5,
175 font=stdfont)
176 extlabel.grid(row=1, column=1)
177 rlabel = Label(statframe, text="Real", font=stdfont)
178 rlabel.grid(row=2, sticky=W)
179 self.sublabel = Label(statframe, text="N/A", width=5, font=stdfont)
180 self.sublabel.grid(row=2, column=1)
181 disc_button = Button(statframe, text="Disconnect",
182 command=self.disconnect, padx=0, pady=0, font=stdfont)
183 disc_button.grid(row=3, columnspan=2)
184 statframe.pack(side=BOTTOM, expand=1, fill=BOTH)
185 self.listbox.pack(expand=1, fill=BOTH)
186 frame.pack(side=LEFT, expand=1, fill=BOTH)
187
188 self.widgets = [frame, self.listbox, statframe, self.statuslabel,
189 ilabel, extlabel, rlabel, self.sublabel, disc_button]
190 3
191 class ExtSliderMapper(Frame): 4 class ExtSliderMapper(Frame):
192 def __init__(self, parent, sliderlevels, sliderinput, 5 def __init__(self, parent, sliderlevels, sliderinput, filename='slidermapping',
193 lightboard, filename='slidermapping', numsliders=4): 6 numsliders=4):
194 'Slider levels is scalelevels, sliderinput is an ExternalInput object' 7 'Slider levels is scalelevels, sliderinput is an ExternalInput object'
195 Frame.__init__(self, parent, bg='black') 8 Frame.__init__(self, parent)
196 self.parent = parent 9 self.parent = parent
197 self.sliderlevels = sliderlevels 10 self.sliderlevels = sliderlevels
198 self.sliderinput = sliderinput 11 self.sliderinput = sliderinput
199 self.filename = filename 12 self.filename = filename
200 self.numsliders = numsliders 13 self.numsliders = numsliders
201 self.lightboard = lightboard
202 self.file = None 14 self.file = None
203 15
204 # don't call setup, let them do that when scalelevels is created 16 # don't call setup, let them do that when scalelevels is created
205 def setup(self): 17 def setup(self):
206 self.subnames = self.sliderlevels.keys() 18 self.subnames = self.sliderlevels.keys()
207 self.subnames.sort() 19 self.subnames.sort()
208 self.presets = {} 20 self.presets = {}
209 self.load_presets() 21 self.load_presets()
210 22
211 self.current_preset = StringVar() # name of current preset 23 self.current_mapping_name = StringVar()
212 self.current_mappings = [] 24 self.current_mapping = []
213 for i, color in zip(range(self.numsliders), colors): 25 self.attached = []
214 self.current_mappings.append(SliderMapping(color)) 26 self.levels_read = []
27 for i in range(self.numsliders):
28 cm_var = StringVar()
29 cm_var.set('disconnected')
30 self.current_mapping.append(cm_var)
31 self.attached.append(BooleanVar())
32 self.levels_read.append(DoubleVar())
215 33
34 self.reallevellabels = []
216 self.draw_interface() 35 self.draw_interface()
217 def load_presets(self, *args): 36 def load_presets(self):
218 self.presets = {}
219 self.file = open(self.filename, 'r') 37 self.file = open(self.filename, 'r')
220 lines = self.file.readlines() 38 lines = self.file.readlines()
221 for l in lines: 39 for l in lines:
222 tokens = l[:-1].split('\t') 40 tokens = l[:-1].split('\t')
223 name = tokens.pop(0) 41 name = tokens.pop(0)
224 self.presets[name] = tokens 42 self.presets[name] = tokens
225 self.file.close() 43 self.file.close()
226 if args: # called from callback
227 self.draw_interface()
228 def save_presets(self): 44 def save_presets(self):
229 self.file = open(self.filename, 'w') 45 self.file = open(self.filename, 'w')
230 self.file.seek(0) 46 self.file.seek(0)
231 preset_names = self.presets.keys() 47 preset_names = self.presets.keys()
232 preset_names.sort() 48 preset_names.sort()
233 for p in preset_names: 49 for p in preset_names:
234 s = '\t'.join([p] + self.presets[p]) + '\n' 50 s = '\t'.join([p] + self.presets[p]) + '\n'
235 self.file.write(s) 51 self.file.write(s)
236 self.file.close() 52 self.file.close()
237 def load_scalelevels(self): 53 def load_scalelevels(self):
238 for slidermap in self.current_mappings: 54 for m, rll, levread, att in zip(self.current_mapping, self.reallevellabels,
55 self.levels_read, self.attached):
239 try: 56 try:
240 v = self.sliderlevels[slidermap.get_mapping()] 57 v = self.sliderlevels[m.get()] # actual scalelevel variable
241 slidermap.set_sublevel_var(v) 58 rll.configure(textvariable=v)
242 # print "ESM: Yes submaster named", slidermap.get_mapping() 59 if levread.get() >= v.get(): # attach if physical goes above virtual
60 att.set(1)
243 except KeyError: 61 except KeyError:
244 name = slidermap.get_mapping() 62 pass
245 if name != 'disconnected':
246 print "ESM: No submaster named", name
247 63
248 def get_levels(self): 64 def get_levels(self):
249 'Called by changelevels, returns a dict of new values for submasters' 65 'To be called by changelevels, I think'
66 if not self.current_mapping_name: return {}
250 if not self.sliderinput: return {} 67 if not self.sliderinput: return {}
251 68
252 self.load_scalelevels() # freshen our input from the submasters 69 self.load_scalelevels()
253
254 for m, color in zip(self.current_mappings, colors):
255 name = m.get_mapping()
256 lastsub = self.subs_highlighted.get(color)
257 if name is not lastsub:
258 if lastsub is not None:
259 try:
260 self.lightboard.highlight_sub(lastsub, 'restore')
261 except KeyError:
262 pass
263 try:
264 self.lightboard.highlight_sub(name, color)
265 except KeyError:
266 pass
267 self.subs_highlighted[color] = name
268 70
269 rawlevels = self.sliderinput.get_levels() 71 rawlevels = self.sliderinput.get_levels()
270 for rawlev, slidermap in zip(rawlevels, self.current_mappings): 72 for rawlev, levlabvar in zip(rawlevels, self.levels_read):
271 slidermap.changed_extinput(rawlev) 73 levlabvar.set(rawlev)
74 outputlevels = {}
75 return dict([(name.get(), lev)
76 for name, lev, att in zip(self.current_mapping,
77 rawlevels,
78 self.attached)
79 if att.get()])
272 80
273 return dict([m.get_level_pair()
274 for m in self.current_mappings
275 if m.issynced()])
276 def draw_interface(self): 81 def draw_interface(self):
277 self.subs_highlighted = {} 82 self.reallevellabels = []
278 subchoiceframe = Frame(self, bg='black') 83 subchoiceframe = Frame(self)
279 for m in self.current_mappings: 84 for i, mapping, isattached, lev in zip(range(self.numsliders),
280 m.draw_interface(subchoiceframe, self.subnames) 85 self.current_mapping,
86 self.attached,
87 self.levels_read):
88 f = Frame(subchoiceframe)
89 # Label(f, text="Slider %d" % (i+1)).pack(side=LEFT)
90 c = ComboBox(f, variable=mapping)
91 c.slistbox.listbox.insert(END, "disconnected")
92 for s in self.subnames:
93 c.slistbox.listbox.insert(END, s)
94 c.entry.configure(width=12)
95 statframe = Frame(f)
96 Checkbutton(statframe, variable=isattached,
97 text="Attached").grid(columnspan=2, sticky=W)
98 Label(statframe, text="Input", fg='red').grid(row=1, sticky=W)
99 Label(statframe, textvariable=lev, fg='red', width=5).grid(row=1, column=1)
100 Label(statframe, text="Real").grid(row=2, sticky=W)
101 l = Label(statframe, text="N/A", width=5)
102 l.grid(row=2, column=1)
103 self.reallevellabels.append(l)
104 statframe.pack(side=BOTTOM, expand=1, fill=X)
105 c.pack()
106 f.pack(side=LEFT, expand=1, fill=BOTH)
281 subchoiceframe.pack() 107 subchoiceframe.pack()
282 108
283 presetframe = Frame(self, bg='black') 109 presetframe = Frame(self)
284 Label(presetframe, text="Preset:", font=('Arial', 10), bg='black', 110 Label(presetframe, text="Preset:").pack(side=LEFT)
285 fg='white').pack(side=LEFT) 111 self.presetcombo = ComboBox(presetframe, variable=self.current_mapping_name,
286 self.presetcombo = ComboBox(presetframe, variable=self.current_preset, 112 editable=1, command=self.apply_preset)
287 editable=1, command=self.apply_preset,
288 dropdown=1)
289 self.presetcombo.slistbox.configure(bg='black')
290 self.presetcombo.slistbox.listbox.configure(bg='black', fg='white')
291 self.presetcombo.entry.configure(bg='black', fg='white')
292 self.draw_presets() 113 self.draw_presets()
293 self.presetcombo.pack(side=LEFT) 114 self.presetcombo.pack(side=LEFT)
294 Button(presetframe, text="Prev", padx=0, pady=0, bg='black', 115 Button(presetframe, text="Add", padx=0, pady=0,
295 fg='white', font=stdfont,
296 command=self.prev_preset).pack(side=LEFT)
297 Button(presetframe, text="Next", padx=0, pady=0, bg='black',
298 fg='white', font=stdfont,
299 command=self.next_preset).pack(side=LEFT)
300 Button(presetframe, text="Add", padx=0, pady=0, bg='black',
301 fg='white', font=stdfont,
302 command=self.add_preset).pack(side=LEFT) 116 command=self.add_preset).pack(side=LEFT)
303 Button(presetframe, text="Delete", padx=0, pady=0, bg='black', 117 Button(presetframe, text="Delete", padx=0, pady=0,
304 fg='white', font=stdfont,
305 command=self.delete_preset).pack(side=LEFT) 118 command=self.delete_preset).pack(side=LEFT)
306 Button(presetframe, text="Disconnect", padx=0, pady=0, bg='black',
307 fg='white', font=stdfont,
308 command=self.disconnect_all).pack(side=LEFT)
309 Button(presetframe, text="Reload", padx=0, pady=0, bg='black',
310 fg='white', font=stdfont,
311 command=self.load_presets).pack(side=LEFT)
312 presetframe.pack(side=BOTTOM) 119 presetframe.pack(side=BOTTOM)
313 def apply_preset(self, preset): 120 def apply_preset(self, preset):
314 if not preset: return 121 if not preset: return
315 preset_mapping = self.presets.get(preset) 122 mapping = self.presets.get(preset)
316 if not preset_mapping: return 123 if not mapping: return
317 self.disconnect_all() 124 for name, var, att in zip(mapping, self.current_mapping, self.attached):
318 for subname, slidermap in zip(preset_mapping, self.current_mappings): 125 var.set(name)
319 slidermap.set_subname(subname) 126 att.set(0) # detach all sliders
320 def change_preset_by_index(self, delta):
321 preset_names = self.presets.keys()
322 preset_names.sort()
323 try:
324 next = preset_names[preset_names.index(self.current_preset.get())
325 + delta]
326 self.current_preset.set(next)
327 self.apply_preset(next)
328 except (IndexError, ValueError):
329 print "Light 8.8: Can't go in that direction. Dig up!"
330 def next_preset(self, *args):
331 self.change_preset_by_index(1)
332 def prev_preset(self, *args):
333 self.change_preset_by_index(-1)
334 def delete_preset(self, *args): 127 def delete_preset(self, *args):
335 del self.presets[self.current_preset.get()] 128 del self.presets[self.current_mapping_name.get()]
336 self.presetcombo.slistbox.listbox.delete(0, END) 129 self.presetcombo.slistbox.listbox.delete(0, END)
337 self.draw_presets() 130 self.draw_presets()
338 self.save_presets() 131 self.save_presets()
339 def add_preset(self, *args): 132 def add_preset(self, *args):
340 self.presets[self.current_preset.get()] = [m.get_mapping() 133 self.presets[self.current_mapping_name.get()] = [m.get()
341 for m in self.current_mappings] 134 for m in self.current_mapping]
342 self.presetcombo.slistbox.listbox.delete(0, END) 135 self.presetcombo.slistbox.listbox.delete(0, END)
343 self.draw_presets() 136 self.draw_presets()
344 self.save_presets() 137 self.save_presets()
345 self.draw_interface()
346 def draw_presets(self): 138 def draw_presets(self):
347 preset_names = self.presets.keys() 139 preset_names = self.presets.keys()
348 preset_names.sort() 140 preset_names.sort()
349 for p in preset_names: 141 for p in preset_names:
350 self.presetcombo.slistbox.listbox.insert(END, p) 142 self.presetcombo.slistbox.listbox.insert(END, p)
351 def disconnect_all(self):
352 for m in self.current_mappings:
353 m.disconnect()