@@ -67,6 +67,10 @@ class DatExecType(Enum):
67
67
CellChange = auto ()
68
68
SizeChange = auto ()
69
69
70
+ class ParExecType (Enum ):
71
+ ValueChange = auto ()
72
+ OnPulse = auto ()
73
+
70
74
MARK_COLOR = (0.5 , 0.05 , 0.5 )
71
75
72
76
CHOP_VALUECHANGE_EXEC : DAT = op ('extChopValueChangeExec' )
@@ -109,17 +113,40 @@ class DatExecType(Enum):
109
113
DATEXEC_IS_ENABLED : bool = False
110
114
KEYBOARD_IS_ENABLED : bool = False
111
115
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
113
134
114
135
@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 :
116
138
"""Initialize the NoNode functionality."""
139
+ cls .EXT_OWNER_COMP = ownerComp
117
140
cls .CHOPEXEC_IS_ENABLED = enable_chopexec
118
141
cls .DATEXEC_IS_ENABLED = enable_datexec
119
142
cls .KEYBOARD_IS_ENABLED = enable_keyboard_shortcuts
120
143
cls .CHOPEXEC_CALLBACKS = TDStoreTools .DependDict ()
121
144
cls .DATEXEC_CALLBACKS = TDStoreTools .DependDict ()
122
145
cls .KEYBOARD_CALLBACKS = TDStoreTools .DependDict ()
146
+ cls .PAREXEC_IS_ENABLED = enable_parexec
147
+ cls .PAREXEC_CALLBACKS = TDStoreTools .DependDict ()
148
+
149
+ cls .__setOwnerCompToDocked (ownerComp )
123
150
124
151
# Disable all execute operators by default
125
152
for exec in cls .ALL_EXECS :
@@ -141,6 +168,19 @@ def Init(cls, enable_chopexec: bool = True, enable_datexec: bool = True, enable_
141
168
else :
142
169
cls .DisableKeyboardShortcuts ()
143
170
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
+
144
184
### CHOP and DAT Exec ###
145
185
146
186
@classmethod
@@ -425,3 +465,121 @@ def SetMarkColor(cls, color: tuple[float, float, float]) -> None:
425
465
for event_type in cls .CHOPEXEC_CALLBACKS .getRaw ().keys () | cls .DATEXEC_CALLBACKS .getRaw ().keys ():
426
466
for _op in cls .CHOPEXEC_CALLBACKS .getRaw ().get (event_type , {}).keys () | cls .DATEXEC_CALLBACKS .getRaw ().get (event_type , {}).keys ():
427
467
_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
+
0 commit comments