1 import pvl.web |
1 import pvl.web |
2 from pvl.web import urls, html |
2 from pvl.web import urls, html |
3 |
3 |
4 import logging; log = logging.getLogger('qmsk.dmx.web') |
4 import logging; log = logging.getLogger('qmsk.dmx.web') |
|
5 |
|
6 def colorize (x, red, green, blue, alpha=1.0) : |
|
7 return x(style='background-color: rgba({red}, {green}, {blue}, {alpha:0.2f})'.format(red=red, green=green, blue=blue, alpha=alpha)) |
|
8 |
|
9 def input (head, name, value) : |
|
10 return html.input( |
|
11 type = 'text', |
|
12 name = '-'.join([head, name]), |
|
13 |
|
14 id = '-'.join([head, name]), |
|
15 class_ = 'form-control dmx-input dmx-input-{name}'.format(name=name), |
|
16 |
|
17 placeholder = name, |
|
18 value = '{v:d}'.format(v=value) if value else None, |
|
19 ) |
|
20 |
|
21 def color_input (head, c, value) : |
|
22 color = dict(red=0, green=0, blue=0, alpha=0) |
|
23 |
|
24 color[c] = 255 |
|
25 if value : |
|
26 color[alpha] = value / 255.0 |
|
27 |
|
28 return colorize(input(head, c, value), **color) |
|
29 |
|
30 def slider (head, name) : |
|
31 return html.div(id='-'.join([head, name, 'slider']), class_='dmx-slider dmx-slider-{name}'.format(name=name)) |
|
32 |
|
33 def color_slider (head, c) : |
|
34 return slider(head, c) |
|
35 |
|
36 def head_color (head, value) : |
|
37 return html.div(class_='dmx-color-background')(colorize(html.div(id='-'.join([head, 'color']), class_='dmx-color')(' '), **value)) |
5 |
38 |
6 class Handler (pvl.web.Handler) : |
39 class Handler (pvl.web.Handler) : |
7 # Bootstrap |
40 # Bootstrap |
8 DOCTYPE = 'html' |
41 DOCTYPE = 'html' |
9 HTML_XMLNS = None |
42 HTML_XMLNS = None |
10 HTML_LANG = 'en' |
43 HTML_LANG = 'en' |
11 CSS = ( |
44 CSS = ( |
12 '//code.jquery.com/ui/1.10.4/themes/ui-darkness/jquery-ui.css', |
45 '//code.jquery.com/ui/1.10.4/themes/ui-darkness/jquery-ui.css', |
13 '//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css', |
46 '//netdna.bootstrapcdn.com/bootstrap/3.0.3/css/bootstrap.min.css', |
|
47 |
|
48 '/static/dmx.css', |
14 ) |
49 ) |
15 JS = ( |
50 JS = ( |
16 '//code.jquery.com/jquery-2.1.0.js', |
51 '//code.jquery.com/jquery-2.1.0.js', |
17 '//code.jquery.com/ui/1.10.4/jquery-ui.js', |
52 '//code.jquery.com/ui/1.10.4/jquery-ui.js', |
18 '//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js', |
53 '//netdna.bootstrapcdn.com/bootstrap/3.0.3/js/bootstrap.min.js', |
19 |
54 |
20 '/static/color-slider.js', |
55 '/static/color-slider.js', |
|
56 '/static/dmx.js', |
21 ) |
57 ) |
22 |
|
23 STYLE = """ |
|
24 body { |
|
25 padding-top: 2em; |
|
26 text-align: center; |
|
27 } |
|
28 |
|
29 .container { |
|
30 padding: 2em 1em; |
|
31 text-align: left; |
|
32 } |
|
33 |
|
34 .panel { |
|
35 width: 30em; |
|
36 margin: 1em auto; |
|
37 } |
|
38 |
|
39 input.color-control { |
|
40 width: 5em; |
|
41 } |
|
42 |
|
43 div#color { |
|
44 width: 5em; |
|
45 height: 5em; |
|
46 |
|
47 margin: 1em auto; |
|
48 } |
|
49 |
|
50 div.color-slider { |
|
51 margin: 1em; |
|
52 } |
|
53 |
|
54 div.color-slider#slider-r .ui-slider-range { |
|
55 background: #ff0000; |
|
56 } |
|
57 |
|
58 div.color-slider#slider-g .ui-slider-range { |
|
59 background: #00ff00; |
|
60 } |
|
61 |
|
62 div.color-slider#slider-b .ui-slider-range { |
|
63 background: #0000ff; |
|
64 } |
|
65 """ |
|
66 |
58 |
67 # test |
59 # test |
68 TITLE = u"DMX Control" |
60 TITLE = u"DMX Control" |
69 |
61 |
70 def process (self) : |
62 def process (self) : |
71 if self.request.method == 'POST' : |
63 if self.request.method == 'POST' : |
72 self.color = tuple((int(x, 16) if x else 0) for x in ( |
64 # XXX |
|
65 r, g, b = tuple((int(x, 16) if x else 0) for x in ( |
73 self.request.form.get('r'), |
66 self.request.form.get('r'), |
74 self.request.form.get('g'), |
67 self.request.form.get('g'), |
75 self.request.form.get('b'), |
68 self.request.form.get('b'), |
76 )) |
69 )) |
77 |
70 |
78 r, g, b = self.color |
|
79 |
|
80 self.app.dmx_color(r, g, b, 255) |
71 self.app.dmx_color(r, g, b, 255) |
81 |
72 |
|
73 def render_head (self, name, head) : |
|
74 if head.alpha() is None : |
|
75 head_input = head_slider = None |
82 else : |
76 else : |
83 self.color = None |
77 head_input = input(name, 'alpha', head.alpha()['alpha']) |
|
78 head_slider = slider(name, 'alpha') |
84 |
79 |
85 log.info("%s", self.color) |
80 rowspan = 1 |
86 |
81 |
|
82 if head.color() is None : |
|
83 colors = { } |
|
84 color = None |
|
85 else : |
|
86 colors = head.color() |
|
87 color = head_color(name, colors) |
|
88 rowspan += 3 |
|
89 |
|
90 yield html.tr( |
|
91 html.th(rowspan=rowspan)(name), |
|
92 html.td(head_input), |
|
93 html.td(head_slider), |
|
94 html.td(rowspan=rowspan)(color), |
|
95 ) |
|
96 |
|
97 for c in colors : |
|
98 yield html.tr( |
|
99 html.td( |
|
100 color_input(name, c, colors[c]), |
|
101 ), |
|
102 html.td( |
|
103 color_slider(name, c), |
|
104 ), |
|
105 ) |
|
106 |
87 def render (self) : |
107 def render (self) : |
88 if self.color : |
108 return html.div(class_='container')( |
89 r, g, b = self.color |
109 html.form(action='.', method='POST')( |
90 else : |
110 html.table(class_='dmx')( |
91 r = g = b = None |
111 html.thead( |
|
112 html.th(class_='dmx-head')(u"Head"), |
|
113 html.th(class_='dmx-value')(u"DMX"), |
|
114 html.th(class_='dmx-control')(u"Control"), |
|
115 html.th(class_='dmx-head-control')(u"Head Control"), |
|
116 ), |
|
117 html.tbody(self.render_head(name, head) for name, head in sorted(self.app.heads.iteritems())), |
|
118 ), |
92 |
119 |
93 def color_swatch () : |
120 html.button(type='submit', class_='btn btn-primary')("Go"), |
94 return html.div(id='color', |
|
95 style='background-color: rgb({r}, {g}, {b})'.format(r=r, g=g, b=b) |
|
96 )(' '), |
|
97 |
|
98 def color_input (name, value) : |
|
99 color = dict(r=0, g=0, b=0) |
|
100 bgcolor = dict(r=0, g=0, b=0) |
|
101 |
|
102 if value : |
|
103 color[name] = value |
|
104 alpha = value / 255.0 |
|
105 else : |
|
106 alpha = 0 |
|
107 |
|
108 bgcolor[name] = 255 |
|
109 |
|
110 return html.input(type='text', name=name, |
|
111 class_ = 'form-control color-control', |
|
112 placeholder = name, |
|
113 id = name, |
|
114 |
|
115 value = '{v:02x}'.format(v=value) if value else None, |
|
116 style = 'background-color: rgba({r}, {g}, {b}, {a:0.2f})'.format(a=alpha, **bgcolor), |
|
117 ) |
|
118 |
|
119 return html.div(class_='container')( |
|
120 html.div(class_='panel')( |
|
121 color_swatch(), |
|
122 html.div(id='slider-r', class_='color-slider')(' '), |
|
123 html.div(id='slider-g', class_='color-slider')(' '), |
|
124 html.div(id='slider-b', class_='color-slider')(' '), |
|
125 html.form(action='.', method='POST', class_='form-inline')( |
|
126 #html.label(for_='color', class_='control-label')("Color"), |
|
127 html.div(class_='form-group')( |
|
128 color_input('r', r), |
|
129 ), |
|
130 html.div(class_='form-group')( |
|
131 color_input('g', g), |
|
132 ), |
|
133 html.div(class_='form-group')( |
|
134 color_input('b', b), |
|
135 ), |
|
136 html.div(class_='form-group')( |
|
137 html.button(type='submit', class_='btn btn-primary')("Go"), |
|
138 ), |
|
139 ) |
|
140 ) |
121 ) |
141 ) |
122 ) |
142 |
123 |
143 class DMXWebApplication (pvl.web.Application) : |
124 class DMXWebApplication (pvl.web.Application) : |
144 URLS = urls.Map(( |
125 URLS = urls.Map(( |
145 urls.rule('/', Handler), |
126 urls.rule('/', Handler), |
146 )) |
127 )) |
147 |
128 |
148 def __init__ (self, dmx, **opts) : |
129 def __init__ (self, dmx, heads, **opts) : |
149 super(DMXWebApplication, self).__init__(**opts) |
130 super(DMXWebApplication, self).__init__(**opts) |
150 |
131 |
151 self.dmx = dmx |
132 self.dmx = dmx |
152 |
133 self.heads = heads |
153 def dmx_color (self, r, g, b, a=255) : |
|
154 # Stairville LED Par56 |
|
155 self.dmx[1] = (0, r, g, b, 0) |
|
156 |
|
157 # 4ch dimmer |
|
158 self.dmx[5] = (a, a, a, a) |
|
159 |
|
160 # American DJ - Mega Tri 60 - Mode 2 |
|
161 self.dmx[10] = (r, g, b, 0, a) |
|