comparison light8/ExtSliderMapper.py @ 0:45b12307c695

Initial revision
author drewp
date Wed, 03 Jul 2002 09:37:57 +0000
parents
children 5f0c6bc8e9de
comparison
equal deleted inserted replaced
-1:000000000000 0:45b12307c695
1 """ The External Slider Mapping widget determines which pots map to which
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 *
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
191 class ExtSliderMapper(Frame):
192 def __init__(self, parent, sliderlevels, sliderinput,
193 lightboard, filename='slidermapping', numsliders=4):
194 'Slider levels is scalelevels, sliderinput is an ExternalInput object'
195 Frame.__init__(self, parent, bg='black')
196 self.parent = parent
197 self.sliderlevels = sliderlevels
198 self.sliderinput = sliderinput
199 self.filename = filename
200 self.numsliders = numsliders
201 self.lightboard = lightboard
202 self.file = None
203
204 # don't call setup, let them do that when scalelevels is created
205 def setup(self):
206 self.subnames = self.sliderlevels.keys()
207 self.subnames.sort()
208 self.presets = {}
209 self.load_presets()
210
211 self.current_preset = StringVar() # name of current preset
212 self.current_mappings = []
213 for i, color in zip(range(self.numsliders), colors):
214 self.current_mappings.append(SliderMapping(color))
215
216 self.draw_interface()
217 def load_presets(self, *args):
218 self.presets = {}
219 self.file = open(self.filename, 'r')
220 lines = self.file.readlines()
221 for l in lines:
222 tokens = l[:-1].split('\t')
223 name = tokens.pop(0)
224 self.presets[name] = tokens
225 self.file.close()
226 if args: # called from callback
227 self.draw_interface()
228 def save_presets(self):
229 self.file = open(self.filename, 'w')
230 self.file.seek(0)
231 preset_names = self.presets.keys()
232 preset_names.sort()
233 for p in preset_names:
234 s = '\t'.join([p] + self.presets[p]) + '\n'
235 self.file.write(s)
236 self.file.close()
237 def load_scalelevels(self):
238 for slidermap in self.current_mappings:
239 try:
240 v = self.sliderlevels[slidermap.get_mapping()]
241 slidermap.set_sublevel_var(v)
242 # print "ESM: Yes submaster named", slidermap.get_mapping()
243 except KeyError:
244 name = slidermap.get_mapping()
245 if name != 'disconnected':
246 print "ESM: No submaster named", name
247
248 def get_levels(self):
249 'Called by changelevels, returns a dict of new values for submasters'
250 if not self.sliderinput: return {}
251
252 self.load_scalelevels() # freshen our input from the submasters
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
269 rawlevels = self.sliderinput.get_levels()
270 for rawlev, slidermap in zip(rawlevels, self.current_mappings):
271 slidermap.changed_extinput(rawlev)
272
273 return dict([m.get_level_pair()
274 for m in self.current_mappings
275 if m.issynced()])
276 def draw_interface(self):
277 self.subs_highlighted = {}
278 subchoiceframe = Frame(self, bg='black')
279 for m in self.current_mappings:
280 m.draw_interface(subchoiceframe, self.subnames)
281 subchoiceframe.pack()
282
283 presetframe = Frame(self, bg='black')
284 Label(presetframe, text="Preset:", font=('Arial', 10), bg='black',
285 fg='white').pack(side=LEFT)
286 self.presetcombo = ComboBox(presetframe, variable=self.current_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()
293 self.presetcombo.pack(side=LEFT)
294 Button(presetframe, text="Prev", padx=0, pady=0, bg='black',
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)
303 Button(presetframe, text="Delete", padx=0, pady=0, bg='black',
304 fg='white', font=stdfont,
305 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)
313 def apply_preset(self, preset):
314 if not preset: return
315 preset_mapping = self.presets.get(preset)
316 if not preset_mapping: return
317 self.disconnect_all()
318 for subname, slidermap in zip(preset_mapping, self.current_mappings):
319 slidermap.set_subname(subname)
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):
335 del self.presets[self.current_preset.get()]
336 self.presetcombo.slistbox.listbox.delete(0, END)
337 self.draw_presets()
338 self.save_presets()
339 def add_preset(self, *args):
340 self.presets[self.current_preset.get()] = [m.get_mapping()
341 for m in self.current_mappings]
342 self.presetcombo.slistbox.listbox.delete(0, END)
343 self.draw_presets()
344 self.save_presets()
345 self.draw_interface()
346 def draw_presets(self):
347 preset_names = self.presets.keys()
348 preset_names.sort()
349 for p in preset_names:
350 self.presetcombo.slistbox.listbox.insert(END, p)
351 def disconnect_all(self):
352 for m in self.current_mappings:
353 m.disconnect()