-
Notifications
You must be signed in to change notification settings - Fork 3
/
ValueComponent.py
270 lines (212 loc) · 9.86 KB
/
ValueComponent.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
#Embedded file name: /Users/versonator/Jenkins/live/Projects/AppLive/Resources/MIDI Remote Scripts/Push/ValueComponent.py
from functools import partial
from _Framework.CompoundComponent import CompoundComponent
from _Framework.ControlSurfaceComponent import ControlSurfaceComponent
from _Framework.DisplayDataSource import DisplayDataSource
from _Framework.SubjectSlot import subject_slot
from _Framework.Util import forward_property
from _Framework.InputControlElement import ParameterSlot
from _Framework import Task
from DisplayingDeviceComponent import convert_parameter_value_to_graphic
import consts
NUM_SEGMENTS = 4
def convert_value_to_graphic(value):
index = int(value * (len(consts.GRAPH_VOL) - 1))
if index != None and index < len(consts.GRAPH_VOL):
graphic_display_string = consts.GRAPH_VOL[index]
else:
graphic_display_string = ' '
return graphic_display_string
class ValueDisplayComponentBase(ControlSurfaceComponent):
def __init__(self, display_label = ' ', display_seg_start = 0, *a, **k):
super(ValueDisplayComponentBase, self).__init__(*a, **k)
self._label_data_source = DisplayDataSource(display_label)
self._value_data_source = DisplayDataSource()
self._graphic_data_source = DisplayDataSource()
self._display_label = display_label
self._display_seg_start = display_seg_start
def get_value_string(self):
raise NotImplementedError
def get_graphic_string(self):
raise NotImplementedError
def set_label_display(self, display):
self._set_display(display, self._label_data_source)
def set_value_display(self, display):
self._set_display(display, self._value_data_source)
def set_graphic_display(self, display):
self._set_display(display, self._graphic_data_source)
def set_clear_display1(self, display):
self._clear_display(display)
def set_clear_display2(self, display):
self._clear_display(display)
def set_clear_display3(self, display):
self._clear_display(display)
def set_clear_display4(self, display):
self._clear_display(display)
def _set_display(self, display, source):
if display:
display.set_data_sources((None,) * NUM_SEGMENTS)
display.segment(self._display_seg_start).set_data_source(source)
def _clear_display(self, display):
if display:
display.set_data_sources((None,))
display.reset()
def update(self):
if self.is_enabled():
self._value_data_source.set_display_string(self.get_value_string())
self._graphic_data_source.set_display_string(self.get_graphic_string())
class ValueComponentBase(CompoundComponent):
"""
Component to control one continuous property with a infinite
touch-sensitive encoder. You can optionally give it a display and
a button such that the value will be displayed while its pressed.
"""
TOUCH_BASED = 0
TIMER_BASED = 1
AUTO_HIDE_IN_SEC = 0.5
def create_display_component(self, *a, **k):
raise NotImplementedError
def __init__(self, display_label = ' ', display_seg_start = 0, encoder = None, *a, **k):
super(ValueComponentBase, self).__init__(*a, **k)
self._display_mode = self.TOUCH_BASED
self._button = None
self._on_encoder_changed.subject = encoder
self._display = self.register_component(self.create_display_component(display_label=display_label, display_seg_start=display_seg_start))
self._display.set_enabled(False)
self._hide_display_task = self._tasks.add(Task.sequence(Task.wait(self.AUTO_HIDE_IN_SEC), Task.run(partial(self._display.set_enabled, False))))
self._hide_display_task.kill()
display_layer = forward_property('_display')('layer')
def _get_display_mode(self):
return self._display_mode
def _set_display_mode(self, mode):
if self._display_mode != mode:
self._display_mode = mode
self._update_display_state()
display_mode = property(_get_display_mode, _set_display_mode)
def set_encoder(self, encoder):
raise NotImplementedError
def set_button(self, button):
self._button = button
self._on_button_value.subject = button
self._update_display_state()
@subject_slot('value')
def _on_button_value(self, value):
self._update_display_state()
@subject_slot('value')
def _on_encoder_changed(self, value):
if self.display_mode == self.TIMER_BASED:
self._display.set_enabled(True)
self._hide_display_task.restart()
def _update_display_state(self):
if self.display_mode == self.TOUCH_BASED:
self._display.set_enabled(self._button and self._button.is_pressed())
if self._button:
self._hide_display_task.kill()
elif self.display_mode == self.TIMER_BASED:
self._display.set_enabled(False)
def update(self):
button = self._on_button_value.subject
self._display.set_enabled(button and button.is_pressed())
class ValueDisplayComponent(ValueDisplayComponentBase):
"""
Display for values from standard Python properties.
"""
def __init__(self, property_name = None, subject = None, display_format = '%f', view_transform = None, graphic_transform = None, *a, **k):
super(ValueDisplayComponent, self).__init__(*a, **k)
self._subject = subject
self._property_name = property_name
self._display_format = display_format
if view_transform is not None:
self.view_transform = view_transform
if graphic_transform is not None:
self.graphic_transform = graphic_transform
self.register_slot(subject, self._on_value_changed, property_name)
self._on_value_changed()
def view_transform(self, x):
return x
def graphic_transform(self, x):
return self.view_transform(x)
def get_value_string(self):
value = getattr(self._subject, self._property_name)
return self._display_format % self.view_transform(value)
def get_graphic_string(self):
value = getattr(self._subject, self._property_name)
graph = self.graphic_transform(value)
return convert_value_to_graphic(graph)
def _on_value_changed(self):
self.update()
class ValueComponent(ValueComponentBase):
"""
Component to control one continuous property with a infinite
touch-sensitive encoder. You can optionally give it a display and
a button such that the value will be displayed while its pressed.
"""
encoder_factor = 1.0
def create_display_component(self, *a, **k):
return ValueDisplayComponent(property_name=self._property_name, subject=self._subject, display_format=self._display_format, view_transform=(lambda x: self.view_transform(x)), graphic_transform=(lambda x: self.graphic_transform(x)), *a, **k)
def __init__(self, property_name = None, subject = None, display_format = '%f', model_transform = None, view_transform = None, graphic_transform = None, encoder_factor = 1.0, *a, **k):
self._property_name = property_name
self._subject = subject
self._display_format = display_format
super(ValueComponent, self).__init__(*a, **k)
if model_transform is not None:
self.model_transform = model_transform
if view_transform is not None:
self.view_transform = view_transform
if graphic_transform is not None:
self.graphic_transform = graphic_transform
if encoder_factor is not None:
self.encoder_factor = encoder_factor
def model_transform(self, x):
"""
Tranform a value 'x' from the view domain to the domain as
stored in the subject.
"""
return x
def view_transform(self, x):
"""
Transform a value 'x' from the model domain to the view domain
as represented to the user.
"""
return x
def graphic_transform(self, x):
"""
Transform a value 'x' from the model domain to [0..1] range to
be used in the slider-representation of the value.
"""
return self.view_transform(x) / self.encoder_factor
def set_encoder(self, encoder):
self._on_encoder_value.subject = encoder
@subject_slot('normalized_value')
def _on_encoder_value(self, value):
value = self.view_transform(getattr(self._subject, self._property_name)) + value * self.encoder_factor
setattr(self._subject, self._property_name, self.model_transform(value))
class ParameterValueDisplayComponent(ValueDisplayComponentBase):
"""
Display for values from device parameters.
"""
def __init__(self, device_parameter = None, *a, **k):
super(ParameterValueDisplayComponent, self).__init__(*a, **k)
self._on_value_changed.subject = device_parameter
self._on_value_changed()
def get_value_string(self):
return str(self._on_value_changed.subject)
def get_graphic_string(self):
return convert_parameter_value_to_graphic(self._on_value_changed.subject)
@subject_slot('value')
def _on_value_changed(self):
self.update()
class ParameterValueComponent(ValueComponentBase):
"""
Component to control a device parameter with a infinite
touch-sensitive encoder. You can optionally give it a display and
a button such that the value will be displayed while its pressed.
"""
def create_display_component(self, *a, **k):
return ParameterValueDisplayComponent(device_parameter=self._parameter_slot.parameter, *a, **k)
def __init__(self, device_parameter = None, *a, **k):
self._parameter_slot = ParameterSlot(device_parameter)
super(ParameterValueComponent, self).__init__(*a, **k)
self.register_disconnectable(self._parameter_slot)
def set_encoder(self, encoder):
self._parameter_slot.control = encoder