valueTree updates


#1

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


#2

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


#3

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


#4

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


#5

ValueTree::createCopy?

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


#6

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…


#7

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


#8

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;


#9

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! :) 

 


#10

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!


#11

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 :)


#12

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?