0

I have a Swing tree that is changing as a result of a background thread that receives updates to the tree, in a streaming fashion. Updates can come quite fast and they can be complex. And they can come fast enough that previous updates have not yet been rendered. I would like the tree displayed in the GUI to be visually updated, as fast as practical, as the tree model updates are applied.

Currently I have code abstractly as below:

public void ReceiveStreamDocument(final Document d) {
    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            ComplexChangeToTree(d);
            TreeReload();
        }
    });
}

Now the complex change is time consuming, what I would like is:

public void ReceiveStreamDocument(final Document d) {
    ComplexChangeToTree(d);
    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            TreeReload();
        }
    });
}

However the problem appears to be that modifications to the tree structure are now outside of the swing thread and when the tree is actually re-rendered there can be exceptions thrown. It does not seem to help to have something like this:

public void ReceiveStreamDocument(final Document d) {
    synchronized(root){
        ComplexChangeToTree(d);
    }
    SwingUtilities.invokeLater(new Runnable(){
        public void run(){
            synchronized(root){
               TreeReload();
            }
        }
    });
}

The TreeReload() is not actually updating the display, but rather just signaling that the display should be updated. I believe I need to override a painting operation somewhere and synchronize root on that, but I'm not sure where, or how, or indeed what is the best practice.

1 Answers1

1

The key to this is doing as much as possible in the background and firing as few tree model events as possible to the listening tree. Using SwingWorker, collect updates in your implementation of doInBackground(), where you can also do any parsing and rearranging that doesn't fire events from your TreeModel. Invoke publish() for results as they become available. A List of accumulated results will be coalesced and passed to your implementation of process() on the event dispatch thread at a sustainable rate, where you have another opportunity to optimize firing update events. A related question is examined here.

Community
  • 1
  • 1
trashgod
  • 203,806
  • 29
  • 246
  • 1,045
  • Thanks for that. I had a read about `SwingWorker`. As far as I understand the "heavy" work is meant to be done in the `doInBackground()` implementation. However the work involves modifying my tree (`DefaultMutableTreeNode`s), and my tree model (`DefaultTreeModel`) is dependent on my tree, and my `JTree` is dependent on my model. When modifying the tree (which doesn't fire any events in itself correct?), how does use of `SwingWorker` guarantee that there is no GUI updates that will occur concurrently? I'm assuming the `process()` implementation would simply call the tree reload. –  Jun 19 '16 at 07:36
  • Yes, `process()` is called on the event dispatch thread; `DefaultTreeModel` fires tree model events for you on `reload()`, etc.; you may have to [profile](http://stackoverflow.com/q/2064427/230513) to see where to optimize. – trashgod Jun 19 '16 at 14:38