valueTree updates

Hello everyone,
I am looking for a smart way to update my GUI whenever I changed the data valueTree of my program.
What would be the best way to replace all the properties and child of my valueTree from another one?
Is the = operator replacing all the properties and child as well as the listeners pointer?

I don’t seen to have any callback when I load a new tree via the = operator.

Best
Silvere

Yes, if you use operator=, it just swaps the pointer, it won’t actually change the tree itself. You could use copyPropertiesFrom() ?

I could use the copyPropertiesFrom() indeed but it will only copy the properties not the childs’s properties right?
Do I need to iterate each child’s properties and apply the copyProperties for each recursively then?

thanks Jules

yes… probably would have to iterate… I could have sworn I’d written a method to do this, but apparently not!

ValueTree::createCopy?

Also didn’t you add a callback to find out when a tree is re-assigned a while ago? ValueTree::Listener::valueTreeRedirected

No, createCopy doesn’t help in this case - you want something that’ll iteratively go through and reuse any existing children, removing any unnecessary ones, adding new ones, etc., recursively. It’s quite a messy task, maybe I was too scared to attempt it…

Thanks for your help.
I managed to copy all the data from a tree to another with this function:

inline void GUI::loadTree(ValueTree& destinationTree,ValueTree& newValueTree)
{
destinationTree.copyPropertiesFrom(newValueTree, 0);
const int numChildren = newValueTree.getNumChildren();
for (int i = 0; i < numChildren; ++i)
{
ValueTree child = newValueTree.getChild(i);
ValueTree destinationChild = destinationTree.getChild(i);
destinationChild.copyPropertiesFrom(child, 0);
if(child.getNumChildren()!=0)
loadTree(destinationChild,child);
}
}

Best
Silvere

Maybe you have reasons, but the trick with ValueTree and Values is that is use always references. So why not just use ValueTree-References to the real data, and listen to it directly (maybe use extra-ValueTree as detectors for subtree changes)…


void GUI::updateDataReference(ValueTree sourceTree)
{
 myValueTreeReference = sourceTree;
 detectChangesInSubtreeBValueTree = sourceTree.getOrCreateChildWithName("B");
 detectChangesInSubtreeCValueTree = sourceTree.getOrCreateChildWithName("C");
}

ValueTree myValueTreeReference; 
ValueTree  detectChangesInSubtreeBValueTree;
ValueTree  detectChangesInSubtreeCValueTree;

I've been looking at this copying a replacement tree into place.

It's a horrible task to do a merge unless you assign some kind of ID to each node.  In the end I've replaced most of the ValueTree references in my code with another object.  I give my new object a fancy path that describes where it ought to attach itself relative to a root node. Then I can just swap a whole branch of the tree (removeChild(...) and addChild(...) at the top basically) and all my objects re-bind to the right data and everything gets an update call. 

However, I"ve got a little request:

I've got some situations where i have to be very careful about how I do tree updates. It'd be nice to be able to stop the update calls (pauseAndQueueListenerCallbacks()) while I tweak the tree into a good consistent state.  Then have them happen all at once (restoreListenerCallbacks())? 

I suspect there's some barrier of impracticality ... but it'd be handy! :) 

 

Hmm, it's a good request, but queuing those callbacks would be fiendishly difficult - there's no mechanism in there to make them asynchronous, and there are a whole load of different callback types, with data that may go out of scope if they get queued, etc.. Can't think of an easy way to do it!

It's the data that goes out of scope that's the tricky bit.  

Queuing would be pretty easy I think: though might go all a bit C++11 if it's not careful.  Something a bit like:

if (queuing)

  stack.push(std::bind(callback, object, arg, arg, arg))

else

  callback(object, arg, arg, arg);

But the data going out of scope could be a problem.   Though on resumeCallbacks() perhaps it doesn't really need to call anything that's out of scope...  

Anyway - I've got mine working for now - so I"ll survive :)

So.  I take it back.  I do need to solve the problem, though not at the ValueTree layer.  I've got a layer above that now which needs to sync entirely before it does some callbacks to the application.

This code works nicely for queuing the calls: 


/*
Build: clang++ -Wall -std=c++11 -g -o delayedlistener delayedlistener.cpp
Run: ./delayedlistener
*/
/* 
Output: 
function one
pausing
resuming
function one
function two
function one
*/
#include <iostream>
#include <deque>
#include <functional>
class ListenerQueue
{
public:
    ListenerQueue()
        :
        enabled (true) {}
    template <class ListenerFunctor>
    void call (const ListenerFunctor& f)
    {
        if (enabled)
            f();
        else
            queue (f);
    }
    void pauseAndQueueListeners()
    {
        enabled = false;
    }
    void resumeListeners()
    {
        enabled = true;
        runQueue();
    }
private:
    template <class ListenerFunctor>
    void queue (const ListenerFunctor& f)
    {
        backlog.push_back (new QueueItem<ListenerFunctor> (f));
    }
    void runQueue()
    {
        while (backlog.size() > 0)
        {
            backlog.front()->callNow();
            backlog.pop_front();
        }
    }
    struct QueueItemType
    {
        virtual ~QueueItemType() {};
        virtual void callNow() = 0;
    };
    template <class ListenerFunctor>
    struct QueueItem : public QueueItemType
    {
        QueueItem (const ListenerFunctor& f) : f (f) {}
        void callNow()
        {
            f();
        }
    private:
        ListenerFunctor f;
    };
    bool enabled;
    std::deque<QueueItemType*> backlog;
};

void functionOne() { std::cout << "function one" << std::endl; }
void functionTwo() { std::cout << "function two" << std::endl; }
int main()
{
    ListenerQueue q; 
    
    q.call(std::bind(functionOne)); 
    
    std::cout << "pausing" << std::endl;
    q.pauseAndQueueListeners();
    
    q.call(std::bind(functionOne)); 
    q.call(std::bind(functionTwo)); 
    
    std::cout << "resuming" << std::endl;
    
    q.resumeListeners();
    
    q.call(std::bind(functionOne)); 
    return 0;
};

But I'm not sure about the best way to deal with the pause/resume flag.  

It's essentially a lock, so I'm going to wrap it in some RAII lock-style object.  But then I need to apply the lock to all of my classes at once - suggesting a static shared flag.  

And then all the objects need to know when the flag has been cleared so they can clear their queue.  And the clearing needs to be a synchronous event - because otherwise things might get out of order. 

What's the best way to trigger a synchronous event on a variable change?  I suppose it might just be another ValueTree object, but a static one..? Any good ideas?