-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathyv_presets.py
285 lines (211 loc) · 6.75 KB
/
yv_presets.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
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
# py/pyext - python script objects for PD and MaxMSP
#
# Copyright (c) 2002-2005 Thomas Grill ([email protected])
# For information on usage and redistribution, and for a DISCLAIMER OF ALL
# WARRANTIES, see the file, "license.txt," in this distribution.
# yv_presets.py
# [email protected] - 20100313
"""This is a preset management script that I wrote for my friend.
I thought it would be a nice exercise and a good way to learn how to use [py/pyext].
You can:
- manage presets for all UI objects that have proper send/receive symbols
There are several classes exposing py/pyext features:
- UiObject: A class for the UI objects (with their type and send/receive symbols)
- Patch: A class for patches as text. patches are parsed to find UI objects inside it
- Preset: the main class
"""
try:
import pyext
except:
print "ERROR: This script must be loaded by the PD/Max pyext external"
import os, re, pickle
class UiObject(object):
ui = "" # type of object (hsl, tgl, ...)
s_name = ""
r_name = ""
l_name = ""
def __init__(self, ui):
self.ui = ui
class Patch(object):
"""for some reasons, pd patches are weirdly formatted (with extra \n).
this class reformats the patch and checks each line to find whether
there's an UI object or not.
the UI objects *must* have send/receive names to respond to presets.
this might not be the best way to do it (parsing lines is not so elegant)
but it's the only one I thought about"""
path = ''
ui = ['hsl', 'vsl', 'tgl', 'nbx', 'floatatom', 'hradio', 'vradio']
txt = [] # patch as text
uis = [] # found ui objects
arg = None # $1 argument
found = False
def __init__(self, path):
self.uis = [] # weird bug otherwise
self.arg = None
f = open(str(path), 'r')
self.txt = f.readlines()
f.close()
self.reformat()
def reformat(self):
"""in case the patch as text has \n characters"""
self.txt = ' '.join(self.txt)
self.txt = self.txt.replace(" ;", ";")
self.txt = self.txt.replace("\n", "")
#self.txt = self.txt.replace("\n", "")
# remove boring ";" who (when alone on a line) mess things up
#self.txt = self.txt.replace(";", "\n")
self.txt = self.txt.split(";")
def find_uis(self):
"""parse each line of the patch and look for UI objects"""
for l in self.txt:
# search UI objects
for u in self.ui:
if re.search(u, l):
x = UiObject(u)
self.find_send_receive(l, x)
break
return self.found
def validate_send_receive(self, arr):
"""check that send and receive symbols are correct (not 'empty' neither '-')"""
wrong = ['-', 'empty']
for a in arr:
valid = True
if a in wrong:
valid = False
break
return valid
def find_send_receive(self, l, u):
"""look for 3 words in a row, which means send symbol,
receive symbol and label (floatatom has those in reverse order)
"""
label, send, receive = ['empty', 'empty', 'empty']
# special case for floatatom: line ends with 'label, receive, send'
if u.ui == 'floatatom':
#l = l.replace(';', '')
l = l.split(" ")
label, receive, send = l[-3:]
else:
# this could/should be better
s = r"(\S+[a-zA-Z_]+\S*)\s+(\S*[a-zA-Z_]+\S*)\s+(\S*[a-zA-Z_]+\S*)"
reg = re.findall(s, l)
if len(reg) > 0:
send, receive, label = reg[0]
if self.validate_send_receive([send, receive]):
u.s_name = send.replace("\\", "")
u.r_name = receive.replace("\\", "")
# replace $1 and #1 with provided argument
if self.arg:
u.s_name = u.s_name.replace("$1", str(self.arg))
u.r_name = u.r_name.replace("$1", str(self.arg))
u.s_name = u.s_name.replace("#1", str(self.arg))
u.r_name = u.r_name.replace("#1", str(self.arg))
self.uis.append(u)
self.found = True
class Preset(pyext._class):
"""main class"""
_inlets = 1
_outlets = 0
patches = []
found = [] # all UI objects in patch and abstractions
preset = {} # current state
presets = {} # all presets
current = "" # current object being queried
special = [] # special case for toggle (needs 2 bangs)
verbose = True
def __init__(self,*args):
"""Class constructor"""
self.clean()
print "\n##############\n# Presets Manager #\n##############\n"
def clean(self):
self.patches = []
self.preset = {}
self.presets = {}
self.found = []
self.special = []
def reset_1(self):
"""clean all"""
self.clean()
def path_1(self, *args):
"""path of a patch that will be using presets
can be on the form [path PATH/TO/ABSTRACTION ARGUMENT(
ARGUMENT will replace '$1' in receive symbols of UI objects"""
path = os.path.expanduser(str(args[0]))
patch = Patch(path)
if len(args) > 1: patch.arg = args[1]
if self.verbose: print "\nadded ", path
# store and bind the UI objects
if patch.find_uis():
self.patches.append(patch)
self.bind_uis(patch)
def bind_uis(self, patch):
"""bind objects send symbol to recv method"""
for u in patch.uis:
self.found.append(u)
self._bind(u.s_name, self.recv)
if self.verbose: print "bound", u.ui, "&s_name:", u.s_name, "&r_name:", u.r_name
def debug_1(self):
for f in self.found:
print f.ui
print f.s_name
print f.r_name
print "\n"
def store_1(self, a):
"""loop through all UI objects and send them a bang to get
their current value and store it"""
for f in self.found:
# unlock and prepare to store the first received value
self.current = f.r_name # receive symbol
self._send(f.r_name, "bang", ()) # send a bang
# tgl case
if f.ui == 'tgl':
self.special.append(f.r_name)
# lock recv function, store and clean
self.current = ""
if len(self.special) > 0:
# special case with [tgl]:
# sending [tgl] a bang changes its state so it has to recover
# its initial state. this resets all found [tgl]
for s in self.special:
self.preset[s] = 1 - self.preset[s]
self._send(s, "bang", ()) # send a second bang
self.special = []
self.presets[a] = self.preset
self.preset = {}
print "stored ", a
def recall_1(self, a):
"""recall method"""
new = None
try:
new = self.presets[a]
for k in new.keys():
self._send(k, new[k])
except:
print "no preset ", a
def recv(self, arg):
"""receive method"""
# store the value if unlocked
if self.current != "":
self.preset[self.current] = arg
# lock again
self.current = ""
def bind_1(self, s_name):
"""bind object to recv method"""
self._bind(s_name, self.recv)
def write_1(self, path):
"""write presets to file"""
path = os.path.expanduser(str(path))
f = open(path, 'w')
pickle.dump(self.presets, f)
f.close()
print "presets written to ", path
def read_1(self, path):
"""read presets from file"""
path = os.path.expanduser(str(path))
f = open(path, 'rb')
self.presets = pickle.load(f)
f.close()
def print_1(self):
print self.presets
def __del__(self):
"""Class destructor"""
pass