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
|
import os
import logging
from PyQt6 import QtCore
from .exceptions import ComponentError
from ..toolkit import connectWidget
from ..toolkit.frame import BlankFrame
log = logging.getLogger("AVP.ComponentHandler")
class ComponentMetaclass(type(QtCore.QObject)):
"""
Checks the validity of each Component class and mutates some attrs.
E.g., takes only major version from version string & decorates methods
"""
def initializationWrapper(func):
def initializationWrapper(self, *args, **kwargs):
try:
return func(self, *args, **kwargs)
except Exception:
try:
raise ComponentError(self, "initialization process")
except ComponentError:
return
return initializationWrapper
def renderWrapper(func):
def renderWrapper(self, *args, **kwargs):
try:
log.verbose(
"### %s #%s renders a preview frame ###",
self.__class__.name,
str(self.compPos),
)
return func(self, *args, **kwargs)
except Exception as e:
try:
if e.__class__.__name__.startswith("Component"):
raise
else:
raise ComponentError(self, "renderer")
except ComponentError:
return BlankFrame()
return renderWrapper
def commandWrapper(func):
"""Intercepts the command() method to check for global args"""
def commandWrapper(self, arg):
if arg.startswith("preset="):
_, preset = arg.split("=", 1)
path = os.path.join(self.core.getPresetDir(self), preset)
if not os.path.exists(path):
print('Couldn\'t locate preset "%s"' % preset)
quit(1)
else:
print('Opening "%s" preset on layer %s' % (preset, self.compPos))
self.core.openPreset(path, self.compPos, preset)
# Don't call the component's command() method
return
else:
return func(self, arg)
return commandWrapper
def propertiesWrapper(func):
"""Intercepts the usual properties if the properties are locked."""
def propertiesWrapper(self):
if self._lockedProperties is not None:
return self._lockedProperties
else:
try:
return func(self)
except Exception:
try:
raise ComponentError(self, "properties")
except ComponentError:
return []
return propertiesWrapper
def errorWrapper(func):
"""Intercepts the usual error message if it is locked."""
def errorWrapper(self):
if self._lockedError is not None:
return self._lockedError
else:
return func(self)
return errorWrapper
def loadPresetWrapper(func):
"""Wraps loadPreset to handle the self.openingPreset boolean"""
class openingPreset:
def __init__(self, comp):
self.comp = comp
def __enter__(self):
self.comp.openingPreset = True
def __exit__(self, *args):
self.comp.openingPreset = False
def presetWrapper(self, *args):
with openingPreset(self):
try:
return func(self, *args)
except Exception:
try:
raise ComponentError(self, "preset loader")
except ComponentError:
return
return presetWrapper
def updateWrapper(func):
"""
Calls _preUpdate before every subclass update().
Afterwards, for non-user updates, calls _autoUpdate().
For undoable updates triggered by the user, calls _userUpdate()
"""
class wrap:
def __init__(self, comp, auto):
self.comp = comp
self.auto = auto
def __enter__(self):
self.comp._preUpdate()
def __exit__(self, *args):
if (
self.auto
or self.comp.openingPreset
or not hasattr(self.comp.parent, "undoStack")
):
log.verbose("Automatic update")
self.comp._autoUpdate()
else:
log.verbose("User update")
self.comp._userUpdate()
def updateWrapper(self, **kwargs):
auto = kwargs["auto"] if "auto" in kwargs else False
with wrap(self, auto):
try:
return func(self)
except Exception:
try:
raise ComponentError(self, "update method")
except ComponentError:
return
return updateWrapper
def widgetWrapper(func):
"""Connects all widgets to update method after the subclass's method"""
class wrap:
def __init__(self, comp):
self.comp = comp
def __enter__(self):
pass
def __exit__(self, *args):
for widgetList in self.comp._allWidgets.values():
for widget in widgetList:
log.verbose("Connecting %s", str(widget.__class__.__name__))
connectWidget(widget, self.comp.update)
def widgetWrapper(self, *args, **kwargs):
auto = kwargs["auto"] if "auto" in kwargs else False
with wrap(self):
try:
return func(self, *args, **kwargs)
except Exception:
try:
raise ComponentError(self, "widget creation")
except ComponentError:
return
return widgetWrapper
def __new__(cls, name, parents, attrs):
if "ui" not in attrs:
# Use module name as ui filename by default
attrs["ui"] = (
"%s.ui" % os.path.splitext(attrs["__module__"].split(".")[-1])[0]
)
decorate = (
"names", # Class methods
"error",
"audio",
"properties", # Properties
"preFrameRender",
"previewRender",
"loadPreset",
"command",
"update",
"widget",
)
# Auto-decorate methods
for key in decorate:
if key not in attrs:
continue
if key in ("names"):
attrs[key] = classmethod(attrs[key])
elif key in ("audio"):
attrs[key] = property(attrs[key])
elif key == "command":
attrs[key] = cls.commandWrapper(attrs[key])
elif key == "previewRender":
attrs[key] = cls.renderWrapper(attrs[key])
elif key == "preFrameRender":
attrs[key] = cls.initializationWrapper(attrs[key])
elif key == "properties":
attrs[key] = cls.propertiesWrapper(attrs[key])
elif key == "error":
attrs[key] = cls.errorWrapper(attrs[key])
elif key == "loadPreset":
attrs[key] = cls.loadPresetWrapper(attrs[key])
elif key == "update":
attrs[key] = cls.updateWrapper(attrs[key])
elif key == "widget" and parents[0] != QtCore.QObject:
attrs[key] = cls.widgetWrapper(attrs[key])
# Turn version string into a number
try:
if "version" not in attrs:
log.error(
"No version attribute in %s. Defaulting to 1",
attrs["name"],
)
attrs["version"] = 1
else:
attrs["version"] = int(attrs["version"].split(".")[0])
except ValueError:
log.critical(
"%s component has an invalid version string:\n%s",
attrs["name"],
str(attrs["version"]),
)
except KeyError:
log.critical("%s component has no version string.", attrs["name"])
else:
return super().__new__(cls, name, parents, attrs)
quit(1)
|