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
|
"""Scene function module.
Contains the definition of the Scene, the lighting primitive.
"""
import xml.etree.ElementTree as et
from typing import Mapping
from .function import Function
from ..topology import Fixture
from ..constants import INFTY, SCENE, BXW
from ..exceptions import LoadError
class Scene(Function):
"""Class representing a lighting scene.
This is the primitive for lighting, and all light cues must be based in some manner off
of Scene. Scenes are simple, having no fading and infinite duration.
Modifying the scene can be done in a few ways: the easiest is using the dictionary-
style accesses, e.g. setting the values using:
scene[channel] = value
Values may be removed from the scene using the ``del`` operator and retrieved in the
same fashion. Alternatively, values can be updated in bulk using ``Scene.update`` or
set in bulk with ``Scene.set``.
"""
audio_scope = ()
scope = frozenset()
actual_duration = INFTY
values = frozenset()
def __init__(self, w, id_ = None, name = None, values: Mapping[Fixture.Channel, int] = None):
super().__init__(w=w, type_=SCENE, id_=id_, name=name, duration=INFTY, fade_in=0,
fade_out=0)
self._values = {}
self._render = ()
self._update(values, changed=False)
def _update_render(self, changed=True):
self._render = tuple(self._values.items())
self.scope = frozenset(self._values.keys())
self.values = self._render
if changed:
self.w.function_changed(self)
def set(self, v):
"""Set the scene's values to the given ones.
Equivalent to update, except that all non-given values are deleted from the scene.
"""
self._values = {}
self.update(v)
def _update(self, v, changed=True):
if isinstance(v, dict):
v = v.items()
vn = []
for c, val in v:
val = int(val)
if val < 0 or val > 255:
raise ValueError("Values must be integers on [0,256)")
vn.append((c, val))
for c, val in vn:
self._values[c] = val
self._update_render(changed=changed)
def update(self, v):
"""Update the scene's values."""
self._update(v=v, changed=True)
def __getitem__(self, c: Fixture.Channel):
return self._values[c] if c in self._values else None
def __setitem__(self, c: Fixture.Channel, v: int):
self.update(((c, v),))
def __delitem__(self, c: Fixture.Channel):
if c in self._values:
del self._values[c]
self._update_render()
def get_data(self):
return None
def copy_data(self, data):
return None
def render(self, t, data = None):
return (self._render, (), None)
def serialize(self):
e = et.Element(BXW+"function")
e.set("type", self.type)
e.set("id", str(self.id))
e.set("name", self.name)
for c, v in self.values:
ce = et.SubElement(e, BXW+"value")
ce.set("fixture", str(c.f.id))
ce.set("channel", str(c.id))
ce.text = str(v)
return e
@classmethod
def deserialize(cls, w, e):
if e.tag != BXW+"function":
raise LoadError("Invalid function tag")
elif e.get("type") != SCENE:
raise LoadError("Load delegated to wrong class (this is a bug)")
id_ = cls.int_or_none(e.get("id"))
if id_ is None:
raise LoadError("Function tag has invalid/missing ID")
name = e.get("name")
values = {}
for ve in e:
if ve.tag != BXW+"value":
raise LoadError("Invalid value tag")
fixture = cls.int_or_none(ve.get("fixture"))
channel = cls.int_or_none(ve.get("channel"))
if None in (fixture, channel):
raise LoadError("Missing/invalid fixture/channel value")
elif fixture not in w.fixtures:
raise LoadError("Missing fixture ID %d" % fixture)
elif channel >= len(w.fixtures[fixture].channels):
raise LoadError("Fixture %d missing channel ID %d" % (fixture, channel))
channel = w.fixtures[fixture].channels[channel]
value = cls.int_or_none(ve.text)
if value is None:
raise LoadError("Missing/invalid value for channel")
values[channel] = value
return cls(w=w, id_=id_, name=name, values=values)
|