comparison bin/keyboardcomposer @ 214:38c1ccfb6820

keyboardcomposer works
author drewp@bigasterisk.com
date Mon, 11 Apr 2005 02:21:26 +0000
parents flax/KeyboardComposer.py@3905d3c92aaa
children 233ee9b1e561
comparison
equal deleted inserted replaced
213:87caa8906582 214:38c1ccfb6820
1 #!/usr/bin/python
2
3 from __future__ import division,nested_scopes
4 import sys, time
5
6 from twisted.internet import reactor,tksupport
7 from twisted.web import xmlrpc, server
8 from Tix import *
9 import math, atexit, pickle
10
11 import run_local
12 from light9.Fadable import Fadable
13 from light9.Submaster import Submasters, sub_maxes
14 from light9 import dmxclient
15 from light9.uihelpers import toplevelat
16
17 nudge_keys = {
18 'up' : list('qwertyuiop'),
19 'down' : list('asdfghjkl')
20 }
21 nudge_keys['down'].append('semicolon')
22
23 class SubScale(Scale, Fadable):
24 def __init__(self, master, *args, **kw):
25 self.scale_var = kw.get('variable') or DoubleVar()
26 kw.update({'variable' : self.scale_var,
27 'from' : 1, 'to' : 0, 'showvalue' : 0,
28 'sliderlength' : 15, 'res' : 0.01,
29 'width' : 40, 'troughcolor' : 'black', 'bg' : 'grey40',
30 'highlightthickness' : 1, 'bd' : 1,
31 'highlightcolor' : 'red', 'highlightbackground' : 'black',
32 'activebackground' : 'red'})
33 Scale.__init__(self, master, *args, **kw)
34 Fadable.__init__(self, var=self.scale_var, wheel_step=0.05)
35 self.draw_indicator_colors()
36 def draw_indicator_colors(self):
37 if self.scale_var.get() == 0:
38 self['troughcolor'] = 'black'
39 else:
40 self['troughcolor'] = 'blue'
41
42 class SubmasterTk(Frame):
43 def __init__(self, master, name, current_level):
44 Frame.__init__(self, master, bd=1, relief='raised', bg='black')
45 self.slider_var = DoubleVar()
46 self.slider_var.set(current_level)
47 self.scale = SubScale(self, variable=self.slider_var, width=20)
48 namelabel = Label(self, text=name, font="Arial 11", bg='black',
49 fg='white')
50 namelabel.pack(side=TOP)
51 levellabel = Label(self, textvariable=self.slider_var, font="Arial 11",
52 bg='black', fg='white')
53 levellabel.pack(side=TOP)
54 self.scale.pack(side=BOTTOM, expand=1, fill=BOTH)
55
56 class KeyboardComposer(Frame):
57 def __init__(self, root, submasters, current_sub_levels=None, dmxdummy=0):
58 Frame.__init__(self, root, bg='black')
59 self.submasters = submasters
60 self.dmxdummy = dmxdummy
61
62 self.current_sub_levels = {}
63 if current_sub_levels:
64 self.current_sub_levels = current_sub_levels
65 else:
66 try:
67 self.current_sub_levels = \
68 pickle.load(file('.keyboardcomposer.savedlevels'))
69 except IOError:
70 pass
71
72 self.draw_ui()
73 self.send_levels_loop()
74 def draw_ui(self):
75 self.rows = [] # this holds Tk Frames for each row
76 self.slider_vars = {} # this holds subname:sub Tk vars
77 self.slider_table = {} # this holds coords:sub Tk vars
78 self.name_to_subtk = {} # subname : SubmasterTk instance
79 self.current_row = 0
80
81 self.make_key_hints()
82 self.draw_sliders()
83 self.highlight_row(self.current_row)
84 self.rows[self.current_row].focus()
85
86 self.buttonframe = Frame(self, bg='black')
87 self.buttonframe.pack(side=BOTTOM)
88 self.refreshbutton = Button(self.buttonframe, text="Refresh",
89 command=self.refresh, bg='black', fg='white')
90 self.refreshbutton.pack(side=LEFT)
91 self.save_stage_button = Button(self.buttonframe, text="Save",
92 command=lambda: self.save_current_stage(self.sub_name.get()),
93 bg='black', fg='white')
94 self.save_stage_button.pack(side=LEFT)
95 self.sub_name = Entry(self.buttonframe, bg='black', fg='white')
96 self.sub_name.pack(side=LEFT)
97 self.stop_frequent_update_time = 0
98 def make_key_hints(self):
99 keyhintrow = Frame(self)
100
101 col = 0
102 for upkey, downkey in zip(nudge_keys['up'],
103 nudge_keys['down']):
104 # what a hack!
105 downkey = downkey.replace('semicolon', ';')
106 upkey, downkey = (upkey.upper(), downkey.upper())
107
108 # another what a hack!
109 keylabel = Label(keyhintrow, text='%s\n%s' % (upkey, downkey),
110 width=1, font=('Arial', 10), bg='red', fg='white', anchor='c')
111 keylabel.pack(side=LEFT, expand=1, fill=X)
112 col += 1
113
114 keyhintrow.pack(fill=X, expand=0)
115 self.keyhints = keyhintrow
116 def setup_key_nudgers(self, tkobject):
117 for d, keys in nudge_keys.items():
118 for key in keys:
119 # lowercase makes full=0
120 keysym = "<KeyPress-%s>" % key
121 tkobject.bind(keysym, \
122 lambda evt, num=keys.index(key), d=d: \
123 self.got_nudger(num, d))
124
125 # uppercase makes full=1
126 keysym = "<KeyPress-%s>" % key.upper()
127 keysym = keysym.replace('SEMICOLON', 'colon')
128 tkobject.bind(keysym, \
129 lambda evt, num=keys.index(key), d=d: \
130 self.got_nudger(num, d, full=1))
131
132 # Row changing:
133 # Page dn, C-n, and ] do down
134 # Page up, C-p, and ' do up
135 for key in '<Prior> <Next> <Control-n> <Control-p> ' \
136 '<Key-bracketright> <Key-apostrophe>'.split():
137 tkobject.bind(key, self.change_row)
138
139 def change_row(self, event):
140 diff = 1
141 if event.keysym in ('Prior', 'p', 'bracketright'):
142 diff = -1
143 old_row = self.current_row
144 self.current_row += diff
145 self.current_row = max(0, self.current_row)
146 self.current_row = min(len(self.rows) - 1, self.current_row)
147 self.unhighlight_row(old_row)
148 self.highlight_row(self.current_row)
149 row = self.rows[self.current_row]
150 self.keyhints.pack_configure(before=row)
151 def got_nudger(self, number, direction, full=0):
152 subtk = self.slider_table[(self.current_row, number)]
153 if direction == 'up':
154 if full:
155 subtk.scale.fade(1)
156 else:
157 subtk.scale.increase()
158 else:
159 if full:
160 subtk.scale.fade(0)
161 else:
162 subtk.scale.decrease()
163 def draw_sliders(self):
164 self.tk_focusFollowsMouse()
165
166 rowcount = -1
167 col = 0
168 for sub in self.submasters.get_all_subs():
169 if col == 0: # make new row
170 row = self.make_row()
171 rowcount += 1
172 current_level = self.current_sub_levels.get(sub.name, 0)
173 subtk = self.draw_sub_slider(row, col, sub.name, current_level)
174 self.slider_table[(rowcount, col)] = subtk
175 self.name_to_subtk[sub.name] = subtk
176 col += 1
177 col %= 10
178
179 def slider_changed(x, y, z, subtk=subtk):
180 subtk.scale.draw_indicator_colors()
181 self.send_levels()
182
183 subtk.slider_var.trace('w', slider_changed)
184 def make_row(self):
185 row = Frame(self, bd=2, bg='black')
186 row.pack(expand=1, fill=BOTH)
187 self.setup_key_nudgers(row)
188 self.rows.append(row)
189 return row
190 def draw_sub_slider(self, row, col, name, current_level):
191 subtk = SubmasterTk(row, name, current_level)
192 subtk.place(relx=col * 0.1, rely=0, relwidth=0.1, relheight=1)
193 self.setup_key_nudgers(subtk.scale)
194
195 self.slider_vars[name] = subtk.slider_var
196 return subtk
197 def highlight_row(self, row):
198 row = self.rows[row]
199 row['bg'] = 'red'
200 def unhighlight_row(self, row):
201 row = self.rows[row]
202 row['bg'] = 'black'
203 def get_levels(self):
204 return dict([(name, slidervar.get())
205 for name, slidervar in self.slider_vars.items()])
206 def get_levels_as_sub(self):
207 scaledsubs = [self.submasters.get_sub_by_name(sub) * level \
208 for sub, level in self.get_levels().items()]
209
210 maxes = sub_maxes(*scaledsubs)
211 return maxes
212 def save_current_stage(self, subname):
213 print "saving current levels as", subname
214 sub = self.get_levels_as_sub()
215 sub.name = subname
216 sub.temporary = 0
217 sub.save()
218
219 def save(self):
220 pickle.dump(self.get_levels(),
221 file('.keyboardcomposer.savedlevels', 'w'))
222 def send_frequent_updates(self):
223 """called when we get a fade -- send events as quickly as possible"""
224 if time.time() <= self.stop_frequent_update_time:
225 self.send_levels()
226 self.after(10, self.send_frequent_updates)
227
228 def get_dmx_list(self):
229 maxes = self.get_levels_as_sub()
230 return maxes.get_dmx_list()
231 def send_levels(self):
232 if not self.dmxdummy:
233 levels = self.get_dmx_list()
234 dmxclient.outputlevels(levels)
235 # print "sending levels", levels
236 def send_levels_loop(self):
237 self.send_levels()
238 self.after(1000, self.send_levels_loop)
239 def refresh(self):
240 self.save()
241 self.submasters = Submasters()
242 self.current_sub_levels = \
243 pickle.load(file('.keyboardcomposer.savedlevels'))
244 for r in self.rows:
245 r.destroy()
246 self.keyhints.destroy()
247 self.buttonframe.destroy()
248 self.draw_ui()
249
250 class LevelServer(xmlrpc.XMLRPC):
251 def __init__(self,name_to_subtk):
252 self.name_to_subtk = name_to_subtk
253
254 def xmlrpc_fadesub(self,subname,level,secs):
255 """submaster will fade to level in secs"""
256 try:
257 self.name_to_subtk[subname].scale.fade(level,secs)
258 ret='ok'
259 except Exception,e:
260 ret=str(e)
261 return ret
262
263
264 if __name__ == "__main__":
265 s = Submasters()
266
267 root = Tk()
268 tl = toplevelat("Keyboard Composer", existingtoplevel=root)
269 kc = KeyboardComposer(tl, s, dmxdummy=0)
270 kc.pack(fill=BOTH, expand=1)
271
272 ls = LevelServer(kc.name_to_subtk)
273 reactor.listenTCP(8050, server.Site(ls))
274
275 root.bind("<Destroy>",reactor.stop)
276 root.protocol('WM_DELETE_WINDOW', reactor.stop)
277 reactor.addSystemEventTrigger('after','shutdown',kc.save)
278 tksupport.install(root,ms=10)
279 reactor.run()