Mercurial > code > home > repos > light9
comparison bin/keyboardcomposer @ 799:fcf95ff23cc5
PersistentSubmaster split. keyboardcomposer now notices submaster changes
Ignore-this: 2ea847e25af9b784cfec6aa4335dcc70
author | drewp@bigasterisk.com |
---|---|
date | Mon, 16 Jul 2012 21:51:04 +0000 |
parents | 62f99e2a00ac |
children | caeaa88430b8 |
comparison
equal
deleted
inserted
replaced
798:5c158d37f1ce | 799:fcf95ff23cc5 |
---|---|
2 | 2 |
3 from __future__ import division, nested_scopes | 3 from __future__ import division, nested_scopes |
4 import cgi, os, sys, time, subprocess, logging | 4 import cgi, os, sys, time, subprocess, logging |
5 from optparse import OptionParser | 5 from optparse import OptionParser |
6 import webcolors, colorsys | 6 import webcolors, colorsys |
7 | 7 from louie import dispatcher |
8 from twisted.internet import reactor, tksupport | 8 from twisted.internet import reactor, tksupport |
9 from twisted.web import xmlrpc, server, resource | 9 from twisted.web import xmlrpc, server, resource |
10 from Tix import * | 10 from Tix import * |
11 import Tix as tk | 11 import Tix as tk |
12 import pickle | 12 import pickle |
17 from light9.subclient import SubClient | 17 from light9.subclient import SubClient |
18 from light9 import dmxclient, showconfig, networking, prof | 18 from light9 import dmxclient, showconfig, networking, prof |
19 from light9.uihelpers import toplevelat, bindkeys | 19 from light9.uihelpers import toplevelat, bindkeys |
20 from light9.namespaces import L9 | 20 from light9.namespaces import L9 |
21 from light9.tkdnd import initTkdnd, dragSourceRegister | 21 from light9.tkdnd import initTkdnd, dragSourceRegister |
22 from light9.rdfdb.syncedgraph import SyncedGraph | |
23 | |
22 from bcf2000 import BCF2000 | 24 from bcf2000 import BCF2000 |
23 | 25 |
24 nudge_keys = { | 26 nudge_keys = { |
25 'up' : list('qwertyui'), | 27 'up' : list('qwertyui'), |
26 'down' : list('asdfghjk') | 28 'down' : list('asdfghjk') |
53 else: | 55 else: |
54 self['troughcolor'] = 'blue' | 56 self['troughcolor'] = 'blue' |
55 | 57 |
56 class SubmasterTk(Frame): | 58 class SubmasterTk(Frame): |
57 def __init__(self, master, sub, current_level): | 59 def __init__(self, master, sub, current_level): |
60 self.sub = sub | |
58 bg = sub.graph.value(sub.uri, L9.color, default='#000000') | 61 bg = sub.graph.value(sub.uri, L9.color, default='#000000') |
59 rgb = webcolors.hex_to_rgb(bg) | 62 rgb = webcolors.hex_to_rgb(bg) |
60 hsv = colorsys.rgb_to_hsv(*[x/255 for x in rgb]) | 63 hsv = colorsys.rgb_to_hsv(*[x/255 for x in rgb]) |
61 darkBg = webcolors.rgb_to_hex(tuple([x * 255 for x in colorsys.hsv_to_rgb( | 64 darkBg = webcolors.rgb_to_hex(tuple([x * 255 for x in colorsys.hsv_to_rgb( |
62 hsv[0], hsv[1], .3)])) | 65 hsv[0], hsv[1], .3)])) |
63 Frame.__init__(self, master, bd=1, relief='raised', bg=bg) | 66 Frame.__init__(self, master, bd=1, relief='raised', bg=bg) |
64 self.name = sub.name | 67 self.name = sub.name |
65 self.slider_var = DoubleVar() | 68 self.slider_var = DoubleVar() |
66 self.slider_var.set(current_level) | 69 self.slider_var.set(current_level) |
67 self.scale = SubScale(self, variable=self.slider_var, width=20) | 70 self.scale = SubScale(self, variable=self.slider_var, width=20) |
68 namelabel = Label(self, text=sub.name, font="Arial 7", bg=darkBg, | 71 |
72 self.namelabel = Label(self, font="Arial 7", bg=darkBg, | |
69 fg='white', pady=0) | 73 fg='white', pady=0) |
70 namelabel.pack(side=TOP) | 74 self.sub.graph.addHandler(self.updateName) |
75 | |
76 self.namelabel.pack(side=TOP) | |
71 levellabel = Label(self, textvariable=self.slider_var, font="Arial 7", | 77 levellabel = Label(self, textvariable=self.slider_var, font="Arial 7", |
72 bg='black', fg='white', pady=0) | 78 bg='black', fg='white', pady=0) |
73 levellabel.pack(side=TOP) | 79 levellabel.pack(side=TOP) |
74 self.scale.pack(side=BOTTOM, expand=1, fill=BOTH) | 80 self.scale.pack(side=BOTTOM, expand=1, fill=BOTH) |
75 bindkeys(self, "<Control-Key-l>", self.launch_subcomposer) | 81 bindkeys(self, "<Control-Key-l>", self.launch_subcomposer) |
76 | 82 |
77 for w in [self, namelabel, levellabel]: | 83 for w in [self, self.namelabel, levellabel]: |
78 dragSourceRegister(w, 'copy', 'text/uri-list', sub.uri) | 84 dragSourceRegister(w, 'copy', 'text/uri-list', sub.uri) |
85 | |
86 def updateName(self): | |
87 self.namelabel.config(text=self.sub.graph.label(self.sub.uri)) | |
79 | 88 |
80 def launch_subcomposer(self, *args): | 89 def launch_subcomposer(self, *args): |
81 subprocess.Popen(["bin/subcomposer", "--no-geometry", self.name]) | 90 subprocess.Popen(["bin/subcomposer", "--no-geometry", self.name]) |
82 | 91 |
83 class KeyboardComposer(Frame, SubClient): | 92 class KeyboardComposer(Frame, SubClient): |
84 def __init__(self, root, graph, submasters, current_sub_levels=None, | 93 def __init__(self, root, graph, current_sub_levels=None, |
85 hw_sliders=True): | 94 hw_sliders=True): |
86 Frame.__init__(self, root, bg='black') | 95 Frame.__init__(self, root, bg='black') |
87 SubClient.__init__(self) | 96 SubClient.__init__(self) |
88 self.graph = graph | 97 self.graph = graph |
89 self.submasters = submasters | 98 self.submasters = Submasters(graph) |
90 self.name_to_subtk = {} | 99 self.name_to_subtk = {} |
91 self.current_sub_levels = {} | 100 self.current_sub_levels = {} |
92 self.current_row = 0 | 101 self.current_row = 0 |
93 if current_sub_levels is not None: | 102 if current_sub_levels is not None: |
94 self.current_sub_levels = current_sub_levels | 103 self.current_sub_levels = current_sub_levels |
99 except IOError: | 108 except IOError: |
100 pass | 109 pass |
101 | 110 |
102 self.use_hw_sliders = hw_sliders | 111 self.use_hw_sliders = hw_sliders |
103 self.connect_to_hw(hw_sliders) | 112 self.connect_to_hw(hw_sliders) |
104 self.draw_ui() | 113 |
114 self.make_key_hints() | |
115 self.make_buttons() | |
116 | |
117 self.graph.addHandler(self.redraw_sliders) | |
105 self.send_levels_loop() | 118 self.send_levels_loop() |
106 | 119 |
107 def draw_ui(self): | 120 def make_buttons(self): |
108 self.rows = [] # this holds Tk Frames for each row | |
109 self.slider_vars = {} # this holds subname:sub Tk vars | |
110 self.slider_table = {} # this holds coords:sub Tk vars | |
111 self.name_to_subtk.clear() # subname : SubmasterTk instance | |
112 | |
113 self.make_key_hints() | |
114 self.draw_sliders() | |
115 if len(self.rows): | |
116 self.change_row(self.current_row) | |
117 self.rows[self.current_row].focus() | |
118 | |
119 self.buttonframe = Frame(self, bg='black') | 121 self.buttonframe = Frame(self, bg='black') |
120 self.buttonframe.pack(side=BOTTOM) | 122 self.buttonframe.pack(side=BOTTOM) |
121 | 123 |
122 self.sliders_status_var = IntVar() | 124 self.sliders_status_var = IntVar() |
123 self.sliders_status_var.set(self.use_hw_sliders) | 125 self.sliders_status_var.set(self.use_hw_sliders) |
129 | 131 |
130 self.alltozerobutton = Button(self.buttonframe, text="All to Zero", | 132 self.alltozerobutton = Button(self.buttonframe, text="All to Zero", |
131 command=self.alltozero, bg='black', fg='white') | 133 command=self.alltozero, bg='black', fg='white') |
132 self.alltozerobutton.pack(side='left') | 134 self.alltozerobutton.pack(side='left') |
133 | 135 |
134 self.refreshbutton = Button(self.buttonframe, text="Refresh", | |
135 command=self.refresh, bg='black', fg='white') | |
136 self.refreshbutton.pack(side=LEFT) | |
137 | |
138 self.save_stage_button = Button(self.buttonframe, text="Save", | 136 self.save_stage_button = Button(self.buttonframe, text="Save", |
139 command=lambda: self.save_current_stage(self.sub_name.get()), | 137 command=lambda: self.save_current_stage(self.sub_name.get()), |
140 bg='black', fg='white') | 138 bg='black', fg='white') |
141 self.save_stage_button.pack(side=LEFT) | 139 self.save_stage_button.pack(side=LEFT) |
142 self.sub_name = Entry(self.buttonframe, bg='black', fg='white') | 140 self.sub_name = Entry(self.buttonframe, bg='black', fg='white') |
143 self.sub_name.pack(side=LEFT) | 141 self.sub_name.pack(side=LEFT) |
144 | 142 |
143 | |
144 def redraw_sliders(self): | |
145 self.slider_vars = {} # this holds subname:sub Tk vars | |
146 self.slider_table = {} # this holds coords:sub Tk vars | |
147 self.name_to_subtk.clear() # subname : SubmasterTk instance | |
148 | |
149 self.graph.addHandler(self.draw_sliders) | |
150 if len(self.rows): | |
151 self.change_row(self.current_row) | |
152 self.rows[self.current_row].focus() | |
153 | |
145 self.stop_frequent_update_time = 0 | 154 self.stop_frequent_update_time = 0 |
146 | 155 |
156 def onNewSub(self, sub): | |
157 log.info("new %s", sub) | |
158 self.graph.addHandler(self.draw_sliders) | |
159 | |
160 def onLostSub(self, subUri): | |
161 log.info("lost %s", subUri) | |
162 self.graph.addHandler(self.draw_sliders) | |
163 | |
147 def draw_sliders(self): | 164 def draw_sliders(self): |
165 | |
166 | |
167 if hasattr(self, 'rows'): | |
168 for r in self.rows: | |
169 r.destroy() | |
170 self.rows = [] # this holds Tk Frames for each row | |
171 | |
172 | |
148 self.tk_focusFollowsMouse() | 173 self.tk_focusFollowsMouse() |
149 | 174 |
150 rowcount = -1 | 175 rowcount = -1 |
151 col = 0 | 176 col = 0 |
152 last_group = None | 177 last_group = None |
178 | |
179 # there are unlikely to be any subs at startup because we | |
180 # probably haven't been called back with the graph data yet | |
181 | |
182 #read get_all_subs then watch 'new submaster' 'lost submaster' signals | |
153 withgroups = sorted((self.graph.value(sub.uri, L9['group']), | 183 withgroups = sorted((self.graph.value(sub.uri, L9['group']), |
154 self.graph.value(sub.uri, L9['order']), | 184 self.graph.value(sub.uri, L9['order']), |
155 sub) | 185 sub) |
156 for sub in self.submasters.get_all_subs()) | 186 for sub in self.submasters.get_all_subs()) |
187 dispatcher.connect(self.onNewSub, "new submaster") | |
188 dispatcher.connect(self.onLostSub, "lost submaster") | |
189 log.info("withgroups %s", withgroups) | |
157 | 190 |
158 for group, order, sub in withgroups: | 191 for group, order, sub in withgroups: |
159 group = self.graph.value(sub.uri, L9['group']) | 192 group = self.graph.value(sub.uri, L9['group']) |
160 | 193 |
161 if col == 0 or group != last_group: # make new row | 194 if col == 0 or group != last_group: # make new row |
201 def connect_to_hw(self, hw_sliders): | 234 def connect_to_hw(self, hw_sliders): |
202 if hw_sliders: | 235 if hw_sliders: |
203 try: | 236 try: |
204 self.sliders = Sliders(self) | 237 self.sliders = Sliders(self) |
205 except IOError: | 238 except IOError: |
206 print "Couldn't actually find any sliders (but really, it's no problem)" | 239 log.info("no hardware sliders") |
207 self.sliders = DummySliders() | 240 self.sliders = DummySliders() |
208 self.use_hw_sliders = False | 241 self.use_hw_sliders = False |
209 else: | 242 else: |
210 self.sliders = DummySliders() | 243 self.sliders = DummySliders() |
211 | 244 |
254 def change_row_cb(self, event): | 287 def change_row_cb(self, event): |
255 diff = 1 | 288 diff = 1 |
256 if event.keysym in ('Prior', 'p', 'bracketright'): | 289 if event.keysym in ('Prior', 'p', 'bracketright'): |
257 diff = -1 | 290 diff = -1 |
258 self.change_row(self.current_row + diff) | 291 self.change_row(self.current_row + diff) |
292 | |
259 def change_row(self, row): | 293 def change_row(self, row): |
260 old_row = self.current_row | 294 old_row = self.current_row |
261 self.current_row = row | 295 self.current_row = row |
262 self.current_row = max(0, self.current_row) | 296 self.current_row = max(0, self.current_row) |
263 self.current_row = min(len(self.rows) - 1, self.current_row) | 297 self.current_row = min(len(self.rows) - 1, self.current_row) |
326 return row | 360 return row |
327 | 361 |
328 def highlight_row(self, row): | 362 def highlight_row(self, row): |
329 row = self.rows[row] | 363 row = self.rows[row] |
330 row['bg'] = 'red' | 364 row['bg'] = 'red' |
365 | |
331 def unhighlight_row(self, row): | 366 def unhighlight_row(self, row): |
332 row = self.rows[row] | 367 row = self.rows[row] |
333 row['bg'] = 'black' | 368 row['bg'] = 'black' |
369 | |
334 def get_levels(self): | 370 def get_levels(self): |
335 return dict([(name, slidervar.get()) | 371 return dict([(name, slidervar.get()) |
336 for name, slidervar in self.slider_vars.items()]) | 372 for name, slidervar in self.slider_vars.items()]) |
373 | |
337 def get_levels_as_sub(self): | 374 def get_levels_as_sub(self): |
338 scaledsubs = [self.submasters.get_sub_by_name(sub) * level \ | 375 scaledsubs = [self.submasters.get_sub_by_name(sub) * level \ |
339 for sub, level in self.get_levels().items() if level > 0.0] | 376 for sub, level in self.get_levels().items() if level > 0.0] |
340 | 377 |
341 maxes = sub_maxes(*scaledsubs) | 378 maxes = sub_maxes(*scaledsubs) |
342 return maxes | 379 return maxes |
380 | |
343 def save_current_stage(self, subname): | 381 def save_current_stage(self, subname): |
344 print "saving current levels as", subname | 382 log.info("saving current levels as %s", subname) |
345 sub = self.get_levels_as_sub() | 383 sub = self.get_levels_as_sub() |
346 sub.name = subname | 384 sub.name = subname |
347 sub.temporary = 0 | 385 sub.temporary = 0 |
348 sub.save() | 386 sub.save() |
349 | 387 |
353 def send_frequent_updates(self): | 391 def send_frequent_updates(self): |
354 """called when we get a fade -- send events as quickly as possible""" | 392 """called when we get a fade -- send events as quickly as possible""" |
355 if time.time() <= self.stop_frequent_update_time: | 393 if time.time() <= self.stop_frequent_update_time: |
356 self.send_levels() | 394 self.send_levels() |
357 self.after(10, self.send_frequent_updates) | 395 self.after(10, self.send_frequent_updates) |
358 | |
359 def refresh(self): | |
360 self.save() | |
361 graph = showconfig.getGraph() | |
362 self.submasters = Submasters(graph) | |
363 self.current_sub_levels, self.current_row = \ | |
364 pickle.load(file('.keyboardcomposer.savedlevels')) | |
365 for r in self.rows: | |
366 r.destroy() | |
367 self.keyhints.destroy() | |
368 self.buttonframe.destroy() | |
369 self.draw_ui() | |
370 # possibly paranoia (but possibly not) | |
371 self.change_row(self.current_row) | |
372 | 396 |
373 def alltozero(self): | 397 def alltozero(self): |
374 for name, subtk in self.name_to_subtk.items(): | 398 for name, subtk in self.name_to_subtk.items(): |
375 if subtk.scale.scale_var.get() != 0: | 399 if subtk.scale.scale_var.get() != 0: |
376 subtk.scale.fade(value=0.0, length=0) | 400 subtk.scale.fade(value=0.0, length=0) |
461 help="don't attach to hardware sliders") | 485 help="don't attach to hardware sliders") |
462 parser.add_option('-v', action='store_true', help="log info level") | 486 parser.add_option('-v', action='store_true', help="log info level") |
463 opts, args = parser.parse_args() | 487 opts, args = parser.parse_args() |
464 | 488 |
465 logging.basicConfig(level=logging.INFO if opts.v else logging.WARN) | 489 logging.basicConfig(level=logging.INFO if opts.v else logging.WARN) |
466 log = logging.getLogger() | 490 log = logging.getLogger('keyboardcomposer') |
467 | 491 |
468 graph = showconfig.getGraph() | 492 graph = SyncedGraph("keyboardcomposer") |
469 s = Submasters(graph) | |
470 | 493 |
471 root = Tk() | 494 root = Tk() |
472 initTkdnd(root.tk, 'tkdnd/trunk/') | 495 initTkdnd(root.tk, 'tkdnd/trunk/') |
473 | 496 |
474 tl = toplevelat("Keyboard Composer", existingtoplevel=root) | 497 tl = toplevelat("Keyboard Composer", existingtoplevel=root) |
475 | 498 |
476 startLevels = None | 499 startLevels = None |
477 if opts.nonpersistent: | 500 if opts.nonpersistent: |
478 startLevels = {} | 501 startLevels = {} |
479 kc = KeyboardComposer(tl, graph, s, startLevels, | 502 kc = KeyboardComposer(tl, graph, startLevels, |
480 hw_sliders=not opts.no_sliders) | 503 hw_sliders=not opts.no_sliders) |
481 kc.pack(fill=BOTH, expand=1) | 504 kc.pack(fill=BOTH, expand=1) |
482 | 505 |
483 for helpline in ["Bindings: B3 mute; C-l edit levels in subcomposer"]: | 506 for helpline in ["Bindings: B3 mute; C-l edit levels in subcomposer"]: |
484 tk.Label(root,text=helpline, font="Helvetica -12 italic", | 507 tk.Label(root,text=helpline, font="Helvetica -12 italic", |
487 import twisted.internet | 510 import twisted.internet |
488 try: | 511 try: |
489 reactor.listenTCP(networking.keyboardComposer.port, | 512 reactor.listenTCP(networking.keyboardComposer.port, |
490 server.Site(LevelServerHttp(kc.name_to_subtk))) | 513 server.Site(LevelServerHttp(kc.name_to_subtk))) |
491 except twisted.internet.error.CannotListenError, e: | 514 except twisted.internet.error.CannotListenError, e: |
492 print "Can't (and won't!) start level server:" | 515 log.warn("Can't (and won't!) start level server:") |
493 print e | 516 log.warn(e) |
494 | 517 |
495 root.protocol('WM_DELETE_WINDOW', reactor.stop) | 518 root.protocol('WM_DELETE_WINDOW', reactor.stop) |
496 if not opts.nonpersistent: | 519 if not opts.nonpersistent: |
497 reactor.addSystemEventTrigger('after', 'shutdown', kc.save) | 520 reactor.addSystemEventTrigger('after', 'shutdown', kc.save) |
498 | 521 |
499 tksupport.install(root,ms=10) | 522 tksupport.install(root,ms=10) |
500 | 523 |
501 | 524 |
502 # prof.watchPoint("/usr/lib/python2.4/site-packages/rdflib-2.3.3-py2.4-linux-i686.egg/rdflib/Graph.py", 615) | 525 # prof.watchPoint("/usr/lib/python2.4/site-packages/rdflib-2.3.3-py2.4-linux-i686.egg/rdflib/Graph.py", 615) |
503 | 526 |
504 prof.run(reactor.run, profile=False) | 527 prof.run(reactor.run, profile=False) |