Bind to python object possible?

Is it possible to bind to a python object , let’s say to a key in a dict ?

We did add a little-used feature to allow binding to dependencies. I hadn’t messed with it much, but I did some experimenting to answer your question and it’s kind of blowing my mind.

Attached is a basic setup to get you started. Like I said, I don’t think anyone has really explored this deeply, so please do let me know how it goes and let’s stay on this post if you find any bugs or gotchas.

Good luck with this, excited to hear results.dependencyBinds.tox (606 Bytes)

2 Likes

Just asking, is there some kind of callback-function in the dependable-object that could be overriden with another function to catch changes?

There currently is not. This is because dependencies are (often) changing outside of Python contexts. But because they are dependable you can set up a node to watch them and an exec to react to changes. That’s obviously a once-per-frame thing and not the most convenient.

We’ve talked about how to achieve this but haven’t figured out a reasonable way.

@Ivan this is amazing. thank you. I though I read about this at some point, but couldn’t find the info again. So glad I asked.

1 Like

@ivan is there a way to make a custom object dependable ?
I had a look on how the DependDict is build but it’s not really clear how it work.

def __getitem__(self, key):
    try:
        item = self.myItems[key]
        return item.val
    except:
        self.myMainDep.val  # dummy for dependency
        raise

for example what effect has calling self.myMainDep.val when we try to access a key that doesn’t exist in the dependDict ?

@val.setter
def val(self, value):
    self.clear()
    try:
        self.clear()
        self.update(value)
        self.myMainDep.modified()
    except:
        print("DependDict.val can only be set to a dict")

or why do we need to clear two times ?

Is there any way to inherit from tdu.Dependency ?

Cheers,
Colas

You can put anything inside a dependency wrapper to make it usable as a dependency. The trick is that if the object is mutable (like a list) you have to call modified manually when the insides change.

The reason you call another dependency is to create a chain. It’s a bit difficult to explain, but when a parameter evaluates an expression, any dependable objects it accesses are considered “interests”. If a dependency changes that a parameter is interested in, the parameter will update automatically. So in the case of dependDict, if a parameter looks at an item in a dictionary it needs to have an interest in the dictionary itself as well as the internal item.

Clearing two times in that last code look like me being a bad programmer. Will fix!

This thread motivated us to look into callbacks for dependencies again. No guarantees, but we might get it to work.

3 Likes

Hi Ivan,
Thank you for the explanation.
Colas

Hi @Ivan

I’m getting a lot of parameter evaluations overhead when using bindings to dicts. Please open the file and run “set_to_panel_B” and then “set_to_panel_A”

ISSUE 1
On each execution, the op(“useBinds”).par.Test is evaluated 7 times

ISSUE 2
the cook count for “useBinds” COMP increases by 2 instead of just 1

For the demo I created the GetDependency wrapper in the extension. But the same happens if the parameter references the dependency directly (and you add a print statement to getDependency() in TDStoreTools )

depBindsAK.toe (4.1 KB)

Thanks for tests, @Achim
Calling in @rob and @selina for cook/evaluation optimizing.

Been testing the workflow in a production file and the number of par evaluations print-outs is flooding my Textport

FYI: What I’m doing is basically a preset system using pars binded to dependencies. Is super nice as saving and loading a preset is just a simple Json dumps/loads statement

Did anyone get a chance looking into this ?

Sorry about slow response. I did some testing on this and the coming 30k experimental has only 2 evaluations (Issue 1) and somehow 0 cooks (Issue 2). Seeing about the possibility of getting those changes into 10k official.

That would be very much appreciated

Still researching this. It’s dangerous to mess with cooking stuff in official, so this may have to wait until next experimental

I understand. Are those significantly fewer par evaluations the result of a general par evaluation optimization or an optimization specific to pars binded to python objects? And can you elaborate why it creates zero cooks?

As those optimizations seem not to make it into official, would you say that (in current official) its OK to build a preset system using pars binded to dependencies objects? Because such a system easily has hundreds of dependency bindings in all the pars, and I’m worried that with the current “multiple parameter evaluations” will create huge performance issues.

This is a question for @rob

Follow up after talking to Rob…

We are currently experimenting with the optimizations. There is a small chance that they will be backported to the 10k branch, but it’s probably too dangerous of a change to add to official. They will definitely be in the next set of experimental builds, unless we discover some kind of terrible problem. At any rate, it’s a pretty sure thing that Dependency objects will be more efficient in coming builds. Also, your research has contributed to some great new ideas for powering up these Dependency objects, so thanks for pushing the envelope.

1 Like

edited: proper example

thanks for the info @Ivan looking forward to it.

In the meantime I have another question. In panels.toe /ctrlpanel (upper left pane) is selecting a panel from /panels. When no app is loaded (default), it selects the red panel.

Verify that the dependency is working by running loadGreenPanel and then loadRedPanel.
/ctrlpanel/getPanel updates automatically and selects the proper panel

Now I need to load an app and use that apps config to control which panel to select. So please run loadApp1 . This will set op("/ctrlpanel").Config to point to op("/app1").Config
But getPanel won’t update and select the blue panel.

You can get the Panel parameter to update by cooking getPanel, but the select COMP inside getPanel still doesn’t refresh. Any ideas how to handle such a situation?

PS: I do not want to do a self.Config.update(op("/app1").Config) because changing the current panel should update the Config of the loaded app. So if you (after running loadApp1) run loadRedPanel again, this will now update /app1.Config

Another excellent test case. Thanks for putting this stuff through the wringer!

panels.3.toe (5.1 KB)

exprSolution and exprSolution2
My expression solutions work because I am putting the dependDict itself inside a dependency (using createProperty). If you want to replace the entire dependDict, you need to wrap it in a dependency because that wrapper is what notifies the parameters that the entire object (the dependDict) has been replaced.

bindBug
BindBug still has a cook problem that I will get on the list for @rob. You can change out the config, but you have to open and close the par dialog or hover with the mouse over the Panel parameter to get it to update. Weird!

bindSolution
Wrapping the Config in another layer of dependDict seems to fix the problem in bindBug. I’m not sure why, and I don’t recommend this, just wanted to show you how you could get around the problem if Rob didn’t fix it.

gotcha!
One thing that confused me for a while working on this, in case you didn’t notice: If you run loadApp2 and then run loadRedPanel you end up changing App2’s color because you’re using its config now. Then when you try loadApp2 again it seems like it doesn’t work because App2 is now red instead of purple.