Skip to content

Commit 82f74bd

Browse files
added ParExecs to NoNode
Kinda redundant with CustomParHelper class, but actually more proper
1 parent 4a9ad97 commit 82f74bd

File tree

7 files changed

+341
-4
lines changed

7 files changed

+341
-4
lines changed

modules/release/QuickExt.tox

2.29 KB
Binary file not shown.
Binary file not shown.
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
CustomParHelper: CustomParHelper = next(d for d in me.docked if 'ExtUtils' in d.tags).mod('CustomParHelper').CustomParHelper # import
2+
NoNode: NoNode = next(d for d in me.docked if 'ExtUtils' in d.tags).mod('NoNode').NoNode # import
3+
####
4+
5+
class ExtTest:
6+
def __init__(self, ownerComp):
7+
self.ownerComp = ownerComp
8+
CustomParHelper.Init(self, ownerComp, enable_properties=True, enable_callbacks=True)
9+
NoNode.Init(ownerComp, enable_chopexec=True, enable_datexec=True, enable_keyboard_shortcuts=True, enable_parexec=True)
10+
NoNode.SetMarkColor((0.5, 0.05, 0.5))
11+
12+
# CHOP Exec tests
13+
NoNode.RegisterChopExec(NoNode.ChopExecType.ValueChange, self.ownerComp.op('null_test_chopexec'), 'v1', self.onTestChopValueChange)
14+
NoNode.RegisterChopExec(NoNode.ChopExecType.OffToOn, self.ownerComp.op('null_test_chopexec'), 'v*, v2', self.onTestChopOffToOn)
15+
NoNode.RegisterChopExec(NoNode.ChopExecType.OnToOff, self.ownerComp.op('null_test_chopexec'), ['v1', 'v2'], self.onTestChopOnToOff)
16+
#NoNode.RegisterChopExec(NoNode.ChopExecType.WhileOn, self.ownerComp.op('null_test_chopexec'), '*', self.onTestChopWhileOn)
17+
#NoNode.RegisterChopExec(NoNode.ChopExecType.WhileOff, self.ownerComp.op('null_test_chopexec'), '*', self.onTestChopWhileOff)
18+
19+
# DAT Exec tests
20+
NoNode.RegisterDatExec(NoNode.DatExecType.TableChange, self.ownerComp.op('null_test_datexec'), self.onTestDatExecTableChange)
21+
NoNode.RegisterDatExec(NoNode.DatExecType.RowChange, self.ownerComp.op('null_test_datexec'), self.onTestDatExecRowChange)
22+
NoNode.RegisterDatExec(NoNode.DatExecType.ColChange, self.ownerComp.op('null_test_datexec'), self.onTestDatExecColChange)
23+
NoNode.RegisterDatExec(NoNode.DatExecType.CellChange, self.ownerComp.op('null_test_datexec'), self.onTestDatExecCellChange)
24+
NoNode.RegisterDatExec(NoNode.DatExecType.SizeChange, self.ownerComp.op('null_test_datexec'), self.onTestDatExecSizeChange)
25+
NoNode.DisableDatExec()
26+
27+
# Keyboard shortcut test
28+
NoNode.RegisterKeyboardShortcut('ctrl.t', self.onTestKeyboardShortcut)
29+
30+
# Parexec tests
31+
NoNode.RegisterParExec(NoNode.ParExecType.ValueChange, 'Float', self.onTestParValueChange)
32+
NoNode.RegisterParExec(NoNode.ParExecType.OnPulse, 'Pulse', self.onTestParOnPulse)
33+
NoNode.RegisterParExec(NoNode.ParExecType.ValueChange, self.ownerComp.par.Testseq0float, self.onTestParSeqValueChange)
34+
35+
# CHOP Exec callbacks
36+
def onTestChopValueChange(self, _channel, _sampleIndex, _val, _prev):
37+
debug(f'onTestChopValueChange: {_channel.name} {_sampleIndex} {_val} {_prev}')
38+
39+
def onTestChopValueChange2(self, _channel, _sampleIndex, _val, _prev):
40+
debug(f'onTestChopValueChange2: {_channel.name} {_sampleIndex} {_val} {_prev}')
41+
42+
def onTestChopOffToOn(self, _channel, _sampleIndex, _val, _prev):
43+
debug(f'onTestChopOffToOn: {_channel.name} {_sampleIndex} {_val} {_prev}')
44+
45+
def onTestChopOnToOff(self, _channel, _sampleIndex, _val, _prev):
46+
debug(f'onTestChopOnToOff: {_channel.name} {_sampleIndex} {_val} {_prev}')
47+
48+
def onTestChopWhileOn(self, _channel, _sampleIndex, _val, _prev):
49+
debug(f'onTestChopWhileOn: {_channel.name} {_sampleIndex} {_val} {_prev}')
50+
51+
def onTestChopWhileOff(self, _channel, _sampleIndex, _val, _prev):
52+
debug(f'onTestChopWhileOff: {_channel.name} {_sampleIndex} {_val} {_prev}')
53+
54+
# DAT Exec callbacks
55+
def onTestDatExecTableChange(self, _dat):
56+
debug(f'onTestDatExecTableChange: {_dat}')
57+
58+
def onTestDatExecRowChange(self, _dat, _row):
59+
debug(f'onTestDatExecRowChange: {_dat} {_row}')
60+
61+
def onTestDatExecColChange(self, _dat, _col):
62+
debug(f'onTestDatExecColChange: {_dat} {_col}')
63+
64+
def onTestDatExecCellChange(self, _dat, _cells, _prev):
65+
debug(f'onTestDatExecCellChange: {_dat} {_cells} {_prev}')
66+
67+
def onTestDatExecSizeChange(self, _dat):
68+
debug(f'onTestDatExecSizeChange: {_dat}')
69+
70+
# Keyboard shortcut callback
71+
def onTestKeyboardShortcut(self):
72+
debug('onTestKeyboardShortcut: ctrl+t pressed')
73+
74+
# # Existing parameter callbacks
75+
# def onParFloat(self, _par, _val, _prev):
76+
# debug(f'onParFloat: {_par} {_val} {_prev}')
77+
78+
# def onParPulse(self, _par):
79+
# debug(f'onParPulse: {_par}')
80+
# print('printing parameters as self.par<Parname> properties:', self.parFloat, self.parPulse, self.parGroupInt)
81+
# print('printing parameters as self.eval<Parname> properties:', self.evalFloat, self.evalPulse, self.evalGroupInt)
82+
83+
# def onParGroupInt(self, _parGroup, _val):
84+
# debug(f'onParGroupInt: {_parGroup} {_val}')
85+
86+
# def onSeqTestseqN(self, idx):
87+
# debug(f'onSeqTestN: {idx}')
88+
89+
# def onSeqTestseqNfloat(self, _par, idx, _val, _prev):
90+
# debug(f'onSeqTestNFloat: {_par} {idx} {_val} {_prev}')
91+
92+
# def onSeqTestseqNstr(self, _par, idx, _val, _prev):
93+
# debug(f'onSeqTestNStr: {_par} {idx} {_val} {_prev}')
94+
95+
# Parexec callbacks
96+
def onTestParValueChange(self, _par, _val):
97+
debug(f'onTestParValueChange: {_par.name} {_val}')
98+
99+
def onTestParOnPulse(self):
100+
debug(f'onTestParOnPulse')
101+
102+
def onTestParSeqValueChange(self, _par, _val):
103+
debug(f'onTestParSeqValueChange: {_par.name} {_val}')
104+

scripts/QuickExt/templates/ExtUtils/NoNode/NoNode.py

Lines changed: 160 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,10 @@ class DatExecType(Enum):
6767
CellChange = auto()
6868
SizeChange = auto()
6969

70+
class ParExecType(Enum):
71+
ValueChange = auto()
72+
OnPulse = auto()
73+
7074
MARK_COLOR = (0.5, 0.05, 0.5)
7175

7276
CHOP_VALUECHANGE_EXEC: DAT = op('extChopValueChangeExec')
@@ -109,17 +113,40 @@ class DatExecType(Enum):
109113
DATEXEC_IS_ENABLED: bool = False
110114
KEYBOARD_IS_ENABLED: bool = False
111115

112-
ALL_EXECS: list[DAT] = CHOP_EXECS + DAT_EXECS + [KEYBOARD_EXEC]
116+
# Add these class variables after other similar declarations
117+
PAR_VALUECHANGE_EXEC: DAT = op('extParExecNoNodeValueChange')
118+
PAR_VALUESCHANGED_EXEC: DAT = op('extParExecNoNodeValuesChanged')
119+
PAR_ONPULSE_EXEC: DAT = op('extParExecNoNodeOnPulse')
120+
121+
PAR_EXECS: list[DAT] = [PAR_VALUECHANGE_EXEC, PAR_VALUESCHANGED_EXEC, PAR_ONPULSE_EXEC]
122+
123+
PAR_EXEC_MAP: Dict[ParExecType, COMP] = {
124+
ParExecType.ValueChange: PAR_VALUECHANGE_EXEC,
125+
ParExecType.OnPulse: PAR_ONPULSE_EXEC
126+
}
127+
128+
PAREXEC_CALLBACKS: TDStoreTools.DependDict[ParExecType, dict[Union[Par, str], Callable]] = TDStoreTools.DependDict()
129+
PAREXEC_IS_ENABLED: bool = False
130+
131+
# Update ALL_EXECS to include PAR_EXECS
132+
ALL_EXECS: list[DAT] = CHOP_EXECS + DAT_EXECS + PAR_EXECS + [KEYBOARD_EXEC]
133+
EXT_OWNER_COMP: COMP = None
113134

114135
@classmethod
115-
def Init(cls, enable_chopexec: bool = True, enable_datexec: bool = True, enable_keyboard_shortcuts: bool = True) -> None:
136+
def Init(cls, ownerComp, enable_chopexec: bool = True, enable_datexec: bool = True,
137+
enable_keyboard_shortcuts: bool = True, enable_parexec: bool = True) -> None:
116138
"""Initialize the NoNode functionality."""
139+
cls.EXT_OWNER_COMP = ownerComp
117140
cls.CHOPEXEC_IS_ENABLED = enable_chopexec
118141
cls.DATEXEC_IS_ENABLED = enable_datexec
119142
cls.KEYBOARD_IS_ENABLED = enable_keyboard_shortcuts
120143
cls.CHOPEXEC_CALLBACKS = TDStoreTools.DependDict()
121144
cls.DATEXEC_CALLBACKS = TDStoreTools.DependDict()
122145
cls.KEYBOARD_CALLBACKS = TDStoreTools.DependDict()
146+
cls.PAREXEC_IS_ENABLED = enable_parexec
147+
cls.PAREXEC_CALLBACKS = TDStoreTools.DependDict()
148+
149+
cls.__setOwnerCompToDocked(ownerComp)
123150

124151
# Disable all execute operators by default
125152
for exec in cls.ALL_EXECS:
@@ -141,6 +168,19 @@ def Init(cls, enable_chopexec: bool = True, enable_datexec: bool = True, enable_
141168
else:
142169
cls.DisableKeyboardShortcuts()
143170

171+
if enable_parexec:
172+
cls.EnableParExec()
173+
else:
174+
cls.DisableParExec()
175+
176+
@classmethod
177+
def __setOwnerCompToDocked(cls, ownerComp: COMP) -> None:
178+
for _op in me.docked:
179+
if hasattr(_op.par, 'ops'):
180+
_op.par.ops.val = ownerComp
181+
if hasattr(_op.par, 'op'):
182+
_op.par.op.val = ownerComp
183+
144184
### CHOP and DAT Exec ###
145185

146186
@classmethod
@@ -425,3 +465,121 @@ def SetMarkColor(cls, color: tuple[float, float, float]) -> None:
425465
for event_type in cls.CHOPEXEC_CALLBACKS.getRaw().keys() | cls.DATEXEC_CALLBACKS.getRaw().keys():
426466
for _op in cls.CHOPEXEC_CALLBACKS.getRaw().get(event_type, {}).keys() | cls.DATEXEC_CALLBACKS.getRaw().get(event_type, {}).keys():
427467
_op.color = cls.MARK_COLOR
468+
469+
### Parameter Exec ###
470+
471+
@classmethod
472+
def EnableParExec(cls) -> None:
473+
"""Enable parameter execute handling."""
474+
cls.PAREXEC_IS_ENABLED = True
475+
476+
@classmethod
477+
def DisableParExec(cls, event_type: ParExecType = None) -> None:
478+
"""Disable parameter execute handling for a specific event type or all event types."""
479+
if event_type is None:
480+
for par_exec in cls.PAR_EXECS:
481+
par_exec.par.active = False
482+
elif event_type in cls.PAR_EXEC_MAP:
483+
cls.PAR_EXEC_MAP[event_type].par.active = False
484+
485+
@classmethod
486+
def RegisterParExec(cls, event_type: ParExecType, parameter: Union[Par, str], callback: Callable) -> None:
487+
"""
488+
Register a parameter execute callback.
489+
490+
Args:
491+
event_type (ParExecType): The type of event to listen for.
492+
parameter (Union[Par, str]): The parameter to watch. Can be a Par object or a string in format 'op/parameter'.
493+
callback (Callable): The callback function to be called on parameter execution.
494+
495+
Example:
496+
def my_callback(par, prev):
497+
print(f"Parameter {par} changed from {prev} to {par.eval()}")
498+
499+
# Using Par object
500+
NoNode.RegisterParExec(ParExecType.ValueChange, op('base1').par.v, my_callback)
501+
# Using string reference
502+
NoNode.RegisterParExec(ParExecType.ValueChange, 'base1/v', my_callback)
503+
"""
504+
if event_type not in cls.PAREXEC_CALLBACKS.getRaw():
505+
cls.PAREXEC_CALLBACKS.setItem(event_type, {}, raw=True)
506+
507+
current_callbacks = cls.PAREXEC_CALLBACKS.getDependency(event_type)
508+
509+
# convert string parameter reference to Par object
510+
if isinstance(parameter, str):
511+
if not hasattr(cls.EXT_OWNER_COMP.par, parameter):
512+
return
513+
parameter = cls.EXT_OWNER_COMP.par[parameter]
514+
515+
# mark the operator
516+
#cls.__markOperatorAsWatched(parameter.owner)
517+
518+
current_callbacks.val[parameter] = callback
519+
cls.PAREXEC_CALLBACKS.setItem(event_type, current_callbacks)
520+
521+
if event_type in cls.PAR_EXEC_MAP:
522+
cls.PAR_EXEC_MAP[event_type].par.active = True
523+
524+
@classmethod
525+
def DeregisterParExec(cls, event_type: ParExecType, parameter: Union[Par, str] = None) -> None:
526+
"""
527+
Deregister a parameter execute callback.
528+
529+
Args:
530+
event_type (ParExecType): The event type to deregister.
531+
parameter (Union[Par, str], optional): The parameter to deregister. If None, deregisters all parameters for the event type.
532+
"""
533+
if event_type not in cls.PAREXEC_CALLBACKS.getRaw():
534+
return
535+
536+
current_callbacks = cls.PAREXEC_CALLBACKS.getDependency(event_type)
537+
538+
if parameter is None:
539+
# Deregister all callbacks for this event type
540+
cls.PAREXEC_CALLBACKS.setItem(event_type, {}, raw=True)
541+
cls.DisableParExec(event_type)
542+
return
543+
544+
# Convert string parameter reference to Par object if needed
545+
if isinstance(parameter, str):
546+
if not hasattr(cls.EXT_OWNER_COMP.par, parameter):
547+
return
548+
parameter = cls.EXT_OWNER_COMP.par[parameter]
549+
550+
if parameter in current_callbacks.val:
551+
del current_callbacks.val[parameter]
552+
cls.PAREXEC_CALLBACKS.setItem(event_type, current_callbacks)
553+
554+
# Disable exec if no more callbacks
555+
if not current_callbacks.val:
556+
cls.DisableParExec(event_type)
557+
558+
@classmethod
559+
def OnParExec(cls, event_type: ParExecType, parameter: Par, value = None, prev = None) -> None:
560+
"""Handle parameter execute events."""
561+
562+
if not cls.PAREXEC_IS_ENABLED:
563+
return
564+
565+
if event_type not in cls.PAREXEC_CALLBACKS.getRaw():
566+
return
567+
568+
# Create parameter string reference for matching
569+
par_str = parameter.name
570+
571+
# Check both Par object and string reference
572+
callback = cls.PAREXEC_CALLBACKS[event_type].get(parameter) or \
573+
cls.PAREXEC_CALLBACKS[event_type].get(par_str)
574+
575+
if callback:
576+
arg_count = callback.__code__.co_argcount
577+
if arg_count == 1:
578+
callback()
579+
elif arg_count == 2:
580+
callback(value if event_type == cls.ParExecType.ValueChange else parameter)
581+
elif arg_count == 3:
582+
callback(parameter, value)
583+
elif arg_count == 4:
584+
callback(parameter, value, prev)
585+
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
2+
def onPulse(par):
3+
## In Extension code implement as follows:
4+
# def On<Insert Paramname Here>(self, _par):
5+
# ...
6+
package = mod(me.dock.name).NoNode
7+
package.OnParExec(package.ParExecType.OnPulse, par)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
2+
def onValueChange(par, prev):
3+
## In Extension code implement as follows:
4+
# def On<Insert Paramname Here>(self, _par, _val, _prev):
5+
# ...
6+
package = mod(me.dock.name).NoNode
7+
package.OnParExec(package.ParExecType.ValueChange, par, par.eval(), prev)
8+

0 commit comments

Comments
 (0)